mirror of https://github.com/containers/podman.git
1468 lines
29 KiB
Go
1468 lines
29 KiB
Go
//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"
|
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
|
"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 {
|
|
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 {
|
|
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.NoError(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 {
|
|
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 {
|
|
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 {
|
|
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)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDeviceResource(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
specGenerator specgen.SpecGenerator
|
|
container v1.Container
|
|
succeed bool
|
|
devices []spec.LinuxDevice
|
|
}{
|
|
{
|
|
"ParseQualifiedCDI",
|
|
specgen.SpecGenerator{},
|
|
v1.Container{
|
|
Resources: v1.ResourceRequirements{
|
|
Limits: v1.ResourceList{
|
|
"nvidia.com/gpu=0": resource.MustParse("1"),
|
|
},
|
|
},
|
|
},
|
|
true,
|
|
[]spec.LinuxDevice{
|
|
{Path: "nvidia.com/gpu=0"},
|
|
},
|
|
},
|
|
{
|
|
"ParsePodmanDeviceResource",
|
|
specgen.SpecGenerator{},
|
|
v1.Container{
|
|
Resources: v1.ResourceRequirements{
|
|
Requests: v1.ResourceList{
|
|
"podman.io/device=/dev/kmsg": resource.MustParse("1"),
|
|
},
|
|
},
|
|
},
|
|
true,
|
|
[]spec.LinuxDevice{
|
|
{Path: "/dev/kmsg"},
|
|
},
|
|
},
|
|
{
|
|
"InvalidCDI",
|
|
specgen.SpecGenerator{},
|
|
v1.Container{
|
|
Resources: v1.ResourceRequirements{
|
|
Limits: v1.ResourceList{
|
|
"foobar.net/class=///": resource.MustParse("1"),
|
|
},
|
|
},
|
|
},
|
|
false,
|
|
[]spec.LinuxDevice{},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
err := setupContainerDevices(&test.specGenerator, test.container)
|
|
assert.Equal(t, err == nil, test.succeed)
|
|
if err == nil {
|
|
assert.Equal(t, test.specGenerator.Devices, test.devices)
|
|
}
|
|
})
|
|
}
|
|
}
|