mirror of
https://github.com/hanzoai/deploy.git
synced 2026-10-07 08:44:50 +00:00
go.mod still declared `module github.com/hanzoai/deploy` after the repo was renamed. A Go module path is not cosmetic — it is the resolvable location. So `go get github.com/hanzoai/cd` could not work, and anything importing this module had to spell a name that no longer exists at that address. The build log of v3.6.0 is where it surfaced: the compiler was still emitting github.com/hanzoai/deploy/cmd/cd/commands. 3541 import paths across 690 .go, 19 .proto, 4 .yaml, 3 .sh, 2 .mod, Procfile and Makefile. Plus the 62 ghcr.io/hanzoai/deploy image references, moved to ghcr.io/hanzoai/cd to match hanzo.yml's build target — a distinct string from the module path, replaced separately so neither could clobber the other. go mod tidy clean, CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ./... clean. Co-authored-by: Hanzo Dev <[email protected]>
2044 lines
52 KiB
Go
2044 lines
52 KiB
Go
package cache
|
|
|
|
import (
|
|
"sort"
|
|
"testing"
|
|
|
|
"github.com/hanzoai/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/hanzoai/cd/common"
|
|
"github.com/hanzoai/cd/pkg/apis/application/v1alpha1"
|
|
"github.com/hanzoai/cd/util/cd/normalizers"
|
|
"github.com/hanzoai/cd/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.cd.hanzo.ai/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.cd.hanzo.ai/javascript: 'javascript:alert(1)'
|
|
link.cd.hanzo.ai/data: 'data:text/html,<script>alert(1)</script>'
|
|
link.cd.hanzo.ai/vbscript: 'vbscript:msgbox(1)'
|
|
link.cd.hanzo.ai/titled-javascript: 'click me|javascript:alert(1)'
|
|
link.cd.hanzo.ai/no-scheme: 'example.com/foo'
|
|
link.cd.hanzo.ai/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.cd.hanzo.ai/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.cd.hanzo.ai/external-link: http://my-grafana.example.com/ingress-link
|
|
cd.hanzo.ai/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.cd.hanzo.ai/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.cd.hanzo.ai/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.cd.hanzo.ai/external-link: https://custom-link.example.com
|
|
cd.hanzo.ai/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) {
|
|
t.Parallel()
|
|
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) {
|
|
t.Parallel()
|
|
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)
|
|
}
|