package kubeadm

import (
	"os"
	"testing"

	"github.com/google/go-cmp/cmp"
)

const localEtcdClusterConf = `'apiServer:
  certSANs:
  - localhost
  - 127.0.0.1
  extraArgs:
    authorization-mode: Node,RBAC
    runtime-config: ""
  timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta2
certificatesDir: /etc/kubernetes/pki
clusterName: eksa-test-eks-a-cluster
controlPlaneEndpoint: eksa-test-eks-a-cluster-control-plane:6443
controllerManager:
  extraArgs:
    enable-hostpath-provisioner: "true"
dns:
  imageRepository: public.ecr.aws/eks-distro/coredns
  imageTag: v1.8.3-eks-1-20-5
  type: CoreDNS
etcd:
  local:
    dataDir: /var/lib/etcd
    imageRepository: public.ecr.aws/eks-distro/etcd-io
    imageTag: v3.4.15-eks-1-20-5
featureGates:
  IPv6DualStack: true
imageRepository: public.ecr.aws/eks-distro/kubernetes
kind: ClusterConfiguration
kubernetesVersion: v1.20.7-eks-1-20-5
networking:
  dnsDomain: cluster.local
  podSubnet: 10.244.0.0/16
  serviceSubnet: 10.96.0.0/16
scheduler: {}'`

const externalEtcdClusterConf = `'apiServer:
  extraArgs:
    cloud-provider: external
  timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta2
certificatesDir: /var/lib/kubeadm/pki
clusterName: eksa-test
controlPlaneEndpoint: 198.18.210.144:6443
controllerManager:
  extraArgs:
    cloud-provider: external
  extraVolumes:
  - hostPath: /var/lib/kubeadm/controller-manager.conf
    mountPath: /etc/kubernetes/controller-manager.conf
    name: kubeconfig
    pathType: File
    readOnly: true
dns:
  imageRepository: public.ecr.aws/eks-distro/coredns
  imageTag: v1.8.3-eks-1-20-5
  type: CoreDNS
etcd:
  external:
    caFile: /var/lib/kubeadm/pki/etcd/ca.crt
    certFile: /var/lib/kubeadm/pki/server-etcd-client.crt
    endpoints:
    - https://198.18.138.154:2379
    - https://198.18.138.155:2379
    - https://198.18.69.78:2379
    keyFile: /var/lib/kubeadm/pki/apiserver-etcd-client.key
imageRepository: public.ecr.aws/eks-distro/kubernetes
kind: ClusterConfiguration
kubernetesVersion: v1.20.7-eks-1-20-5
networking:
  dnsDomain: cluster.local
  podSubnet: 192.168.0.0/16
  serviceSubnet: 10.96.0.0/12
scheduler:
  extraVolumes:
  - hostPath: /var/lib/kubeadm/scheduler.conf
    mountPath: /etc/kubernetes/scheduler.conf
    name: kubeconfig
    pathType: File
    readOnly: true'`

func TestIsExternalEtcd(t *testing.T) {
	tests := []struct {
		testName             string
		clusterConfiguration []byte
		wantExternal         bool
	}{
		{
			testName:             "local",
			clusterConfiguration: []byte(localEtcdClusterConf),
			wantExternal:         false,
		},
		{
			testName:             "external",
			clusterConfiguration: []byte(externalEtcdClusterConf),
			wantExternal:         true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.testName, func(t *testing.T) {
			gotExternal, err := isExternalEtcd(tt.clusterConfiguration)
			if err != nil {
				t.Fatalf("isExternalEtcd() -> err = %v, want err = nil", err)
			}

			if gotExternal != tt.wantExternal {
				t.Fatalf("isExternalEtcd() -> gotExternal = %t, wantExternal = %t", gotExternal, tt.wantExternal)
			}
		})
	}
}

type mockFileReader func(filename string) ([]byte, error)

func (m mockFileReader) ReadFile(filename string) ([]byte, error) {
	return m(filename)
}

func TestReadKubeletTlsConfig(t *testing.T) {
	tests := []struct {
		testName                 string
		expectedKubeletTlsConfig *KubeletTlsConfig
		mockReader               func(t *testing.T) FileReader
	}{
		{
			testName:                 "skip config cert file missing",
			expectedKubeletTlsConfig: nil,
			mockReader: func(t *testing.T) FileReader {
				return mockFileReader(func(filename string) ([]byte, error) {
					t.Helper()
					if filename == "/.bottlerocket/rootfs/var/lib/kubeadm/pki/kubelet-serving.crt" {
						return nil, os.ErrNotExist
					} else {
						return []byte("mock-key"), nil
					}
				})
			},
		},
		{
			testName:                 "skip config key file missing",
			expectedKubeletTlsConfig: nil,
			mockReader: func(t *testing.T) FileReader {
				return mockFileReader(func(filename string) ([]byte, error) {
					t.Helper()
					if filename == "/.bottlerocket/rootfs/var/lib/kubeadm/pki/kubelet-serving.key" {
						return nil, os.ErrNotExist
					} else {
						return []byte("mock-cert"), nil
					}
				})
			},
		},
		{
			testName:                 "skip config cert file empty contents",
			expectedKubeletTlsConfig: nil,
			mockReader: func(t *testing.T) FileReader {
				return mockFileReader(func(filename string) ([]byte, error) {
					t.Helper()
					if filename == "/.bottlerocket/rootfs/var/lib/kubeadm/pki/kubelet-serving.crt" {
						return []byte{}, nil
					} else {
						return []byte("mock-key"), nil
					}
				})
			},
		},
		{
			testName:                 "skip config key file empty contents",
			expectedKubeletTlsConfig: nil,
			mockReader: func(t *testing.T) FileReader {
				return mockFileReader(func(filename string) ([]byte, error) {
					t.Helper()
					if filename == "/.bottlerocket/rootfs/var/lib/kubeadm/pki/kubelet-serving.key" {
						return []byte{}, nil
					} else {
						return []byte("mock-cert"), nil
					}
				})
			},
		},
		{
			testName:                 "success files present with contents",
			expectedKubeletTlsConfig: &KubeletTlsConfig{KubeletServingCert: "bW9jay1jcnQ=", KubeletServingPrivateKey: "bW9jay1rZXk="},
			mockReader: func(t *testing.T) FileReader {
				return mockFileReader(func(filename string) ([]byte, error) {
					t.Helper()
					switch filename {
					case "/.bottlerocket/rootfs/var/lib/kubeadm/pki/kubelet-serving.crt":
						return []byte("mock-crt"), nil

					case "/.bottlerocket/rootfs/var/lib/kubeadm/pki/kubelet-serving.key":
						return []byte("mock-key"), nil
					}
					return nil, nil
				})
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.testName, func(t *testing.T) {
			kubeletTlsConfig := readKubeletTlsConfig(tt.mockReader(t))
			if !cmp.Equal(kubeletTlsConfig, tt.expectedKubeletTlsConfig) {
				t.Fatalf("%v different from expected %v", kubeletTlsConfig, tt.expectedKubeletTlsConfig)
			}
		})
	}
}
