linkerd2/pkg/k8s/api_test.go

483 lines
8.2 KiB
Go

package k8s
import (
"context"
"testing"
corev1 "k8s.io/api/core/v1"
)
func TestGetPodStatus(t *testing.T) {
scenarios := []struct {
desc string
pod string
expected string
}{
{
desc: "Pod is running",
expected: "Running",
pod: `
apiVersion: v1
kind: Pod
metadata:
name: emoji
namespace: emojivoto
labels:
app: emoji-svc
linkerd.io/control-plane-ns: linkerd
status:
phase: Running
`,
},
{
desc: "Pod with proxy native sidecar is running",
expected: "Running",
pod: `
apiVersion: v1
kind: Pod
metadata:
name: emoji
namespace: emojivoto
labels:
app: emoji-svc
linkerd.io/control-plane-ns: linkerd
spec:
initContainers:
- name: linkerd-proxy
restartPolicy: Always
status:
phase: Running
initContainerStatuses:
- name: linkerd-proxy
ready: true
started: true
`,
},
{
desc: "Pod's reason is filled",
expected: "podReason",
pod: `
apiVersion: v1
kind: Pod
metadata:
name: emoji
namespace: emojivoto
labels:
app: emoji-svc
linkerd.io/control-plane-ns: linkerd
status:
phase: Pending
reason: podReason
`,
},
{
desc: "Pod waiting is filled",
expected: "CrashLoopBackOff",
pod: `
apiVersion: v1
kind: Pod
metadata:
name: emoji
namespace: emojivoto
labels:
app: emoji-svc
linkerd.io/control-plane-ns: linkerd
status:
phase: Pending
reason: podReason
containerStatuses:
- state:
waiting:
reason: CrashLoopBackOff
`,
},
{
desc: "Pod is terminated",
expected: "podTerminated",
pod: `
apiVersion: v1
kind: Pod
metadata:
name: emoji
namespace: emojivoto
labels:
app: emoji-svc
linkerd.io/control-plane-ns: linkerd
status:
phase: Pending
reason: podReason
containerStatuses:
- state:
terminated:
reason: podTerminated
exitCode: 2
`,
},
{
desc: "Pod terminated with signal",
expected: "Signal:9",
pod: `
apiVersion: v1
kind: Pod
metadata:
name: emoji
namespace: emojivoto
labels:
app: emoji-svc
linkerd.io/control-plane-ns: linkerd
status:
phase: Pending
reason: podReason
containerStatuses:
- state:
terminated:
signal: 9
`,
},
{
desc: "Pod terminated with exti code",
expected: "ExitCode:2",
pod: `
apiVersion: v1
kind: Pod
metadata:
name: emoji
namespace: emojivoto
labels:
app: emoji-svc
linkerd.io/control-plane-ns: linkerd
status:
phase: Pending
reason: podReason
containerStatuses:
- state:
terminated:
exitCode: 2
`,
},
{
desc: "Pod has a running container",
expected: "Running",
pod: `
apiVersion: v1
kind: Pod
metadata:
name: emoji
namespace: emojivoto
labels:
app: emoji-svc
linkerd.io/control-plane-ns: linkerd
status:
phase: Running
reason: Completed
containerStatuses:
- ready: true
state:
running:
startedAt: 1995-02-10T00:42:42Z
`,
},
{
desc: "Pod init container terminated with exit code 0",
expected: "Running",
pod: `
apiVersion: v1
kind: Pod
metadata:
name: emoji
namespace: emojivoto
labels:
app: emoji-svc
linkerd.io/control-plane-ns: linkerd
spec:
initContainers:
- name: foo
status:
phase: Running
containerStatuses:
- state:
running:
startedAt: 1995-02-10T00:42:42Z
initContainerStatuses:
- name: foo
state:
terminated:
exitCode: 0
reason: Completed
`,
},
{
desc: "Pod init container terminated with exit code 2",
expected: "Init:ExitCode:2",
pod: `
apiVersion: v1
kind: Pod
metadata:
name: emoji
namespace: emojivoto
labels:
app: emoji-svc
linkerd.io/control-plane-ns: linkerd
spec:
initContainers:
- name: foo
containers:
- name: bar
status:
phase: Running
containerStatuses:
- name: bar
state:
running:
startedAt: 1995-02-10T00:42:42Z
initContainerStatuses:
- name: foo
state:
terminated:
exitCode: 2
`,
},
{
desc: "Pod init container terminated with signal",
expected: "Init:Signal:9",
pod: `
apiVersion: v1
kind: Pod
metadata:
name: emoji
namespace: emojivoto
labels:
app: emoji-svc
linkerd.io/control-plane-ns: linkerd
spec:
initContainers:
- name: foo
containers:
- name: bar
status:
phase: Running
containerStatuses:
- name: bar
state:
running:
startedAt: 1995-02-10T00:42:42Z
initContainerStatuses:
- name: foo
state:
terminated:
signal: 9
`,
},
{
desc: "Pod init container in CrashLooBackOff",
expected: "Init:CrashLoopBackOff",
pod: `
apiVersion: v1
kind: Pod
metadata:
name: emoji
namespace: emojivoto
labels:
app: emoji-svc
linkerd.io/control-plane-ns: linkerd
spec:
initContainers:
- name: foo
status:
phase: Pending
initContainerStatuses:
- name: foo
state:
terminated:
exitCode: 2
reason: CrashLoopBackOff
`,
},
{
desc: "Pod init container waiting for someReason",
expected: "Init:someReason",
pod: `
apiVersion: v1
kind: Pod
metadata:
name: emoji
namespace: emojivoto
labels:
app: emoji-svc
linkerd.io/control-plane-ns: linkerd
spec:
initContainers:
- name: foo
status:
phase: Pending
initContainerStatuses:
- name: foo
state:
waiting:
reason: someReason
`,
},
{
desc: "Pod init container is waiting on PodInitializing",
expected: "Init:0/1",
pod: `
apiVersion: v1
kind: Pod
metadata:
name: emoji
namespace: emojivoto
labels:
app: emoji-svc
linkerd.io/control-plane-ns: linkerd
spec:
initContainers:
- name: foo
status:
phase: Pending
initContainerStatuses:
- name: foo
state:
waiting:
reason: PodInitializing
`,
},
}
for _, s := range scenarios {
obj, err := ToRuntimeObject(s.pod)
if err != nil {
t.Fatalf("could not decode yml: %s", err)
}
pod, ok := obj.(*corev1.Pod)
if !ok {
t.Fatalf("could not convert returned object to pod")
}
got := GetPodStatus(*pod)
if s.expected != got {
t.Fatalf("Wrong pod status on '%s'. Expected '%s', got '%s'", s.desc, s.expected, got)
}
}
}
func TestGetPodsFor(t *testing.T) {
configs := []string{
// pod-1
`apiVersion: v1
kind: Pod
metadata:
name: pod-1
namespace: ns
uid: pod-1
labels:
app: foo
ownerReferences:
- apiVersion: apps/v1
controller: true
kind: ReplicaSet
name: rs-1
uid: rs-1
`,
// rs-1
`apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: rs-1
namespace: ns
uid: rs-1
labels:
app: foo
ownerReferences:
- apiVersion: apps/v1
controller: true
kind: Deployment
name: deploy-1
uid: deploy-1
spec:
selector:
matchLabels:
app: foo
`,
// deploy-1
`apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-1
namespace: ns
uid: deploy-1
spec:
selector:
matchLabels:
app: foo
`,
// pod-2
`apiVersion: v1
kind: Pod
metadata:
name: pod-2
namespace: ns
uid: pod-2
labels:
app: foo
ownerReferences:
- apiVersion: apps/v1
controller: true
kind: ReplicaSet
name: rs-2
uid: rs-2
`,
// rs-2
`apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: rs-2
namespace: ns
uid: rs-2
labels:
app: foo
ownerReferences:
- apiVersion: apps/v1
controller: true
kind: Deployment
name: deploy-2
uid: deploy-2
spec:
selector:
matchLabels:
app: foo
`,
// deploy-2
`apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-2
namespace: ns
uid: deploy-2
spec:
selector:
matchLabels:
app: foo
`}
k8sClient, err := NewFakeAPI(configs...)
if err != nil {
t.Fatalf("Unexpected error %s", err)
}
// Both pod-1 and pod-2 have labels which match deploy-1's selector.
// However, only pod-1 is owned by deploy-1 according to the owner references.
// Owner references should be considered authoritative to resolve ambiguity
// when deployments have overlapping seletors.
pods, err := GetPodsFor(context.Background(), k8sClient, "ns", "deploy/deploy-1")
if err != nil {
t.Fatalf("Unexpected error %s", err)
}
if len(pods) != 1 {
for _, p := range pods {
t.Logf("%s/%s", p.Namespace, p.Name)
}
t.Fatalf("Expected 1 pod, got %d", len(pods))
}
}