//go:build linux && !remote

package kube

import (
	"math"
	"runtime"
	"strconv"
	"testing"

	"github.com/containers/common/pkg/secrets"
	"github.com/containers/podman/v5/libpod/define"
	v1 "github.com/containers/podman/v5/pkg/k8s.io/api/core/v1"
	"github.com/containers/podman/v5/pkg/k8s.io/apimachinery/pkg/api/resource"
	v12 "github.com/containers/podman/v5/pkg/k8s.io/apimachinery/pkg/apis/meta/v1"
	"github.com/containers/podman/v5/pkg/k8s.io/apimachinery/pkg/util/intstr"
	"github.com/containers/podman/v5/pkg/specgen"
	"github.com/docker/docker/pkg/meminfo"
	"github.com/stretchr/testify/assert"
	"sigs.k8s.io/yaml"
)

func createSecrets(t *testing.T, d string) *secrets.SecretsManager {
	secretsManager, err := secrets.NewManager(d)
	assert.NoError(t, err)

	driver := "file"
	driverOpts := map[string]string{
		"path": d,
	}

	storeOpts := secrets.StoreOptions{
		DriverOpts: driverOpts,
	}

	for _, s := range k8sSecrets {
		data, err := yaml.Marshal(s)
		assert.NoError(t, err)

		_, err = secretsManager.Store(s.ObjectMeta.Name, data, driver, storeOpts)
		assert.NoError(t, err)
	}

	return secretsManager
}

func TestConfigMapVolumes(t *testing.T) {
	yes := true
	tests := []struct {
		name          string
		volume        v1.Volume
		configmaps    []v1.ConfigMap
		errorMessage  string
		expectedItems map[string][]byte
	}{
		{
			"VolumeFromConfigmap",
			v1.Volume{
				Name: "test-volume",
				VolumeSource: v1.VolumeSource{
					ConfigMap: &v1.ConfigMapVolumeSource{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "bar",
						},
					},
				},
			},
			configMapList,
			"",
			map[string][]byte{"myvar": []byte("bar")},
		},
		{
			"VolumeFromBinaryConfigmap",
			v1.Volume{
				Name: "test-volume",
				VolumeSource: v1.VolumeSource{
					ConfigMap: &v1.ConfigMapVolumeSource{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "binary-bar",
						},
					},
				},
			},
			configMapList,
			"",
			map[string][]byte{"myvar": []byte("bin-bar")},
		},
		{
			"ConfigmapMissing",
			v1.Volume{
				Name: "test-volume",
				VolumeSource: v1.VolumeSource{
					ConfigMap: &v1.ConfigMapVolumeSource{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "fizz",
						},
					},
				},
			},
			configMapList,
			`no such ConfigMap "fizz"`,
			map[string][]byte{},
		},
		{
			"ConfigmapMissingOptional",
			v1.Volume{
				Name: "test-volume",
				VolumeSource: v1.VolumeSource{
					ConfigMap: &v1.ConfigMapVolumeSource{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "fizz",
						},
						Optional: &yes,
					},
				},
			},
			configMapList,
			"",
			map[string][]byte{},
		},
		{
			"MultiValue",
			v1.Volume{
				Name: "test-volume",
				VolumeSource: v1.VolumeSource{
					ConfigMap: &v1.ConfigMapVolumeSource{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "multi-item",
						},
						Optional: &yes,
					},
				},
			},
			configMapList,
			"",
			map[string][]byte{"foo": []byte("bar"), "fizz": []byte("buzz")},
		},
		{
			"SpecificValue",
			v1.Volume{
				Name: "test-volume",
				VolumeSource: v1.VolumeSource{
					ConfigMap: &v1.ConfigMapVolumeSource{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "multi-item",
						},
						Optional: &yes,
						Items:    []v1.KeyToPath{{Key: "fizz", Path: "/custom/path"}},
					},
				},
			},
			configMapList,
			"",
			map[string][]byte{"/custom/path": []byte("buzz")},
		},
		{
			"MultiValueBinary",
			v1.Volume{
				Name: "test-volume",
				VolumeSource: v1.VolumeSource{
					ConfigMap: &v1.ConfigMapVolumeSource{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "multi-binary-item",
						},
						Optional: &yes,
					},
				},
			},
			configMapList,
			"",
			map[string][]byte{"foo": []byte("bin-bar"), "fizz": []byte("bin-buzz")},
		},
		{
			"SpecificValueBinary",
			v1.Volume{
				Name: "test-volume",
				VolumeSource: v1.VolumeSource{
					ConfigMap: &v1.ConfigMapVolumeSource{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "multi-binary-item",
						},
						Optional: &yes,
						Items:    []v1.KeyToPath{{Key: "fizz", Path: "/custom/path"}},
					},
				},
			},
			configMapList,
			"",
			map[string][]byte{"/custom/path": []byte("bin-buzz")},
		},
		{
			"DuplicateValues",
			v1.Volume{
				Name: "test-volume",
				VolumeSource: v1.VolumeSource{
					ConfigMap: &v1.ConfigMapVolumeSource{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "dupe",
						},
					},
				},
			},
			configMapList,
			`the ConfigMap "dupe" is invalid: duplicate key "foo" present in data and binaryData`,
			map[string][]byte{},
		},
		{
			"DuplicateValuesSpecific",
			v1.Volume{
				Name: "test-volume",
				VolumeSource: v1.VolumeSource{
					ConfigMap: &v1.ConfigMapVolumeSource{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "dupe",
						},
						Items: []v1.KeyToPath{{Key: "fizz", Path: "/custom/path"}},
					},
				},
			},
			configMapList,
			`the ConfigMap "dupe" is invalid: duplicate key "foo" present in data and binaryData`,
			map[string][]byte{},
		},
	}

	for _, test := range tests {
		test := test
		t.Run(test.name, func(t *testing.T) {
			result, err := VolumeFromConfigMap(test.volume.ConfigMap, test.configmaps)
			if test.errorMessage == "" {
				assert.NoError(t, err)
				assert.Equal(t, test.expectedItems, result.Items)
			} else {
				assert.Error(t, err)
				assert.Equal(t, test.errorMessage, err.Error())
			}
		})
	}
}

func TestEnvVarsFrom(t *testing.T) {
	d := t.TempDir()
	secretsManager := createSecrets(t, d)

	tests := []struct {
		name     string
		envFrom  v1.EnvFromSource
		options  CtrSpecGenOptions
		succeed  bool
		expected map[string]string
	}{
		{
			"ConfigMapExists",
			v1.EnvFromSource{
				ConfigMapRef: &v1.ConfigMapEnvSource{
					LocalObjectReference: v1.LocalObjectReference{
						Name: "foo",
					},
				},
			},
			CtrSpecGenOptions{
				ConfigMaps: configMapList,
			},
			true,
			map[string]string{
				"myvar": "foo",
			},
		},
		{
			"ConfigMapDoesNotExist",
			v1.EnvFromSource{
				ConfigMapRef: &v1.ConfigMapEnvSource{
					LocalObjectReference: v1.LocalObjectReference{
						Name: "doesnotexist",
					},
				},
			},
			CtrSpecGenOptions{
				ConfigMaps: configMapList,
			},
			false,
			nil,
		},
		{
			"OptionalConfigMapDoesNotExist",
			v1.EnvFromSource{
				ConfigMapRef: &v1.ConfigMapEnvSource{
					LocalObjectReference: v1.LocalObjectReference{
						Name: "doesnotexist",
					},
					Optional: &optional,
				},
			},
			CtrSpecGenOptions{
				ConfigMaps: configMapList,
			},
			true,
			map[string]string{},
		},
		{
			"EmptyConfigMapList",
			v1.EnvFromSource{
				ConfigMapRef: &v1.ConfigMapEnvSource{
					LocalObjectReference: v1.LocalObjectReference{
						Name: "foo",
					},
				},
			},
			CtrSpecGenOptions{
				ConfigMaps: []v1.ConfigMap{},
			},
			false,
			nil,
		},
		{
			"OptionalEmptyConfigMapList",
			v1.EnvFromSource{
				ConfigMapRef: &v1.ConfigMapEnvSource{
					LocalObjectReference: v1.LocalObjectReference{
						Name: "foo",
					},
					Optional: &optional,
				},
			},
			CtrSpecGenOptions{
				ConfigMaps: []v1.ConfigMap{},
			},
			true,
			map[string]string{},
		},
		{
			"SecretExists",
			v1.EnvFromSource{
				SecretRef: &v1.SecretEnvSource{
					LocalObjectReference: v1.LocalObjectReference{
						Name: "foo",
					},
				},
			},
			CtrSpecGenOptions{
				SecretsManager: secretsManager,
			},
			true,
			map[string]string{
				"myvar": "foo",
			},
		},
		{
			"SecretDoesNotExist",
			v1.EnvFromSource{
				SecretRef: &v1.SecretEnvSource{
					LocalObjectReference: v1.LocalObjectReference{
						Name: "doesnotexist",
					},
				},
			},
			CtrSpecGenOptions{
				SecretsManager: secretsManager,
			},
			false,
			nil,
		},
		{
			"SecretExistsMultipleDataEntries",
			v1.EnvFromSource{
				SecretRef: &v1.SecretEnvSource{
					LocalObjectReference: v1.LocalObjectReference{
						Name: "multi-data",
					},
				},
			},
			CtrSpecGenOptions{
				SecretsManager: secretsManager,
			},
			true,
			map[string]string{
				"myvar":  "foo",
				"myvar1": "foo1",
			},
		},
		{
			"SecretExistsMultipleStringDataEntries",
			v1.EnvFromSource{
				SecretRef: &v1.SecretEnvSource{
					LocalObjectReference: v1.LocalObjectReference{
						Name: "multi-stringdata",
					},
				},
			},
			CtrSpecGenOptions{
				SecretsManager: secretsManager,
			},
			true,
			map[string]string{
				"myvar":  "foo",
				"myvar1": "foo1",
			},
		},
		{
			"SecretExistsMultipleDataStringDataEntries",
			v1.EnvFromSource{
				SecretRef: &v1.SecretEnvSource{
					LocalObjectReference: v1.LocalObjectReference{
						Name: "multi-data-stringdata",
					},
				},
			},
			CtrSpecGenOptions{
				SecretsManager: secretsManager,
			},
			true,
			map[string]string{
				"myvardata":   "foodata",
				"myvar1":      "foo1string", // stringData overwrites data
				"myvarstring": "foostring",
			},
		},
		{
			"OptionalSecretDoesNotExist",
			v1.EnvFromSource{
				SecretRef: &v1.SecretEnvSource{
					LocalObjectReference: v1.LocalObjectReference{
						Name: "doesnotexist",
					},
					Optional: &optional,
				},
			},
			CtrSpecGenOptions{
				SecretsManager: secretsManager,
			},
			true,
			map[string]string{},
		},
	}

	for _, test := range tests {
		test := test
		t.Run(test.name, func(t *testing.T) {
			result, err := envVarsFrom(test.envFrom, &test.options)
			assert.Equal(t, err == nil, test.succeed)
			assert.Equal(t, test.expected, result)
		})
	}
}

func TestEnvVarValue(t *testing.T) {
	d := t.TempDir()
	secretsManager := createSecrets(t, d)
	stringNumCPUs := strconv.Itoa(runtime.NumCPU())

	mi, err := meminfo.Read()
	assert.Nil(t, err)
	stringMemTotal := strconv.FormatInt(mi.MemTotal, 10)

	tests := []struct {
		name     string
		envVar   v1.EnvVar
		options  CtrSpecGenOptions
		succeed  bool
		expected string
	}{
		{
			"ConfigMapExists",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ConfigMapKeyRef: &v1.ConfigMapKeySelector{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "foo",
						},
						Key: "myvar",
					},
				},
			},
			CtrSpecGenOptions{
				ConfigMaps: configMapList,
			},
			true,
			"foo",
		},
		{
			"ContainerKeyDoesNotExistInConfigMap",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ConfigMapKeyRef: &v1.ConfigMapKeySelector{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "foo",
						},
						Key: "doesnotexist",
					},
				},
			},
			CtrSpecGenOptions{
				ConfigMaps: configMapList,
			},
			false,
			nilString,
		},
		{
			"OptionalContainerKeyDoesNotExistInConfigMap",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ConfigMapKeyRef: &v1.ConfigMapKeySelector{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "foo",
						},
						Key:      "doesnotexist",
						Optional: &optional,
					},
				},
			},
			CtrSpecGenOptions{
				ConfigMaps: configMapList,
			},
			true,
			nilString,
		},
		{
			"ConfigMapDoesNotExist",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ConfigMapKeyRef: &v1.ConfigMapKeySelector{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "doesnotexist",
						},
						Key: "myvar",
					},
				},
			},
			CtrSpecGenOptions{
				ConfigMaps: configMapList,
			},
			false,
			nilString,
		},
		{
			"OptionalConfigMapDoesNotExist",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ConfigMapKeyRef: &v1.ConfigMapKeySelector{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "doesnotexist",
						},
						Key:      "myvar",
						Optional: &optional,
					},
				},
			},
			CtrSpecGenOptions{
				ConfigMaps: configMapList,
			},
			true,
			nilString,
		},
		{
			"EmptyConfigMapList",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ConfigMapKeyRef: &v1.ConfigMapKeySelector{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "foo",
						},
						Key: "myvar",
					},
				},
			},
			CtrSpecGenOptions{
				ConfigMaps: []v1.ConfigMap{},
			},
			false,
			nilString,
		},
		{
			"OptionalEmptyConfigMapList",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ConfigMapKeyRef: &v1.ConfigMapKeySelector{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "foo",
						},
						Key:      "myvar",
						Optional: &optional,
					},
				},
			},
			CtrSpecGenOptions{
				ConfigMaps: []v1.ConfigMap{},
			},
			true,
			nilString,
		},
		{
			"SecretExists",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					SecretKeyRef: &v1.SecretKeySelector{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "foo",
						},
						Key: "myvar",
					},
				},
			},
			CtrSpecGenOptions{
				SecretsManager: secretsManager,
			},
			true,
			"foo",
		},
		{
			"ContainerKeyDoesNotExistInSecret",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					SecretKeyRef: &v1.SecretKeySelector{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "foo",
						},
						Key: "doesnotexist",
					},
				},
			},
			CtrSpecGenOptions{
				SecretsManager: secretsManager,
			},
			false,
			nilString,
		},
		{
			"OptionalContainerKeyDoesNotExistInSecret",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					SecretKeyRef: &v1.SecretKeySelector{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "foo",
						},
						Key:      "doesnotexist",
						Optional: &optional,
					},
				},
			},
			CtrSpecGenOptions{
				SecretsManager: secretsManager,
			},
			true,
			nilString,
		},
		{
			"SecretDoesNotExist",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					SecretKeyRef: &v1.SecretKeySelector{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "doesnotexist",
						},
						Key: "myvar",
					},
				},
			},
			CtrSpecGenOptions{
				SecretsManager: secretsManager,
			},
			false,
			nilString,
		},
		{
			"OptionalSecretDoesNotExist",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					SecretKeyRef: &v1.SecretKeySelector{
						LocalObjectReference: v1.LocalObjectReference{
							Name: "doesnotexist",
						},
						Key:      "myvar",
						Optional: &optional,
					},
				},
			},
			CtrSpecGenOptions{
				SecretsManager: secretsManager,
			},
			true,
			nilString,
		},
		{
			"FieldRefMetadataName",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						FieldPath: "metadata.name",
					},
				},
			},
			CtrSpecGenOptions{
				PodName: "test",
			},
			true,
			"test",
		},
		{
			"FieldRefMetadataUID",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						FieldPath: "metadata.uid",
					},
				},
			},
			CtrSpecGenOptions{
				PodID: "ec71ff37c67b688598c0008187ab0960dc34e1dfdcbf3a74e3d778bafcfe0977",
			},
			true,
			"ec71ff37c67b688598c0008187ab0960dc34e1dfdcbf3a74e3d778bafcfe0977",
		},
		{
			"FieldRefMetadataLabelsExist",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						FieldPath: "metadata.labels['label']",
					},
				},
			},
			CtrSpecGenOptions{
				Labels: map[string]string{"label": "label"},
			},
			true,
			"label",
		},
		{
			"FieldRefMetadataLabelsEmpty",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						FieldPath: "metadata.labels['label']",
					},
				},
			},
			CtrSpecGenOptions{
				Labels: map[string]string{"label": ""},
			},
			true,
			"",
		},
		{
			"FieldRefMetadataLabelsNotExist",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						FieldPath: "metadata.labels['label']",
					},
				},
			},
			CtrSpecGenOptions{},
			true,
			"",
		},
		{
			"FieldRefMetadataAnnotationsExist",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						FieldPath: "metadata.annotations['annotation']",
					},
				},
			},
			CtrSpecGenOptions{
				Annotations: map[string]string{"annotation": "annotation"},
			},
			true,
			"annotation",
		},
		{
			"FieldRefMetadataAnnotationsEmpty",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						FieldPath: "metadata.annotations['annotation']",
					},
				},
			},
			CtrSpecGenOptions{
				Annotations: map[string]string{"annotation": ""},
			},
			true,
			"",
		},
		{
			"FieldRefMetadataAnnotationsNotExist",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						FieldPath: "metadata.annotations['annotation']",
					},
				},
			},
			CtrSpecGenOptions{},
			true,
			"",
		},
		{
			"FieldRefInvalid1",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						FieldPath: "metadata.annotations['annotation]",
					},
				},
			},
			CtrSpecGenOptions{},
			false,
			nilString,
		},
		{
			"FieldRefInvalid2",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						FieldPath: "metadata.dummy['annotation']",
					},
				},
			},
			CtrSpecGenOptions{},
			false,
			nilString,
		},
		{
			"FieldRefNotSupported",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					FieldRef: &v1.ObjectFieldSelector{
						FieldPath: "metadata.namespace",
					},
				},
			},
			CtrSpecGenOptions{},
			false,
			nilString,
		},
		{
			"ResourceFieldRefNotSupported",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.dummy",
					},
				},
			},
			CtrSpecGenOptions{},
			false,
			nilString,
		},
		{
			"ResourceFieldRefMemoryDivisorNotValid",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.memory",
						Divisor:  resource.MustParse("2M"),
					},
				},
			},
			CtrSpecGenOptions{},
			false,
			nilString,
		},
		{
			"ResourceFieldRefCpuDivisorNotValid",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.cpu",
						Divisor:  resource.MustParse("2m"),
					},
				},
			},
			CtrSpecGenOptions{},
			false,
			nilString,
		},
		{
			"ResourceFieldRefNoDivisor",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.memory",
					},
				},
			},
			CtrSpecGenOptions{
				Container: container,
			},
			true,
			memoryString,
		},
		{
			"ResourceFieldRefMemoryDivisor",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.memory",
						Divisor:  resource.MustParse("1Mi"),
					},
				},
			},
			CtrSpecGenOptions{
				Container: container,
			},
			true,
			strconv.Itoa(int(math.Ceil(float64(memoryInt) / 1024 / 1024))),
		},
		{
			"ResourceFieldRefCpuDivisor",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "requests.cpu",
						Divisor:  resource.MustParse("1m"),
					},
				},
			},
			CtrSpecGenOptions{
				Container: container,
			},
			true,
			strconv.Itoa(int(float64(cpuInt) / 0.001)),
		},
		{
			"ResourceFieldRefNoLimitMemory",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.memory",
					},
				},
			},
			CtrSpecGenOptions{
				Container: v1.Container{
					Name: "test",
				},
			},
			true,
			stringMemTotal,
		},
		{
			"ResourceFieldRefNoRequestMemory",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "requests.memory",
					},
				},
			},
			CtrSpecGenOptions{
				Container: v1.Container{
					Name: "test",
				},
			},
			true,
			stringMemTotal,
		},
		{
			"ResourceFieldRefNoLimitCPU",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "limits.cpu",
					},
				},
			},
			CtrSpecGenOptions{
				Container: v1.Container{
					Name: "test",
				},
			},
			true,
			stringNumCPUs,
		},
		{
			"ResourceFieldRefNoRequestCPU",
			v1.EnvVar{
				Name: "FOO",
				ValueFrom: &v1.EnvVarSource{
					ResourceFieldRef: &v1.ResourceFieldSelector{
						Resource: "requests.cpu",
					},
				},
			},
			CtrSpecGenOptions{
				Container: v1.Container{
					Name: "test",
				},
			},
			true,
			stringNumCPUs,
		},
	}

	for _, test := range tests {
		test := test
		t.Run(test.name, func(t *testing.T) {
			result, err := envVarValue(test.envVar, &test.options)
			assert.Equal(t, err == nil, test.succeed)
			if test.expected == nilString {
				assert.Nil(t, result)
			} else {
				assert.Equal(t, test.expected, *result)
			}
		})
	}
}

var (
	nilString     = "<nil>"
	configMapList = []v1.ConfigMap{
		{
			TypeMeta: v12.TypeMeta{
				Kind: "ConfigMap",
			},
			ObjectMeta: v12.ObjectMeta{
				Name: "bar",
			},
			Data: map[string]string{
				"myvar": "bar",
			},
		},
		{
			TypeMeta: v12.TypeMeta{
				Kind: "ConfigMap",
			},
			ObjectMeta: v12.ObjectMeta{
				Name: "foo",
			},
			Data: map[string]string{
				"myvar": "foo",
			},
		},
		{
			TypeMeta: v12.TypeMeta{
				Kind: "ConfigMap",
			},
			ObjectMeta: v12.ObjectMeta{
				Name: "binary-bar",
			},
			BinaryData: map[string][]byte{
				"myvar": []byte("bin-bar"),
			},
		},
		{
			TypeMeta: v12.TypeMeta{
				Kind: "ConfigMap",
			},
			ObjectMeta: v12.ObjectMeta{
				Name: "multi-item",
			},
			Data: map[string]string{
				"foo":  "bar",
				"fizz": "buzz",
			},
		},
		{
			TypeMeta: v12.TypeMeta{
				Kind: "ConfigMap",
			},
			ObjectMeta: v12.ObjectMeta{
				Name: "multi-binary-item",
			},
			BinaryData: map[string][]byte{
				"foo":  []byte("bin-bar"),
				"fizz": []byte("bin-buzz"),
			},
		},
		{
			TypeMeta: v12.TypeMeta{
				Kind: "ConfigMap",
			},
			ObjectMeta: v12.ObjectMeta{
				Name: "dupe",
			},
			BinaryData: map[string][]byte{
				"fiz": []byte("bin-buzz"),
				"foo": []byte("bin-bar"),
			},
			Data: map[string]string{
				"foo": "bar",
			},
		},
	}

	optional = true

	k8sSecrets = []v1.Secret{
		{
			TypeMeta: v12.TypeMeta{
				Kind: "Secret",
			},
			ObjectMeta: v12.ObjectMeta{
				Name: "bar",
			},
			Data: map[string][]byte{
				"myvar": []byte("bar"),
			},
		},
		{
			TypeMeta: v12.TypeMeta{
				Kind: "Secret",
			},
			ObjectMeta: v12.ObjectMeta{
				Name: "foo",
			},
			Data: map[string][]byte{
				"myvar": []byte("foo"),
			},
		},
		{
			TypeMeta: v12.TypeMeta{
				Kind: "Secret",
			},
			ObjectMeta: v12.ObjectMeta{
				Name: "multi-data",
			},
			Data: map[string][]byte{
				"myvar":  []byte("foo"),
				"myvar1": []byte("foo1"),
			},
		},
		{
			TypeMeta: v12.TypeMeta{
				Kind: "Secret",
			},
			ObjectMeta: v12.ObjectMeta{
				Name: "multi-stringdata",
			},
			StringData: map[string]string{
				"myvar":  string("foo"),
				"myvar1": string("foo1"),
			},
		},
		{
			TypeMeta: v12.TypeMeta{
				Kind: "Secret",
			},
			ObjectMeta: v12.ObjectMeta{
				Name: "multi-data-stringdata",
			},
			Data: map[string][]byte{
				"myvardata": []byte("foodata"),
				"myvar1":    []byte("foo1data"),
			},
			StringData: map[string]string{
				"myvarstring": string("foostring"),
				"myvar1":      string("foo1string"),
			},
		},
	}

	cpuInt       = 4
	cpuString    = strconv.Itoa(cpuInt)
	memoryInt    = 30000000
	memoryString = strconv.Itoa(memoryInt)
	container    = v1.Container{
		Name: "test",
		Resources: v1.ResourceRequirements{
			Limits: v1.ResourceList{
				v1.ResourceCPU:    resource.MustParse(cpuString),
				v1.ResourceMemory: resource.MustParse(memoryString),
			},
			Requests: v1.ResourceList{
				v1.ResourceCPU:    resource.MustParse(cpuString),
				v1.ResourceMemory: resource.MustParse(memoryString),
			},
		},
	}
)

func TestHttpLivenessProbe(t *testing.T) {
	tests := []struct {
		name          string
		specGenerator specgen.SpecGenerator
		container     v1.Container
		restartPolicy string
		succeed       bool
		expectedURL   string
	}{
		{
			"HttpLivenessProbeUrlSetCorrectly",
			specgen.SpecGenerator{},
			v1.Container{
				LivenessProbe: &v1.Probe{
					Handler: v1.Handler{
						HTTPGet: &v1.HTTPGetAction{
							Scheme: "http",
							Host:   "127.0.0.1",
							Port:   intstr.FromInt(8080),
							Path:   "/health",
						},
					},
				},
			},
			"always",
			true,
			"http://127.0.0.1:8080/health",
		},
		{
			"HttpLivenessProbeUrlUsesDefaults",
			specgen.SpecGenerator{},
			v1.Container{
				LivenessProbe: &v1.Probe{
					Handler: v1.Handler{
						HTTPGet: &v1.HTTPGetAction{
							Port: intstr.FromInt(80),
						},
					},
				},
			},
			"always",
			true,
			"http://localhost:80/",
		},
		{
			"HttpLivenessProbeNamedPort",
			specgen.SpecGenerator{},
			v1.Container{
				LivenessProbe: &v1.Probe{
					Handler: v1.Handler{
						HTTPGet: &v1.HTTPGetAction{
							Port: intstr.FromString("httpPort"),
						},
					},
				},
				Ports: []v1.ContainerPort{
					{Name: "servicePort", ContainerPort: 7000},
					{Name: "httpPort", ContainerPort: 8000},
				},
			},
			"always",
			true,
			"http://localhost:8000/",
		},
	}

	for _, test := range tests {
		test := test
		t.Run(test.name, func(t *testing.T) {
			err := setupLivenessProbe(&test.specGenerator, test.container, test.restartPolicy)
			if err == nil {
				assert.Equal(t, err == nil, test.succeed)
				assert.Contains(t, test.specGenerator.ContainerHealthCheckConfig.HealthConfig.Test, test.expectedURL)
			}
		})
	}
}

func TestTCPLivenessProbe(t *testing.T) {
	tests := []struct {
		name          string
		specGenerator specgen.SpecGenerator
		container     v1.Container
		restartPolicy string
		succeed       bool
		expectedHost  string
		expectedPort  string
	}{
		{
			"TCPLivenessProbeNormal",
			specgen.SpecGenerator{},
			v1.Container{
				LivenessProbe: &v1.Probe{
					Handler: v1.Handler{
						TCPSocket: &v1.TCPSocketAction{
							Host: "127.0.0.1",
							Port: intstr.FromInt(8080),
						},
					},
				},
			},
			"always",
			true,
			"127.0.0.1",
			"8080",
		},
		{
			"TCPLivenessProbeHostUsesDefault",
			specgen.SpecGenerator{},
			v1.Container{
				LivenessProbe: &v1.Probe{
					Handler: v1.Handler{
						TCPSocket: &v1.TCPSocketAction{
							Port: intstr.FromInt(200),
						},
					},
				},
			},
			"always",
			true,
			"localhost",
			"200",
		},
		{
			"TCPLivenessProbeUseNamedPort",
			specgen.SpecGenerator{},
			v1.Container{
				LivenessProbe: &v1.Probe{
					Handler: v1.Handler{
						TCPSocket: &v1.TCPSocketAction{
							Port: intstr.FromString("servicePort"),
							Host: "myservice.domain.com",
						},
					},
				},
				Ports: []v1.ContainerPort{
					{ContainerPort: 6000},
					{Name: "servicePort", ContainerPort: 4000},
					{Name: "2ndServicePort", ContainerPort: 3000},
				},
			},
			"always",
			true,
			"myservice.domain.com",
			"4000",
		},
		{
			"TCPLivenessProbeInvalidPortName",
			specgen.SpecGenerator{},
			v1.Container{
				LivenessProbe: &v1.Probe{
					Handler: v1.Handler{
						TCPSocket: &v1.TCPSocketAction{
							Port: intstr.FromString("3rdservicePort"),
							Host: "myservice.domain.com",
						},
					},
				},
				Ports: []v1.ContainerPort{
					{ContainerPort: 6000},
					{Name: "servicePort", ContainerPort: 4000},
					{Name: "2ndServicePort", ContainerPort: 3000},
				},
			},
			"always",
			false,
			"myservice.domain.com",
			"4000",
		},
		{
			"TCPLivenessProbeNormalWithOnFailureRestartPolicy",
			specgen.SpecGenerator{},
			v1.Container{
				LivenessProbe: &v1.Probe{
					Handler: v1.Handler{
						TCPSocket: &v1.TCPSocketAction{
							Host: "127.0.0.1",
							Port: intstr.FromInt(8080),
						},
					},
				},
			},
			"on-failure",
			true,
			"127.0.0.1",
			"8080",
		},
	}

	for _, test := range tests {
		test := test
		t.Run(test.name, func(t *testing.T) {
			err := setupLivenessProbe(&test.specGenerator, test.container, test.restartPolicy)
			assert.Equal(t, err == nil, test.succeed)
			if err == nil {
				assert.Equal(t, int(test.specGenerator.ContainerHealthCheckConfig.HealthCheckOnFailureAction), define.HealthCheckOnFailureActionRestart)
				assert.Contains(t, test.specGenerator.ContainerHealthCheckConfig.HealthConfig.Test, test.expectedHost)
				assert.Contains(t, test.specGenerator.ContainerHealthCheckConfig.HealthConfig.Test, test.expectedPort)
			}
		})
	}
}
