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))
	}
}
