Files
deploy/controller/cache/info_test.go
T
2026-05-22 09:49:18 +02:00

2042 lines
52 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`)
testMaliciousLinkAnnotatedService = strToUnstructured(`
apiVersion: v1
kind: Service
metadata:
name: helm-guestbook
namespace: default
resourceVersion: "123"
uid: "4"
annotations:
link.argocd.argoproj.io/javascript: 'javascript:alert(1)'
link.argocd.argoproj.io/data: 'data:text/html,<script>alert(1)</script>'
link.argocd.argoproj.io/vbscript: 'vbscript:msgbox(1)'
link.argocd.argoproj.io/titled-javascript: 'click me|javascript:alert(1)'
link.argocd.argoproj.io/no-scheme: 'example.com/foo'
link.argocd.argoproj.io/safe: '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
`)
testHTTPRoute = strToUnstructured(`
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: example-route
namespace: default
spec:
parentRefs:
- name: example-gateway
namespace: default
hostnames:
- example.com
rules:
- backendRefs:
- name: service-a
port: 8080
- name: service-b
port: 8080
`)
testHTTPRouteCrossNamespace = strToUnstructured(`
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: cross-ns-route
namespace: default
spec:
rules:
- backendRefs:
- name: service-other
namespace: other-ns
port: 8080
`)
testHTTPRouteWithExternalLink = strToUnstructured(`
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: annotated-route
namespace: default
annotations:
link.argocd.argoproj.io/external-link: https://example.com
spec:
rules:
- backendRefs:
- name: service-a
port: 8080
`)
testHTTPRouteMultipleRules = strToUnstructured(`
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: multi-rule-route
namespace: default
spec:
rules:
- matches:
- path:
value: /api
backendRefs:
- name: api-service
port: 8080
- matches:
- path:
value: /web
backendRefs:
- name: web-service
port: 80
`)
testHTTPRouteCrossNamespaceParent = strToUnstructured(`
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: cross-ns-parent-route
namespace: default
spec:
parentRefs:
- name: shared-gateway
namespace: gateway-ns
rules:
- backendRefs:
- name: my-service
port: 8080
`)
testGRPCRoute = strToUnstructured(`
apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
name: grpc-route
namespace: default
spec:
parentRefs:
- name: grpc-gateway
rules:
- backendRefs:
- name: grpc-service
port: 50051
`)
testGatewayWithIP = strToUnstructured(`
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: example-gateway
namespace: default
spec:
gatewayClassName: example-class
listeners:
- name: http
protocol: HTTP
port: 80
status:
addresses:
- type: IPAddress
value: 192.168.1.100
`)
testGatewayWithHostname = strToUnstructured(`
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: hostname-gateway
namespace: default
spec:
listeners:
- name: https
protocol: HTTPS
port: 443
hostname: api.example.com
status:
addresses:
- type: Hostname
value: lb.example.com
`)
testGatewayMultipleListeners = strToUnstructured(`
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: multi-gateway
namespace: default
spec:
listeners:
- name: http
protocol: HTTP
port: 80
- name: https
protocol: HTTPS
port: 443
status:
addresses:
- type: IPAddress
value: 10.0.0.1
`)
testGatewayNonStandardPort = strToUnstructured(`
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: custom-port-gateway
namespace: default
spec:
listeners:
- name: http-alt
protocol: HTTP
port: 8080
status:
addresses:
- type: IPAddress
value: 10.0.0.1
`)
testGatewayNoAddresses = strToUnstructured(`
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: pending-gateway
namespace: default
spec:
listeners:
- name: http
protocol: HTTP
port: 80
`)
testGatewayWithExternalLink = strToUnstructured(`
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: annotated-gateway
namespace: default
annotations:
link.argocd.argoproj.io/external-link: https://custom-link.example.com
spec:
listeners:
- name: http
protocol: HTTP
port: 80
status:
addresses:
- type: IPAddress
value: 10.0.0.1
`)
testGatewayIgnoreDefaultLinks = strToUnstructured(`
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: no-default-links-gateway
namespace: default
annotations:
link.argocd.argoproj.io/external-link: https://custom-link.example.com
argocd.argoproj.io/ignore-default-links: "true"
spec:
listeners:
- name: http
protocol: HTTP
port: 80
status:
addresses:
- type: IPAddress
value: 10.0.0.1
`)
)
// 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 TestMaliciousLinkAnnotatedServiceInfoFiltered(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testMaliciousLinkAnnotatedService, info, []string{})
require.NotNil(t, info.NetworkingInfo)
// Only the http URL should make it through; javascript:, data:, vbscript:,
// "title|javascript:..." and scheme-less values must be dropped.
assert.Equal(t, []string{"http://my-grafana.example.com/pre-generated-link"}, info.NetworkingInfo.ExternalURLs)
}
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 TestGetHTTPRouteInfo(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testHTTPRoute, info, []string{})
assert.Empty(t, info.Info)
require.NotNil(t, info.NetworkingInfo)
// Should have Gateway (parentRef) + 2 Services (backendRefs)
assert.Len(t, info.NetworkingInfo.TargetRefs, 3)
// Check Gateway ref from parentRefs
assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
Group: "gateway.networking.k8s.io",
Kind: "Gateway",
Namespace: "default",
Name: "example-gateway",
})
// Check Service refs from backendRefs
assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
Group: "",
Kind: kube.ServiceKind,
Namespace: "default",
Name: "service-a",
})
assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
Group: "",
Kind: kube.ServiceKind,
Namespace: "default",
Name: "service-b",
})
assert.Empty(t, info.NetworkingInfo.Ingress)
}
func TestGetHTTPRouteCrossNamespaceInfo(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testHTTPRouteCrossNamespace, info, []string{})
require.NotNil(t, info.NetworkingInfo)
assert.Len(t, info.NetworkingInfo.TargetRefs, 1)
assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
Group: "",
Kind: kube.ServiceKind,
Namespace: "other-ns",
Name: "service-other",
})
}
func TestGetHTTPRouteWithExternalLinkAnnotation(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testHTTPRouteWithExternalLink, info, []string{})
require.NotNil(t, info.NetworkingInfo)
assert.Contains(t, info.NetworkingInfo.ExternalURLs, "https://example.com")
}
func TestGetHTTPRouteMultipleRules(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testHTTPRouteMultipleRules, info, []string{})
require.NotNil(t, info.NetworkingInfo)
assert.Len(t, info.NetworkingInfo.TargetRefs, 2)
serviceNames := make([]string, 0)
for _, ref := range info.NetworkingInfo.TargetRefs {
serviceNames = append(serviceNames, ref.Name)
}
assert.Contains(t, serviceNames, "api-service")
assert.Contains(t, serviceNames, "web-service")
}
func TestGetHTTPRouteCrossNamespaceParent(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testHTTPRouteCrossNamespaceParent, info, []string{})
require.NotNil(t, info.NetworkingInfo)
// Should have Gateway (cross-namespace parentRef) + Service (backendRef)
assert.Len(t, info.NetworkingInfo.TargetRefs, 2)
// Gateway should use explicit namespace from parentRef
assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
Group: "gateway.networking.k8s.io",
Kind: "Gateway",
Namespace: "gateway-ns",
Name: "shared-gateway",
})
// Service should use HTTPRoute's namespace (default)
assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
Group: "",
Kind: kube.ServiceKind,
Namespace: "default",
Name: "my-service",
})
}
func TestGetGRPCRoute(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testGRPCRoute, info, []string{})
require.NotNil(t, info.NetworkingInfo)
// Should have Gateway (parentRef) + Service (backendRef)
assert.Len(t, info.NetworkingInfo.TargetRefs, 2)
assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
Group: "gateway.networking.k8s.io",
Kind: "Gateway",
Namespace: "default",
Name: "grpc-gateway",
})
assert.Contains(t, info.NetworkingInfo.TargetRefs, v1alpha1.ResourceRef{
Group: "",
Kind: kube.ServiceKind,
Namespace: "default",
Name: "grpc-service",
})
}
func TestGetGatewayInfoWithIP(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testGatewayWithIP, info, []string{})
assert.Empty(t, info.Info)
require.NotNil(t, info.NetworkingInfo)
assert.Len(t, info.NetworkingInfo.Ingress, 1)
assert.Equal(t, "192.168.1.100", info.NetworkingInfo.Ingress[0].IP)
assert.Contains(t, info.NetworkingInfo.ExternalURLs, "http://192.168.1.100")
}
func TestGetGatewayInfoWithHostname(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testGatewayWithHostname, info, []string{})
require.NotNil(t, info.NetworkingInfo)
assert.Len(t, info.NetworkingInfo.Ingress, 1)
assert.Equal(t, "lb.example.com", info.NetworkingInfo.Ingress[0].Hostname)
// Listener has explicit hostname, so URL should use that
assert.Contains(t, info.NetworkingInfo.ExternalURLs, "https://api.example.com")
}
func TestGetGatewayInfoMultipleListeners(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testGatewayMultipleListeners, info, []string{})
require.NotNil(t, info.NetworkingInfo)
assert.Len(t, info.NetworkingInfo.Ingress, 1)
assert.Equal(t, "10.0.0.1", info.NetworkingInfo.Ingress[0].IP)
// Should have both HTTP and HTTPS URLs
assert.Len(t, info.NetworkingInfo.ExternalURLs, 2)
assert.Contains(t, info.NetworkingInfo.ExternalURLs, "http://10.0.0.1")
assert.Contains(t, info.NetworkingInfo.ExternalURLs, "https://10.0.0.1")
}
func TestGetGatewayInfoNonStandardPort(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testGatewayNonStandardPort, info, []string{})
require.NotNil(t, info.NetworkingInfo)
assert.Len(t, info.NetworkingInfo.Ingress, 1)
// Non-standard port should be included in URL
assert.Contains(t, info.NetworkingInfo.ExternalURLs, "http://10.0.0.1:8080")
}
func TestGetGatewayInfoNoAddresses(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testGatewayNoAddresses, info, []string{})
require.NotNil(t, info.NetworkingInfo)
// Gateway with no addresses (e.g., pending) should have empty ingress
assert.Empty(t, info.NetworkingInfo.Ingress)
// No URLs since no addresses to use
assert.Empty(t, info.NetworkingInfo.ExternalURLs)
}
func TestGetGatewayInfoWithExternalLink(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testGatewayWithExternalLink, info, []string{})
require.NotNil(t, info.NetworkingInfo)
// Should have both custom link and generated URL
assert.Contains(t, info.NetworkingInfo.ExternalURLs, "https://custom-link.example.com")
assert.Contains(t, info.NetworkingInfo.ExternalURLs, "http://10.0.0.1")
}
func TestGetGatewayInfoIgnoreDefaultLinks(t *testing.T) {
info := &ResourceInfo{}
populateNodeInfo(testGatewayIgnoreDefaultLinks, info, []string{})
require.NotNil(t, info.NetworkingInfo)
// Should only have custom link, not generated URL
assert.Len(t, info.NetworkingInfo.ExternalURLs, 1)
assert.Contains(t, info.NetworkingInfo.ExternalURLs, "https://custom-link.example.com")
}
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)
}