Files
argo-cd/controller/cache/info_test.go
2026-02-12 09:29:40 -05:00

1608 lines
40 KiB
Go

package cache
import (
"sort"
"testing"
"github.com/argoproj/argo-cd/gitops-engine/pkg/utils/kube"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/yaml"
"github.com/argoproj/argo-cd/v3/common"
"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v3/util/argo/normalizers"
"github.com/argoproj/argo-cd/v3/util/errors"
)
func strToUnstructured(jsonStr string) *unstructured.Unstructured {
obj := make(map[string]any)
err := yaml.Unmarshal([]byte(jsonStr), &obj)
errors.CheckError(err)
return &unstructured.Unstructured{Object: obj}
}
var (
testService = strToUnstructured(`
apiVersion: v1
kind: Service
metadata:
name: helm-guestbook
namespace: default
resourceVersion: "123"
uid: "4"
spec:
selector:
app: guestbook
type: LoadBalancer
status:
loadBalancer:
ingress:
- hostname: localhost`)
testLinkAnnotatedService = strToUnstructured(`
apiVersion: v1
kind: Service
metadata:
name: helm-guestbook
namespace: default
resourceVersion: "123"
uid: "4"
annotations:
link.argocd.argoproj.io/external-link: http://my-grafana.example.com/pre-generated-link
spec:
selector:
app: guestbook
type: LoadBalancer
status:
loadBalancer:
ingress:
- hostname: localhost`)
testIngress = strToUnstructured(`
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: helm-guestbook
namespace: default
uid: "4"
spec:
backend:
serviceName: not-found-service
servicePort: 443
rules:
- host: helm-guestbook.example.com
http:
paths:
- backend:
serviceName: helm-guestbook
servicePort: 443
path: /
- backend:
serviceName: helm-guestbook
servicePort: https
path: /
tls:
- host: helm-guestbook.example.com
secretName: my-tls-secret
status:
loadBalancer:
ingress:
- ip: 107.178.210.11`)
testLinkAnnotatedIngress = strToUnstructured(`
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: helm-guestbook
namespace: default
uid: "4"
annotations:
link.argocd.argoproj.io/external-link: http://my-grafana.example.com/ingress-link
spec:
backend:
serviceName: not-found-service
servicePort: 443
rules:
- host: helm-guestbook.example.com
http:
paths:
- backend:
serviceName: helm-guestbook
servicePort: 443
path: /
- backend:
serviceName: helm-guestbook
servicePort: https
path: /
tls:
- host: helm-guestbook.example.com
secretName: my-tls-secret
status:
loadBalancer:
ingress:
- ip: 107.178.210.11`)
testIgnoreDefaultLinksIngress = strToUnstructured(`
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: helm-guestbook
namespace: default
uid: "4"
annotations:
link.argocd.argoproj.io/external-link: http://my-grafana.example.com/ingress-link
argocd.argoproj.io/ignore-default-links: "true"
spec:
backend:
serviceName: not-found-service
servicePort: 443
rules:
- host: helm-guestbook.example.com
http:
paths:
- backend:
serviceName: helm-guestbook
servicePort: 443
path: /
- backend:
serviceName: helm-guestbook
servicePort: https
path: /
tls:
- host: helm-guestbook.example.com
secretName: my-tls-secret
status:
loadBalancer:
ingress:
- ip: 107.178.210.11`)
testIngressWildCardPath = strToUnstructured(`
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: helm-guestbook
namespace: default
uid: "4"
spec:
backend:
serviceName: not-found-service
servicePort: 443
rules:
- host: helm-guestbook.example.com
http:
paths:
- backend:
serviceName: helm-guestbook
servicePort: 443
path: /*
- backend:
serviceName: helm-guestbook
servicePort: https
path: /*
tls:
- host: helm-guestbook.example.com
secretName: my-tls-secret
status:
loadBalancer:
ingress:
- ip: 107.178.210.11`)
testIngressWithoutTLS = strToUnstructured(`
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: helm-guestbook
namespace: default
uid: "4"
spec:
backend:
serviceName: not-found-service
servicePort: 443
rules:
- host: helm-guestbook.example.com
http:
paths:
- backend:
serviceName: helm-guestbook
servicePort: 443
path: /
- backend:
serviceName: helm-guestbook
servicePort: https
path: /
status:
loadBalancer:
ingress:
- ip: 107.178.210.11`)
testIngressNetworkingV1 = strToUnstructured(`
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: helm-guestbook
namespace: default
uid: "4"
spec:
backend:
service:
name: not-found-service
port:
number: 443
rules:
- host: helm-guestbook.example.com
http:
paths:
- backend:
service:
name: helm-guestbook
port:
number: 443
path: /
- backend:
service:
name: helm-guestbook
port:
name: https
path: /
tls:
- host: helm-guestbook.example.com
secretName: my-tls-secret
status:
loadBalancer:
ingress:
- ip: 107.178.210.11`)
testIstioVirtualService = strToUnstructured(`
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: hello-world
namespace: demo
spec:
http:
- match:
- uri:
prefix: "/1"
route:
- destination:
host: service_full.demo.svc.cluster.local
- destination:
host: service_namespace.namespace
- match:
- uri:
prefix: "/2"
route:
- destination:
host: service
`)
testIstioServiceEntry = strToUnstructured(`
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
name: echo
spec:
exportTo:
- '*'
hosts:
- echo.internal
location: MESH_INTERNAL
ports:
- name: http
number: 80
protocol: HTTP
targetPort: 5678
resolution: DNS
workloadSelector:
labels:
app.kubernetes.io/name: echo-2
`)
)
// These tests are equivalent to tests in ui/src/app/applications/components/utils.test.tsx. If you update tests here,
// please make sure to update the equivalent tests in the UI.
func TestGetPodInfo(t *testing.T) {
t.Parallel()
t.Run("TestGetPodInfo", func(t *testing.T) {
t.Parallel()
pod := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
name: helm-guestbook-pod
namespace: default
ownerReferences:
- apiVersion: extensions/v1beta1
kind: ReplicaSet
name: helm-guestbook-rs
resourceVersion: "123"
labels:
app: guestbook
spec:
nodeName: minikube
containers:
- image: bar
resources:
requests:
memory: 128Mi
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "0/1"},
{Name: common.PodRequestsCPU, Value: "0"}, // strings imported from common
{Name: common.PodRequestsMEM, Value: "134217728000"},
}, info.Info)
assert.Equal(t, []string{"bar"}, info.Images)
assert.Equal(t, &PodInfo{
NodeName: "minikube",
ResourceRequests: corev1.ResourceList{corev1.ResourceMemory: resource.MustParse("128Mi")},
}, info.PodInfo)
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{Labels: map[string]string{"app": "guestbook"}}, info.NetworkingInfo)
})
t.Run("TestGetPodWithInitialContainerInfo", func(t *testing.T) {
pod := strToUnstructured(`
apiVersion: "v1"
kind: "Pod"
metadata:
labels:
app: "app-with-initial-container"
name: "app-with-initial-container-5f46976fdb-vd6rv"
namespace: "default"
ownerReferences:
- apiVersion: "apps/v1"
kind: "ReplicaSet"
name: "app-with-initial-container-5f46976fdb"
spec:
containers:
- image: "alpine:latest"
imagePullPolicy: "Always"
name: "app-with-initial-container"
initContainers:
- image: "alpine:latest"
imagePullPolicy: "Always"
name: "app-with-initial-container-logshipper"
nodeName: "minikube"
status:
containerStatuses:
- image: "alpine:latest"
name: "app-with-initial-container"
ready: true
restartCount: 0
started: true
state:
running:
startedAt: "2024-10-08T08:44:25Z"
initContainerStatuses:
- image: "alpine:latest"
name: "app-with-initial-container-logshipper"
ready: true
restartCount: 0
started: false
state:
terminated:
exitCode: 0
reason: "Completed"
phase: "Running"
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "Running"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "1/1"},
{Name: common.PodRequestsCPU, Value: "0"},
{Name: common.PodRequestsMEM, Value: "0"},
}, info.Info)
})
t.Run("TestGetPodWithInitialContainerInfoWithResources", func(t *testing.T) {
pod := strToUnstructured(`
apiVersion: "v1"
kind: "Pod"
metadata:
labels:
app: "app-with-initial-container"
name: "app-with-initial-container-5f46976fdb-vd6rv"
namespace: "default"
ownerReferences:
- apiVersion: "apps/v1"
kind: "ReplicaSet"
name: "app-with-initial-container-5f46976fdb"
spec:
containers:
- image: "alpine:latest"
imagePullPolicy: "Always"
name: "app-with-initial-container"
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
initContainers:
- image: "alpine:latest"
imagePullPolicy: "Always"
name: "app-with-initial-container-logshipper"
resources:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "250m"
memory: "256Mi"
nodeName: "minikube"
status:
containerStatuses:
- image: "alpine:latest"
name: "app-with-initial-container"
ready: true
restartCount: 0
started: true
state:
running:
startedAt: "2024-10-08T08:44:25Z"
initContainerStatuses:
- image: "alpine:latest"
name: "app-with-initial-container-logshipper"
ready: true
restartCount: 0
started: false
state:
terminated:
exitCode: 0
reason: "Completed"
phase: "Running"
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "Running"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "1/1"},
{Name: common.PodRequestsCPU, Value: "100"},
{Name: common.PodRequestsMEM, Value: "134217728000"},
}, info.Info)
})
t.Run("TestGetPodInfoWithSidecar", func(t *testing.T) {
t.Parallel()
pod := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
labels:
app: app-with-sidecar
name: app-with-sidecar-6664cc788c-lqlrp
namespace: default
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: app-with-sidecar-6664cc788c
spec:
containers:
- image: 'docker.m.daocloud.io/library/alpine:latest'
imagePullPolicy: Always
name: app-with-sidecar
initContainers:
- image: 'docker.m.daocloud.io/library/alpine:latest'
imagePullPolicy: Always
name: logshipper
restartPolicy: Always
nodeName: minikube
status:
containerStatuses:
- image: 'docker.m.daocloud.io/library/alpine:latest'
name: app-with-sidecar
ready: true
restartCount: 0
started: true
state:
running:
startedAt: '2024-10-08T08:39:43Z'
initContainerStatuses:
- image: 'docker.m.daocloud.io/library/alpine:latest'
name: logshipper
ready: true
restartCount: 0
started: true
state:
running:
startedAt: '2024-10-08T08:39:40Z'
phase: Running
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "Running"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "2/2"},
{Name: common.PodRequestsCPU, Value: "0"},
{Name: common.PodRequestsMEM, Value: "0"},
}, info.Info)
})
t.Run("TestGetPodInfoWithInitialContainer", func(t *testing.T) {
t.Parallel()
pod := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
generateName: myapp-long-exist-56b7d8794d-
labels:
app: myapp-long-exist
name: myapp-long-exist-56b7d8794d-pbgrd
namespace: linghao
ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: myapp-long-exist-56b7d8794d
spec:
containers:
- image: alpine:latest
imagePullPolicy: Always
name: myapp-long-exist
initContainers:
- image: alpine:latest
imagePullPolicy: Always
name: myapp-long-exist-logshipper
nodeName: minikube
status:
containerStatuses:
- image: alpine:latest
name: myapp-long-exist
ready: false
restartCount: 0
started: false
state:
waiting:
reason: PodInitializing
initContainerStatuses:
- image: alpine:latest
name: myapp-long-exist-logshipper
ready: false
restartCount: 0
started: true
state:
running:
startedAt: '2024-10-09T08:03:45Z'
phase: Pending
startTime: '2024-10-09T08:02:39Z'
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "Init:0/1"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "0/1"},
{Name: common.PodRequestsCPU, Value: "0"},
{Name: common.PodRequestsMEM, Value: "0"},
}, info.Info)
})
// Test pod has 2 restartable init containers, the first one running but not started.
t.Run("TestGetPodInfoWithRestartableInitContainer", func(t *testing.T) {
t.Parallel()
pod := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
name: test1
spec:
initContainers:
- name: restartable-init-1
restartPolicy: Always
- name: restartable-init-2
restartPolicy: Always
containers:
- name: container
nodeName: minikube
status:
phase: Pending
initContainerStatuses:
- name: restartable-init-1
ready: false
restartCount: 3
state:
running: {}
started: false
lastTerminationState:
terminated:
finishedAt: "2023-10-01T00:00:00Z" # Replace with actual time
- name: restartable-init-2
ready: false
state:
waiting: {}
started: false
containerStatuses:
- ready: false
restartCount: 0
state:
waiting: {}
conditions:
- type: ContainersReady
status: "False"
- type: Initialized
status: "False"
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "Init:0/2"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "0/3"},
{Name: "Restart Count", Value: "3"},
{Name: common.PodRequestsCPU, Value: "0"},
{Name: common.PodRequestsMEM, Value: "0"},
}, info.Info)
})
// Test pod has 2 restartable init containers, the first one started and the second one running but not started.
t.Run("TestGetPodInfoWithPartiallyStartedInitContainers", func(t *testing.T) {
t.Parallel()
pod := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
name: test1
spec:
initContainers:
- name: restartable-init-1
restartPolicy: Always
- name: restartable-init-2
restartPolicy: Always
containers:
- name: container
nodeName: minikube
status:
phase: Pending
initContainerStatuses:
- name: restartable-init-1
ready: false
restartCount: 3
state:
running: {}
started: true
lastTerminationState:
terminated:
finishedAt: "2023-10-01T00:00:00Z" # Replace with actual time
- name: restartable-init-2
ready: false
state:
running: {}
started: false
containerStatuses:
- ready: false
restartCount: 0
state:
waiting: {}
conditions:
- type: ContainersReady
status: "False"
- type: Initialized
status: "False"
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "Init:1/2"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "0/3"},
{Name: "Restart Count", Value: "3"},
{Name: common.PodRequestsCPU, Value: "0"},
{Name: common.PodRequestsMEM, Value: "0"},
}, info.Info)
})
// Test pod has 2 restartable init containers started and 1 container running
t.Run("TestGetPodInfoWithStartedInitContainers", func(t *testing.T) {
t.Parallel()
pod := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
name: test2
spec:
initContainers:
- name: restartable-init-1
restartPolicy: Always
- name: restartable-init-2
restartPolicy: Always
containers:
- name: container
nodeName: minikube
status:
phase: Running
initContainerStatuses:
- name: restartable-init-1
ready: false
restartCount: 3
state:
running: {}
started: true
lastTerminationState:
terminated:
finishedAt: "2023-10-01T00:00:00Z" # Replace with actual time
- name: restartable-init-2
ready: false
state:
running: {}
started: true
containerStatuses:
- ready: true
restartCount: 4
state:
running: {}
lastTerminationState:
terminated:
finishedAt: "2023-10-01T00:00:00Z" # Replace with actual time
conditions:
- type: ContainersReady
status: "False"
- type: Initialized
status: "True"
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "Running"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "1/3"},
{Name: "Restart Count", Value: "7"},
{Name: common.PodRequestsCPU, Value: "0"},
{Name: common.PodRequestsMEM, Value: "0"},
}, info.Info)
})
// Test pod has 1 init container restarting and 1 container not running
t.Run("TestGetPodInfoWithNormalInitContainer", func(t *testing.T) {
t.Parallel()
pod := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
name: test7
spec:
initContainers:
- name: init-container
containers:
- name: main-container
nodeName: minikube
status:
phase: podPhase
initContainerStatuses:
- ready: false
restartCount: 3
state:
running: {}
lastTerminationState:
terminated:
finishedAt: "2023-10-01T00:00:00Z" # Replace with the actual time
containerStatuses:
- ready: false
restartCount: 0
state:
waiting: {}
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "Init:0/1"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "0/1"},
{Name: "Restart Count", Value: "3"},
{Name: common.PodRequestsCPU, Value: "0"},
{Name: common.PodRequestsMEM, Value: "0"},
}, info.Info)
})
// Test pod condition succeed
t.Run("TestPodConditionSucceeded", func(t *testing.T) {
t.Parallel()
pod := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
name: test8
spec:
nodeName: minikube
containers:
- name: container
status:
phase: Succeeded
containerStatuses:
- ready: false
restartCount: 0
state:
terminated:
reason: Completed
exitCode: 0
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "Completed"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "0/1"},
}, info.Info)
})
// Test pod condition succeed which had some allocated resources
t.Run("TestPodConditionSucceededWithResources", func(t *testing.T) {
t.Parallel()
pod := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
name: test8
spec:
nodeName: minikube
containers:
- name: container
resources:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "250m"
memory: "256Mi"
status:
phase: Succeeded
containerStatuses:
- ready: false
restartCount: 0
state:
terminated:
reason: Completed
exitCode: 0
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "Completed"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "0/1"},
}, info.Info)
})
// Test pod condition failed
t.Run("TestPodConditionFailed", func(t *testing.T) {
t.Parallel()
pod := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
name: test9
spec:
nodeName: minikube
containers:
- name: container
status:
phase: Failed
containerStatuses:
- ready: false
restartCount: 0
state:
terminated:
reason: Error
exitCode: 1
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "Error"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "0/1"},
}, info.Info)
})
// Test pod condition failed with allocated resources
t.Run("TestPodConditionFailedWithResources", func(t *testing.T) {
t.Parallel()
pod := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
name: test9
spec:
nodeName: minikube
containers:
- name: container
resources:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "250m"
memory: "256Mi"
status:
phase: Failed
containerStatuses:
- ready: false
restartCount: 0
state:
terminated:
reason: Error
exitCode: 1
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "Error"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "0/1"},
}, info.Info)
})
// Test pod condition succeed with deletion
t.Run("TestPodConditionSucceededWithDeletion", func(t *testing.T) {
t.Parallel()
pod := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
name: test10
deletionTimestamp: "2023-10-01T00:00:00Z"
spec:
nodeName: minikube
containers:
- name: container
status:
phase: Succeeded
containerStatuses:
- ready: false
restartCount: 0
state:
terminated:
reason: Completed
exitCode: 0
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "Completed"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "0/1"},
}, info.Info)
})
// Test pod condition running with deletion
t.Run("TestPodConditionRunningWithDeletion", func(t *testing.T) {
t.Parallel()
pod := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
name: test11
deletionTimestamp: "2023-10-01T00:00:00Z"
spec:
nodeName: minikube
containers:
- name: container
status:
phase: Running
containerStatuses:
- ready: false
restartCount: 0
state:
running: {}
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "Terminating"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "0/1"},
{Name: common.PodRequestsCPU, Value: "0"},
{Name: common.PodRequestsMEM, Value: "0"},
}, info.Info)
})
// Test pod condition pending with deletion
t.Run("TestPodConditionPendingWithDeletion", func(t *testing.T) {
t.Parallel()
pod := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
name: test12
deletionTimestamp: "2023-10-01T00:00:00Z"
spec:
nodeName: minikube
containers:
- name: container
status:
phase: Pending
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "Terminating"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "0/1"},
{Name: common.PodRequestsCPU, Value: "0"},
{Name: common.PodRequestsMEM, Value: "0"},
}, info.Info)
})
// Test PodScheduled condition with reason SchedulingGated
t.Run("TestPodScheduledWithSchedulingGated", func(t *testing.T) {
t.Parallel()
pod := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
name: test13
spec:
nodeName: minikube
containers:
- name: container1
- name: container2
status:
phase: podPhase
conditions:
- type: PodScheduled
status: "False"
reason: SchedulingGated
`)
info := &ResourceInfo{}
populateNodeInfo(pod, info, []string{})
assert.Equal(t, []v1alpha1.InfoItem{
{Name: "Status Reason", Value: "SchedulingGated"},
{Name: "Node", Value: "minikube"},
{Name: "Containers", Value: "0/2"},
{Name: common.PodRequestsCPU, Value: "0"},
{Name: common.PodRequestsMEM, Value: "0"},
}, info.Info)
})
}
func TestGetNodeInfo(t *testing.T) {
node := strToUnstructured(`
apiVersion: v1
kind: Node
metadata:
name: minikube
labels:
foo: bar
spec: {}
status:
capacity:
cpu: "6"
memory: 6091320Ki
nodeInfo:
architecture: amd64
operatingSystem: linux
osImage: Ubuntu 20.04 LTS
`)
info := &ResourceInfo{}
populateNodeInfo(node, info, []string{})
assert.Equal(t, &NodeInfo{
Name: "minikube",
Capacity: corev1.ResourceList{corev1.ResourceMemory: resource.MustParse("6091320Ki"), corev1.ResourceCPU: resource.MustParse("6")},
SystemInfo: corev1.NodeSystemInfo{Architecture: "amd64", OperatingSystem: "linux", OSImage: "Ubuntu 20.04 LTS"},
Labels: map[string]string{"foo": "bar"},
}, info.NodeInfo)
}
func TestGetServiceInfo(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testService, info, []string{})
assert.Empty(t, info.Info)
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
TargetLabels: map[string]string{"app": "guestbook"},
Ingress: []corev1.LoadBalancerIngress{{Hostname: "localhost"}},
}, info.NetworkingInfo)
}
func TestGetLinkAnnotatedServiceInfo(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testLinkAnnotatedService, info, []string{})
assert.Empty(t, info.Info)
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
TargetLabels: map[string]string{"app": "guestbook"},
Ingress: []corev1.LoadBalancerIngress{{Hostname: "localhost"}},
ExternalURLs: []string{"http://my-grafana.example.com/pre-generated-link"},
}, info.NetworkingInfo)
}
func TestGetIstioVirtualServiceInfo(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testIstioVirtualService, info, []string{})
assert.Empty(t, info.Info)
require.NotNil(t, info.NetworkingInfo)
require.NotNil(t, info.NetworkingInfo.TargetRefs)
assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
Kind: kube.ServiceKind,
Name: "service_full",
Namespace: "demo",
})
assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
Kind: kube.ServiceKind,
Name: "service_namespace",
Namespace: "namespace",
})
assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
Kind: kube.ServiceKind,
Name: "service",
Namespace: "demo",
})
}
func TestGetIstioServiceEntryInfo(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testIstioServiceEntry, info, []string{})
assert.Empty(t, info.Info)
require.NotNil(t, info.NetworkingInfo)
require.NotNil(t, info.NetworkingInfo.TargetRefs)
assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
Kind: kube.PodKind,
})
assert.Equal(t, map[string]string{
"app.kubernetes.io/name": "echo-2",
}, info.NetworkingInfo.TargetLabels)
}
func TestGetIngressInfo(t *testing.T) {
tests := []struct {
Ingress *unstructured.Unstructured
}{
{testIngress},
{testIngressNetworkingV1},
}
for _, tc := range tests {
info := &ResourceInfo{}
populateNodeInfo(tc.Ingress, info, []string{})
assert.Empty(t, info.Info)
sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
return info.NetworkingInfo.TargetRefs[i].Name < info.NetworkingInfo.TargetRefs[j].Name
})
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
Ingress: []corev1.LoadBalancerIngress{{IP: "107.178.210.11"}},
TargetRefs: []v1alpha1.ResourceRef{{
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "helm-guestbook",
}, {
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "not-found-service",
}},
ExternalURLs: []string{"https://helm-guestbook.example.com/"},
}, info.NetworkingInfo)
}
}
func TestGetLinkAnnotatedIngressInfo(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testLinkAnnotatedIngress, info, []string{})
assert.Empty(t, info.Info)
sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
return info.NetworkingInfo.TargetRefs[i].Name < info.NetworkingInfo.TargetRefs[j].Name
})
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
Ingress: []corev1.LoadBalancerIngress{{IP: "107.178.210.11"}},
TargetRefs: []v1alpha1.ResourceRef{{
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "helm-guestbook",
}, {
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "not-found-service",
}},
ExternalURLs: []string{"http://my-grafana.example.com/ingress-link", "https://helm-guestbook.example.com/"},
}, info.NetworkingInfo)
}
func TestGetIgnoreDefaultLinksIngressInfo(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testIgnoreDefaultLinksIngress, info, []string{})
assert.Empty(t, info.Info)
sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
return info.NetworkingInfo.TargetRefs[i].Name < info.NetworkingInfo.TargetRefs[j].Name
})
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
Ingress: []corev1.LoadBalancerIngress{{IP: "107.178.210.11"}},
TargetRefs: []v1alpha1.ResourceRef{{
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "helm-guestbook",
}, {
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "not-found-service",
}},
ExternalURLs: []string{"http://my-grafana.example.com/ingress-link"},
}, info.NetworkingInfo)
}
func TestGetIngressInfoWildCardPath(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testIngressWildCardPath, info, []string{})
assert.Empty(t, info.Info)
sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
return info.NetworkingInfo.TargetRefs[i].Name < info.NetworkingInfo.TargetRefs[j].Name
})
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
Ingress: []corev1.LoadBalancerIngress{{IP: "107.178.210.11"}},
TargetRefs: []v1alpha1.ResourceRef{{
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "helm-guestbook",
}, {
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "not-found-service",
}},
ExternalURLs: []string{"https://helm-guestbook.example.com/"},
}, info.NetworkingInfo)
}
func TestGetIngressInfoWithoutTls(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testIngressWithoutTLS, info, []string{})
assert.Empty(t, info.Info)
sort.Slice(info.NetworkingInfo.TargetRefs, func(i, j int) bool {
return info.NetworkingInfo.TargetRefs[i].Name < info.NetworkingInfo.TargetRefs[j].Name
})
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
Ingress: []corev1.LoadBalancerIngress{{IP: "107.178.210.11"}},
TargetRefs: []v1alpha1.ResourceRef{{
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "helm-guestbook",
}, {
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "not-found-service",
}},
ExternalURLs: []string{"http://helm-guestbook.example.com/"},
}, info.NetworkingInfo)
}
func TestGetIngressInfoWithHost(t *testing.T) {
ingress := strToUnstructured(`
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: helm-guestbook
namespace: default
spec:
rules:
- http:
paths:
- backend:
serviceName: helm-guestbook
servicePort: 443
path: /
tls:
- secretName: my-tls
status:
loadBalancer:
ingress:
- ip: 107.178.210.11`)
info := &ResourceInfo{}
populateNodeInfo(ingress, info, []string{})
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
Ingress: []corev1.LoadBalancerIngress{{IP: "107.178.210.11"}},
TargetRefs: []v1alpha1.ResourceRef{{
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "helm-guestbook",
}},
ExternalURLs: []string{"https://107.178.210.11/"},
}, info.NetworkingInfo)
}
func TestGetIngressInfoNoHost(t *testing.T) {
ingress := strToUnstructured(`
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: helm-guestbook
namespace: default
spec:
rules:
- http:
paths:
- backend:
serviceName: helm-guestbook
servicePort: 443
path: /
tls:
- secretName: my-tls
`)
info := &ResourceInfo{}
populateNodeInfo(ingress, info, []string{})
assert.Equal(t, &v1alpha1.ResourceNetworkingInfo{
TargetRefs: []v1alpha1.ResourceRef{{
Namespace: "default",
Group: "",
Kind: kube.ServiceKind,
Name: "helm-guestbook",
}},
}, info.NetworkingInfo)
assert.Empty(t, info.NetworkingInfo.ExternalURLs)
}
func TestExternalUrlWithSubPath(t *testing.T) {
ingress := strToUnstructured(`
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: helm-guestbook
namespace: default
spec:
rules:
- http:
paths:
- backend:
serviceName: helm-guestbook
servicePort: 443
path: /my/sub/path/
tls:
- secretName: my-tls
status:
loadBalancer:
ingress:
- ip: 107.178.210.11`)
info := &ResourceInfo{}
populateNodeInfo(ingress, info, []string{})
expectedExternalUrls := []string{"https://107.178.210.11/my/sub/path/"}
assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs)
}
func TestExternalUrlWithMultipleSubPaths(t *testing.T) {
ingress := strToUnstructured(`
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: helm-guestbook
namespace: default
spec:
rules:
- host: helm-guestbook.example.com
http:
paths:
- backend:
serviceName: helm-guestbook
servicePort: 443
path: /my/sub/path/
- backend:
serviceName: helm-guestbook-2
servicePort: 443
path: /my/sub/path/2
- backend:
serviceName: helm-guestbook-3
servicePort: 443
tls:
- secretName: my-tls
status:
loadBalancer:
ingress:
- ip: 107.178.210.11`)
info := &ResourceInfo{}
populateNodeInfo(ingress, info, []string{})
expectedExternalUrls := []string{"https://helm-guestbook.example.com/my/sub/path/", "https://helm-guestbook.example.com/my/sub/path/2", "https://helm-guestbook.example.com"}
actualURLs := info.NetworkingInfo.ExternalURLs
sort.Strings(expectedExternalUrls)
sort.Strings(actualURLs)
assert.Equal(t, expectedExternalUrls, actualURLs)
}
func TestExternalUrlWithNoSubPath(t *testing.T) {
ingress := strToUnstructured(`
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: helm-guestbook
namespace: default
spec:
rules:
- http:
paths:
- backend:
serviceName: helm-guestbook
servicePort: 443
tls:
- secretName: my-tls
status:
loadBalancer:
ingress:
- ip: 107.178.210.11`)
info := &ResourceInfo{}
populateNodeInfo(ingress, info, []string{})
expectedExternalUrls := []string{"https://107.178.210.11"}
assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs)
}
func TestExternalUrlWithNetworkingApi(t *testing.T) {
ingress := strToUnstructured(`
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: helm-guestbook
namespace: default
spec:
rules:
- http:
paths:
- backend:
serviceName: helm-guestbook
servicePort: 443
tls:
- secretName: my-tls
status:
loadBalancer:
ingress:
- ip: 107.178.210.11`)
info := &ResourceInfo{}
populateNodeInfo(ingress, info, []string{})
expectedExternalUrls := []string{"https://107.178.210.11"}
assert.Equal(t, expectedExternalUrls, info.NetworkingInfo.ExternalURLs)
}
func TestCustomLabel(t *testing.T) {
configmap := strToUnstructured(`
apiVersion: v1
kind: ConfigMap
metadata:
name: cm`)
info := &ResourceInfo{}
populateNodeInfo(configmap, info, []string{"my-label"})
assert.Empty(t, info.Info)
configmap = strToUnstructured(`
apiVersion: v1
kind: ConfigMap
metadata:
name: cm
labels:
my-label: value`)
info = &ResourceInfo{}
populateNodeInfo(configmap, info, []string{"my-label", "other-label"})
assert.Len(t, info.Info, 1)
assert.Equal(t, "my-label", info.Info[0].Name)
assert.Equal(t, "value", info.Info[0].Value)
configmap = strToUnstructured(`
apiVersion: v1
kind: ConfigMap
metadata:
name: cm
labels:
my-label: value
other-label: value2`)
info = &ResourceInfo{}
populateNodeInfo(configmap, info, []string{"my-label", "other-label"})
assert.Len(t, info.Info, 2)
assert.Equal(t, "my-label", info.Info[0].Name)
assert.Equal(t, "value", info.Info[0].Value)
assert.Equal(t, "other-label", info.Info[1].Name)
assert.Equal(t, "value2", info.Info[1].Value)
}
func TestManifestHash(t *testing.T) {
manifest := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
name: helm-guestbook-pod
namespace: default
ownerReferences:
- apiVersion: extensions/v1beta1
kind: ReplicaSet
name: helm-guestbook-rs
resourceVersion: "123"
labels:
app: guestbook
spec:
nodeName: minikube
containers:
- image: bar
resources:
requests:
memory: 128Mi
`)
ignores := []v1alpha1.ResourceIgnoreDifferences{
{
Group: "*",
Kind: "*",
JSONPointers: []string{"/metadata/resourceVersion"},
},
}
data, _ := strToUnstructured(`
apiVersion: v1
kind: Pod
metadata:
name: helm-guestbook-pod
namespace: default
ownerReferences:
- apiVersion: extensions/v1beta1
kind: ReplicaSet
name: helm-guestbook-rs
labels:
app: guestbook
spec:
nodeName: minikube
containers:
- image: bar
resources:
requests:
memory: 128Mi
`).MarshalJSON()
expected := hash(data)
hash, err := generateManifestHash(manifest, ignores, nil, normalizers.IgnoreNormalizerOpts{})
assert.Equal(t, expected, hash)
assert.NoError(t, err)
}