client/pkg/kn/flags/podspec_helper_test.go

1076 lines
33 KiB
Go

/*
Copyright 2020 The Knative Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package flags
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"k8s.io/apimachinery/pkg/api/resource"
"knative.dev/client/lib/test"
"gotest.tools/v3/assert"
corev1 "k8s.io/api/core/v1"
"knative.dev/client/pkg/util"
"knative.dev/pkg/ptr"
)
func getPodSpec() (*corev1.PodSpec, *corev1.Container) {
spec := &corev1.PodSpec{
Containers: []corev1.Container{{}},
}
return spec, &spec.Containers[0]
}
func TestUpdateEnvVarsNew(t *testing.T) {
spec, _ := getPodSpec()
expected := []corev1.EnvVar{
{Name: "a", Value: "foo"},
{Name: "b", Value: "bar"},
}
argsEnv := []string{
"a=foo",
"b=bar",
}
envToUpdate, envToRemove, err := util.OrderedMapAndRemovalListFromArray(argsEnv, "=")
assert.NilError(t, err)
args := append([]string{"command"}, argsEnv...)
err = UpdateEnvVars(spec, args, envToUpdate, envToRemove, util.NewOrderedMap(), []string{}, "", util.NewOrderedMap(), []string{})
assert.NilError(t, err)
assert.DeepEqual(t, expected, spec.Containers[0].Env)
}
func TestUpdateEnvVarsMixedEnvOrder(t *testing.T) {
spec, _ := getPodSpec()
expected := []corev1.EnvVar{
{Name: "z", Value: "foo"},
{Name: "a", Value: "bar"},
{Name: "x", Value: "baz"},
}
argsEnv := []string{
"z=foo",
"a=bar",
"x=baz",
}
envToUpdate, envToRemove, err := util.OrderedMapAndRemovalListFromArray(argsEnv, "=")
assert.NilError(t, err)
args := append([]string{"command"}, argsEnv...)
err = UpdateEnvVars(spec, args, envToUpdate, envToRemove, util.NewOrderedMap(), []string{}, "", util.NewOrderedMap(), []string{})
assert.NilError(t, err)
assert.DeepEqual(t, expected, spec.Containers[0].Env)
}
func TestUpdateEnvVarsValueFromNew(t *testing.T) {
spec, _ := getPodSpec()
expected := []corev1.EnvVar{
{Name: "a", ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "foo",
},
Key: "key",
},
}},
{Name: "b", ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "bar",
},
Key: "key2",
},
}},
{Name: "c", ValueFrom: &corev1.EnvVarSource{
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "baz",
},
Key: "key3",
},
}},
{Name: "d", ValueFrom: &corev1.EnvVarSource{
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "goo",
},
Key: "key4",
},
}},
}
argsEnvValueFrom := []string{
"a=secret:foo:key",
"b=sc:bar:key2",
"c=config-map:baz:key3",
"d=cm:goo:key4",
}
args := append([]string{"command"}, argsEnvValueFrom...)
envValueFromToUpdate, envValueFromToRemove, err := util.OrderedMapAndRemovalListFromArray(argsEnvValueFrom, "=")
assert.NilError(t, err)
err = UpdateEnvVars(spec, args, util.NewOrderedMap(), []string{}, envValueFromToUpdate, envValueFromToRemove, "", util.NewOrderedMap(), []string{})
assert.NilError(t, err)
assert.DeepEqual(t, expected, spec.Containers[0].Env)
}
func TestUpdateEnvVarsAllNew(t *testing.T) {
spec, _ := getPodSpec()
expected := []corev1.EnvVar{
{Name: "a", ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "foo",
},
Key: "key",
},
}},
{Name: "b", ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "bar",
},
Key: "key2",
},
}},
{Name: "c", Value: "baz"},
{Name: "d", Value: "goo"},
}
argsEnvValueFrom := []string{
"a=secret:foo:key",
"b=sc:bar:key2",
}
argsEnv := []string{
"c=baz",
"d=goo",
}
args := append([]string{"command"}, append(argsEnvValueFrom, argsEnv...)...)
envToUpdate, envToRemove, err := util.OrderedMapAndRemovalListFromArray(argsEnv, "=")
assert.NilError(t, err)
envValueFromToUpdate, envValueFromToRemove, err := util.OrderedMapAndRemovalListFromArray(argsEnvValueFrom, "=")
assert.NilError(t, err)
err = UpdateEnvVars(spec, args, envToUpdate, envToRemove, envValueFromToUpdate, envValueFromToRemove, "", util.NewOrderedMap(), []string{})
assert.NilError(t, err)
assert.DeepEqual(t, expected, spec.Containers[0].Env)
}
func TestUpdateEnvVarsValueFromValidate(t *testing.T) {
spec, _ := getPodSpec()
envValueFromWrongInput := [][]string{
{"foo=foo"},
{"foo=bar:"},
{"foo=foo:bar"},
{"foo=foo:bar:"},
{"foo=foo:bar:baz"},
{"foo=secret"},
{"foo=sec"},
{"foo=secret:"},
{"foo=sec:"},
{"foo=secret:name"},
{"foo=sec:name"},
{"foo=secret:name"},
{"foo=sec:name:"},
{"foo=config-map"},
{"foo=cm"},
{"foo=config-map:"},
{"foo=cm:"},
{"foo=config-map:name"},
{"foo=cm:name"},
{"foo=config-map:name:"},
{"foo=cm:name:"},
}
for _, input := range envValueFromWrongInput {
args := append([]string{"command"}, input...)
envValueFromToUpdate, envValueFromToRemove, err := util.OrderedMapAndRemovalListFromArray(input, "=")
assert.NilError(t, err)
err = UpdateEnvVars(spec, args, util.NewOrderedMap(), []string{}, envValueFromToUpdate, envValueFromToRemove, "", util.NewOrderedMap(), []string{})
fmt.Println()
msg := fmt.Sprintf("input \"%s\" should fail, as it is not valid entry for containers.env.valueFrom", input[0])
assert.ErrorContains(t, err, " ", msg)
}
}
func TestUpdateEnvFrom(t *testing.T) {
spec, container := getPodSpec()
container.EnvFrom = append(container.EnvFrom,
corev1.EnvFromSource{
ConfigMapRef: &corev1.ConfigMapEnvSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: "config-map-existing-name",
}}},
corev1.EnvFromSource{
SecretRef: &corev1.SecretEnvSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: "secret-existing-name",
}}},
)
UpdateEnvFrom(spec,
[]string{"config-map:config-map-new-name-1", "secret:secret-new-name-1"},
[]string{"config-map:config-map-existing-name", "secret:secret-existing-name"})
assert.Equal(t, len(container.EnvFrom), 2)
assert.Equal(t, container.EnvFrom[0].ConfigMapRef.Name, "config-map-new-name-1")
assert.Equal(t, container.EnvFrom[1].SecretRef.Name, "secret-new-name-1")
}
func TestUpdateVolumeMountsAndVolumes(t *testing.T) {
quantity := resource.MustParse("10Gi")
spec, container := getPodSpec()
spec.Volumes = append(spec.Volumes,
corev1.Volume{
Name: "existing-config-map-volume-name-1",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: "existing-config-map-1",
}}}},
corev1.Volume{
Name: "existing-config-map-volume-name-2",
VolumeSource: corev1.VolumeSource{
ConfigMap: &corev1.ConfigMapVolumeSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: "existing-config-map-2",
}}}},
corev1.Volume{
Name: "existing-secret-volume-name-1",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: "existing-secret-1",
}}},
corev1.Volume{
Name: "existing-secret-volume-name-2",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: "existing-secret-2",
}}},
corev1.Volume{
Name: "new-empty-dir-volume-name-1",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{
Medium: "",
SizeLimit: nil,
},
},
},
corev1.Volume{
Name: "new-empty-dir-volume-name-2",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{
Medium: "Memory",
SizeLimit: &quantity,
},
},
},
corev1.Volume{
Name: "new-empty-dir-volume-name-3",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{
Medium: "Memory",
},
},
},
corev1.Volume{
Name: "new-empty-dir-volume-name-4",
VolumeSource: corev1.VolumeSource{
EmptyDir: &corev1.EmptyDirVolumeSource{
SizeLimit: &quantity,
},
},
})
container.VolumeMounts = append(container.VolumeMounts,
corev1.VolumeMount{
Name: "existing-config-map-volume-name-1",
ReadOnly: true,
MountPath: "/existing-config-map-1/mount/path",
},
corev1.VolumeMount{
Name: "existing-config-map-volume-name-2",
ReadOnly: true,
MountPath: "/existing-config-map-2/mount/path",
},
corev1.VolumeMount{
Name: "existing-secret-volume-name-1",
ReadOnly: true,
MountPath: "/existing-secret-1/mount/path",
},
corev1.VolumeMount{
Name: "existing-secret-volume-name-2",
ReadOnly: true,
MountPath: "/existing-secret-2/mount/path",
},
corev1.VolumeMount{
Name: "new-empty-dir-volume-name-1",
ReadOnly: false,
MountPath: "/empty-dir-1/mount/path",
},
corev1.VolumeMount{
Name: "new-empty-dir-volume-name-2",
ReadOnly: false,
MountPath: "/empty-dir-2/mount/path",
},
corev1.VolumeMount{
Name: "new-empty-dir-volume-name-3",
ReadOnly: false,
MountPath: "/empty-dir-3/mount/path",
},
corev1.VolumeMount{
Name: "new-empty-dir-volume-name-4",
ReadOnly: false,
MountPath: "/empty-dir-4/mount/path",
},
corev1.VolumeMount{
Name: "new-empty-dir-volume-name-5",
ReadOnly: false,
MountPath: "/empty-dir-5/mount/path",
},
)
err := UpdateVolumeMountsAndVolumes(spec,
util.NewOrderedMapWithKVStrings([][]string{{"/new-config-map/mount/path", "new-config-map-volume-name"}}),
[]string{},
util.NewOrderedMapWithKVStrings([][]string{{"new-config-map-volume-name", "config-map:new-config-map"}}),
[]string{})
assert.NilError(t, err)
err = UpdateVolumeMountsAndVolumes(spec,
util.NewOrderedMapWithKVStrings([][]string{{"/updated-config-map/mount/path", "existing-config-map-volume-name-2"}}),
[]string{},
util.NewOrderedMapWithKVStrings([][]string{{"existing-config-map-volume-name-2", "config-map:updated-config-map"}}),
[]string{})
assert.NilError(t, err)
err = UpdateVolumeMountsAndVolumes(spec,
util.NewOrderedMapWithKVStrings([][]string{{"/new-secret/mount/path", "new-secret-volume-name"}}),
[]string{},
util.NewOrderedMapWithKVStrings([][]string{{"new-secret-volume-name", "secret:new-secret"}}),
[]string{})
assert.NilError(t, err)
err = UpdateVolumeMountsAndVolumes(spec,
util.NewOrderedMapWithKVStrings([][]string{{"/updated-secret/mount/path", "existing-secret-volume-name-2"}}),
[]string{"/existing-config-map-1/mount/path",
"/existing-secret-1/mount/path"},
util.NewOrderedMapWithKVStrings([][]string{{"existing-secret-volume-name-2", "secret:updated-secret"}}),
[]string{"existing-config-map-volume-name-1",
"existing-secret-volume-name-1"})
assert.NilError(t, err)
err = UpdateVolumeMountsAndVolumes(spec,
util.NewOrderedMapWithKVStrings([][]string{{"/empty-dir-1/mount/path", "new-empty-dir-volume-name-1"}}),
[]string{},
util.NewOrderedMapWithKVStrings([][]string{{"new-empty-dir-volume-name-1", "emptyDir:new-empty-dir-volume-name-1"}}),
[]string{})
assert.NilError(t, err)
err = UpdateVolumeMountsAndVolumes(spec,
util.NewOrderedMapWithKVStrings([][]string{{"/empty-dir-2/mount/path", "new-empty-dir-volume-name-2"}}),
[]string{},
util.NewOrderedMapWithKVStrings([][]string{{"new-empty-dir-volume-name-2", "emptyDir:new-empty-dir-volume-name-2:type=Memory,size=10Gi"}}),
[]string{})
assert.NilError(t, err)
err = UpdateVolumeMountsAndVolumes(spec,
util.NewOrderedMapWithKVStrings([][]string{{"/empty-dir-3/mount/path", "new-empty-dir-volume-name-3"}}),
[]string{},
util.NewOrderedMapWithKVStrings([][]string{{"new-empty-dir-volume-name-3", "emptyDir:new-empty-dir-volume-name-3:type=Memory"}}),
[]string{})
assert.NilError(t, err)
err = UpdateVolumeMountsAndVolumes(spec,
util.NewOrderedMapWithKVStrings([][]string{{"/empty-dir-4/mount/path", "new-empty-dir-volume-name-4"}}),
[]string{},
util.NewOrderedMapWithKVStrings([][]string{{"new-empty-dir-volume-name-4", "emptyDir:new-empty-dir-volume-name-4:size=10Gi"}}),
[]string{})
assert.NilError(t, err)
err = UpdateVolumeMountsAndVolumes(spec,
util.NewOrderedMapWithKVStrings([][]string{{"/empty-dir-5/mount/path", "emptyDir:new-empty-dir-volume-name-5"}}),
[]string{},
util.NewOrderedMap(),
[]string{})
assert.NilError(t, err)
assert.Equal(t, len(spec.Volumes), 9)
assert.Equal(t, len(container.VolumeMounts), 11)
assert.Equal(t, spec.Volumes[0].Name, "existing-config-map-volume-name-2")
assert.Equal(t, spec.Volumes[0].ConfigMap.Name, "updated-config-map")
assert.Equal(t, spec.Volumes[1].Name, "existing-secret-volume-name-2")
assert.Equal(t, spec.Volumes[1].Secret.SecretName, "updated-secret")
assert.Equal(t, spec.Volumes[2].Name, "new-empty-dir-volume-name-1")
assert.Equal(t, spec.Volumes[2].EmptyDir.Medium, corev1.StorageMediumDefault)
assert.Assert(t, spec.Volumes[2].EmptyDir.SizeLimit == nil)
assert.Equal(t, spec.Volumes[3].Name, "new-empty-dir-volume-name-2")
assert.Equal(t, spec.Volumes[3].EmptyDir.Medium, corev1.StorageMediumMemory)
assert.DeepEqual(t, spec.Volumes[3].EmptyDir.SizeLimit, &quantity)
assert.Equal(t, spec.Volumes[4].Name, "new-empty-dir-volume-name-3")
assert.Equal(t, spec.Volumes[4].EmptyDir.Medium, corev1.StorageMediumMemory)
assert.Equal(t, spec.Volumes[5].Name, "new-empty-dir-volume-name-4")
assert.DeepEqual(t, spec.Volumes[5].EmptyDir.SizeLimit, &quantity)
assert.Equal(t, spec.Volumes[6].Name, "new-config-map-volume-name")
assert.Equal(t, spec.Volumes[6].ConfigMap.Name, "new-config-map")
assert.Equal(t, spec.Volumes[7].Name, "new-secret-volume-name")
assert.Equal(t, spec.Volumes[7].Secret.SecretName, "new-secret")
assert.Assert(t, strings.Contains(spec.Volumes[8].Name, "empty-dir-5"))
assert.Equal(t, spec.Volumes[8].EmptyDir.Medium, corev1.StorageMediumDefault)
assert.Assert(t, spec.Volumes[8].EmptyDir.SizeLimit == nil)
assert.Equal(t, container.VolumeMounts[0].Name, "existing-config-map-volume-name-2")
assert.Equal(t, container.VolumeMounts[0].MountPath, "/existing-config-map-2/mount/path")
assert.Equal(t, container.VolumeMounts[1].Name, "existing-secret-volume-name-2")
assert.Equal(t, container.VolumeMounts[1].MountPath, "/existing-secret-2/mount/path")
assert.Equal(t, container.VolumeMounts[2].Name, "new-empty-dir-volume-name-1")
assert.Equal(t, container.VolumeMounts[2].MountPath, "/empty-dir-1/mount/path")
assert.Equal(t, container.VolumeMounts[3].Name, "new-empty-dir-volume-name-2")
assert.Equal(t, container.VolumeMounts[3].MountPath, "/empty-dir-2/mount/path")
assert.Equal(t, container.VolumeMounts[4].Name, "new-empty-dir-volume-name-3")
assert.Equal(t, container.VolumeMounts[4].MountPath, "/empty-dir-3/mount/path")
assert.Equal(t, container.VolumeMounts[5].Name, "new-empty-dir-volume-name-4")
assert.Equal(t, container.VolumeMounts[5].MountPath, "/empty-dir-4/mount/path")
assert.Equal(t, container.VolumeMounts[6].MountPath, "/empty-dir-5/mount/path")
assert.Equal(t, container.VolumeMounts[7].Name, "new-config-map-volume-name")
assert.Equal(t, container.VolumeMounts[7].MountPath, "/new-config-map/mount/path")
assert.Equal(t, container.VolumeMounts[8].Name, "existing-config-map-volume-name-2")
assert.Equal(t, container.VolumeMounts[8].MountPath, "/updated-config-map/mount/path")
assert.Equal(t, container.VolumeMounts[9].Name, "new-secret-volume-name")
assert.Equal(t, container.VolumeMounts[9].MountPath, "/new-secret/mount/path")
assert.Equal(t, container.VolumeMounts[10].Name, "existing-secret-volume-name-2")
assert.Equal(t, container.VolumeMounts[10].MountPath, "/updated-secret/mount/path")
}
func TestUpdateContainerImage(t *testing.T) {
spec, _ := getPodSpec()
err := UpdateImage(spec, "gcr.io/foo/bar:baz")
assert.NilError(t, err)
// Verify update is successful or not
checkContainerImage(t, spec, "gcr.io/foo/bar:baz")
// Update spec with container image info
spec.Containers[0].Image = "docker.io/foo/bar:baz"
err = UpdateImage(spec, "query.io/foo/bar:baz")
assert.NilError(t, err)
// Verify that given image overrides the existing container image
checkContainerImage(t, spec, "query.io/foo/bar:baz")
}
func checkContainerImage(t *testing.T, spec *corev1.PodSpec, image string) {
if got, want := spec.Containers[0].Image, image; got != want {
t.Errorf("Failed to update the container image: got=%s, want=%s", got, want)
}
}
func TestUpdateContainerCommand(t *testing.T) {
spec, _ := getPodSpec()
err := UpdateContainerCommand(spec, []string{"/app/start"})
assert.NilError(t, err)
assert.DeepEqual(t, spec.Containers[0].Command, []string{"/app/start"})
err = UpdateContainerCommand(spec, []string{"sh", "/app/latest.sh"})
assert.NilError(t, err)
assert.DeepEqual(t, spec.Containers[0].Command, []string{"sh", "/app/latest.sh"})
}
func TestUpdateContainerArg(t *testing.T) {
spec, _ := getPodSpec()
err := UpdateContainerArg(spec, []string{"--myArg"})
assert.NilError(t, err)
assert.DeepEqual(t, spec.Containers[0].Args, []string{"--myArg"})
err = UpdateContainerArg(spec, []string{"myArg1", "--myArg2"})
assert.NilError(t, err)
assert.DeepEqual(t, spec.Containers[0].Args, []string{"myArg1", "--myArg2"})
}
func TestUpdateContainerPort(t *testing.T) {
spec, _ := getPodSpec()
for _, tc := range []struct {
name string
input string
isErr bool
expPort int32
expName string
}{{
name: "only port 8888",
input: "8888",
expPort: int32(8888),
}, {
name: "name and port h2c:8080",
input: "h2c:8080",
expPort: int32(8080),
expName: "h2c",
}, {
name: "error case - not correct format",
input: "h2c:800000000000000000",
isErr: true,
}, {
name: "error case - empty port",
input: "h2c:",
isErr: true,
}, {
name: "error case - wrong format",
input: "8080:h2c",
isErr: true,
}, {
name: "error case - multiple :",
input: "h2c:8080:proto",
isErr: true,
}, {
name: "empty name no error",
input: ":8888",
expPort: int32(8888),
}} {
t.Run(tc.name, func(t *testing.T) {
err := UpdateContainerPort(spec, tc.input)
if tc.isErr {
assert.Error(t, err, fmt.Sprintf(PortFormatErr, tc.input))
} else {
assert.NilError(t, err)
assert.Equal(t, spec.Containers[0].Ports[0].ContainerPort, tc.expPort)
assert.Equal(t, spec.Containers[0].Ports[0].Name, tc.expName)
}
})
}
}
func TestUpdateUser(t *testing.T) {
spec, _ := getPodSpec()
err := UpdateUser(spec, int64(1001))
assert.NilError(t, err)
checkUserUpdate(t, spec, ptr.Int64(int64(1001)))
spec.Containers[0].SecurityContext.RunAsUser = ptr.Int64(int64(1002))
err = UpdateUser(spec, int64(1002))
assert.NilError(t, err)
checkUserUpdate(t, spec, ptr.Int64(int64(1002)))
}
func checkUserUpdate(t *testing.T, spec *corev1.PodSpec, user *int64) {
assert.DeepEqual(t, spec.Containers[0].SecurityContext.RunAsUser, user)
}
func TestUpdateServiceAccountName(t *testing.T) {
spec, _ := getPodSpec()
spec.ServiceAccountName = ""
UpdateServiceAccountName(spec, "foo-bar")
assert.Equal(t, spec.ServiceAccountName, "foo-bar")
UpdateServiceAccountName(spec, "")
assert.Equal(t, spec.ServiceAccountName, "")
}
func TestUpdateImagePullSecrets(t *testing.T) {
spec, _ := getPodSpec()
spec.ImagePullSecrets = nil
UpdateImagePullSecrets(spec, "quay")
assert.Equal(t, spec.ImagePullSecrets[0].Name, "quay")
UpdateImagePullSecrets(spec, " ")
assert.Check(t, spec.ImagePullSecrets == nil)
}
func TestUpdateEnvVarsModify(t *testing.T) {
spec, container := getPodSpec()
container.Env = []corev1.EnvVar{
{Name: "a", Value: "foo"}}
expected := []corev1.EnvVar{
{Name: "a", Value: "bar"},
}
argsEnv := []string{
"a=bar",
}
args := append([]string{"command"}, argsEnv...)
envToUpdate, envToRemove, err := util.OrderedMapAndRemovalListFromArray(argsEnv, "=")
assert.NilError(t, err)
err = UpdateEnvVars(spec, args, envToUpdate, envToRemove, util.NewOrderedMap(), []string{}, "", util.NewOrderedMap(), []string{})
assert.NilError(t, err)
assert.DeepEqual(t, expected, container.Env)
}
func TestUpdateEnvVarsValueFromModify(t *testing.T) {
spec, container := getPodSpec()
container.Env = []corev1.EnvVar{
{Name: "a", ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "foo",
},
Key: "key",
},
}},
}
expected := []corev1.EnvVar{
{Name: "a", ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "bar",
},
Key: "key2",
},
}},
}
argsEnvValueFrom := []string{
"a=secret:bar:key2",
}
args := append([]string{"command"}, argsEnvValueFrom...)
envValueFromToUpdate, envValueFromToRemove, err := util.OrderedMapAndRemovalListFromArray(argsEnvValueFrom, "=")
assert.NilError(t, err)
err = UpdateEnvVars(spec, args, util.NewOrderedMap(), []string{}, envValueFromToUpdate, envValueFromToRemove, "", util.NewOrderedMap(), []string{})
assert.NilError(t, err)
assert.DeepEqual(t, expected, container.Env)
}
func TestUpdateEnvVarsAllModify(t *testing.T) {
spec, container := getPodSpec()
container.Env = []corev1.EnvVar{
{Name: "a", ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "foo",
},
Key: "key",
},
}},
{Name: "b", Value: "bar"},
}
expected := []corev1.EnvVar{
{Name: "a", ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "bar",
},
Key: "key2",
},
}},
{Name: "b", Value: "goo"},
}
argsEnvValueFrom := []string{
"a=secret:bar:key2",
}
argsEnv := []string{
"b=goo",
}
args := append([]string{"command"}, append(argsEnvValueFrom, argsEnv...)...)
envToUpdate, envToRemove, err := util.OrderedMapAndRemovalListFromArray(argsEnv, "=")
assert.NilError(t, err)
envValueFromToUpdate, envValueFromToRemove, err := util.OrderedMapAndRemovalListFromArray(argsEnvValueFrom, "=")
assert.NilError(t, err)
err = UpdateEnvVars(spec, args, envToUpdate, envToRemove, envValueFromToUpdate, envValueFromToRemove, "", util.NewOrderedMap(), []string{})
assert.NilError(t, err)
assert.DeepEqual(t, expected, container.Env)
}
func TestUpdateEnvVarsRemove(t *testing.T) {
spec, container := getPodSpec()
container.Env = []corev1.EnvVar{
{Name: "a", Value: "foo"},
{Name: "b", Value: "bar"},
}
remove := []string{"b-"}
args := append([]string{"command"}, remove...)
envToUpdate, envToRemove, err := util.OrderedMapAndRemovalListFromArray(remove, "=")
assert.NilError(t, err)
err = UpdateEnvVars(spec, args, envToUpdate, envToRemove, util.NewOrderedMap(), []string{}, "", util.NewOrderedMap(), []string{})
assert.NilError(t, err)
expected := []corev1.EnvVar{
{Name: "a", Value: "foo"},
}
assert.DeepEqual(t, expected, container.Env)
}
func TestUpdateEnvVarsValueFromRemove(t *testing.T) {
spec, container := getPodSpec()
container.Env = []corev1.EnvVar{
{Name: "a", ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "foo",
},
Key: "key",
},
}},
{Name: "b", ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "bar",
},
Key: "key2",
},
}},
}
remove := []string{"b-"}
args := append([]string{"command"}, remove...)
envValueFromToUpdate, envValueFromToRemove, err := util.OrderedMapAndRemovalListFromArray(remove, "=")
assert.NilError(t, err)
err = UpdateEnvVars(spec, args, util.NewOrderedMap(), []string{}, envValueFromToUpdate, envValueFromToRemove, "", util.NewOrderedMap(), []string{})
assert.NilError(t, err)
expected := []corev1.EnvVar{
{Name: "a", ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "foo",
},
Key: "key",
},
}},
}
assert.DeepEqual(t, expected, container.Env)
}
func TestUpdateEnvVarsAllRemove(t *testing.T) {
spec, container := getPodSpec()
container.Env = []corev1.EnvVar{
{Name: "a", ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "foo",
},
Key: "key",
},
}},
{Name: "b", ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "bar",
},
Key: "key2",
},
}},
{Name: "c", Value: "baz"},
{Name: "d", Value: "goo"},
}
argsEnvValueFrom := []string{
"a=secret:foo:key",
"b-",
}
argsEnv := []string{
"c=baz",
"d-",
}
args := append([]string{"command"}, append(argsEnvValueFrom, argsEnv...)...)
envToUpdate, envToRemove, err := util.OrderedMapAndRemovalListFromArray(argsEnv, "=")
assert.NilError(t, err)
envValueFromToUpdate, envValueFromToRemove, err := util.OrderedMapAndRemovalListFromArray(argsEnvValueFrom, "=")
assert.NilError(t, err)
err = UpdateEnvVars(spec, args, envToUpdate, envToRemove, envValueFromToUpdate, envValueFromToRemove, "", util.NewOrderedMap(), []string{})
assert.NilError(t, err)
expected := []corev1.EnvVar{
{Name: "a", ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "foo",
},
Key: "key",
},
}},
{Name: "c", Value: "baz"},
}
assert.DeepEqual(t, expected, container.Env)
}
func Test_isValidEnvArg(t *testing.T) {
for _, tc := range []struct {
name string
arg string
envKey string
envValue string
isValid bool
}{{
name: "valid env arg specified",
arg: "FOO=bar",
envKey: "FOO",
envValue: "bar",
isValid: true,
}, {
name: "invalid env arg specified",
arg: "FOObar",
envKey: "FOO",
envValue: "bar",
isValid: false,
}, {
name: "valid env arg specified: -e",
arg: "-e=FOO=bar",
envKey: "FOO",
envValue: "bar",
isValid: true,
}, {
name: "invalid env arg specified: -e",
arg: "-e=FOObar",
envKey: "FOO",
envValue: "bar",
isValid: false,
}, {
name: "valid env arg specified: --env",
arg: "--env=FOO=bar",
envKey: "FOO",
envValue: "bar",
isValid: true,
}, {
name: "invalid env arg specified: --env",
arg: "--env=FOObar",
envKey: "FOO",
envValue: "bar",
isValid: false,
},
} {
t.Run(tc.name, func(t *testing.T) {
result := isValidEnvArg(tc.arg, tc.envKey, tc.envValue)
assert.Equal(t, result, tc.isValid)
})
}
}
func Test_isValidEnvValueFromArg(t *testing.T) {
for _, tc := range []struct {
name string
arg string
envValueFromKey string
envValueFromValue string
isValid bool
}{{
name: "valid env value from arg specified",
arg: "FOO=secret:sercretName:key",
envValueFromKey: "FOO",
envValueFromValue: "secret:sercretName:key",
isValid: true,
}, {
name: "invalid env value from arg specified",
arg: "FOOsecret:sercretName:key",
envValueFromKey: "FOO",
envValueFromValue: "secret:sercretName:key",
isValid: false,
}, {
name: "valid env value from arg specified: --env-value-from",
arg: "--env-value-from=FOO=secret:sercretName:key",
envValueFromKey: "FOO",
envValueFromValue: "secret:sercretName:key",
isValid: true,
}, {
name: "invalid env value from arg specified: --env-value-from",
arg: "--env-value-from=FOOsecret:sercretName:key",
envValueFromKey: "FOO",
envValueFromValue: "secret:sercretName:key",
isValid: false,
},
} {
t.Run(tc.name, func(t *testing.T) {
result := isValidEnvValueFromArg(tc.arg, tc.envValueFromKey, tc.envValueFromValue)
assert.Equal(t, result, tc.isValid)
})
}
}
func TestUpdateContainers(t *testing.T) {
podSpec, _ := getPodSpec()
containers := []corev1.Container{
{
Name: "foo",
Image: "foo:bar",
},
{
Name: "bar",
Image: "foo:bar",
},
}
assert.Equal(t, len(podSpec.Containers), 1)
UpdateContainers(podSpec, containers)
assert.Equal(t, len(podSpec.Containers), 3)
updatedContainer := corev1.Container{Name: "bar", Image: "bar:bar"}
UpdateContainers(podSpec, []corev1.Container{updatedContainer})
assert.Equal(t, len(podSpec.Containers), 3)
for _, container := range podSpec.Containers {
if container.Name == updatedContainer.Name {
assert.DeepEqual(t, container, updatedContainer)
}
}
// Verify that containers aren't multiplied
UpdateContainers(podSpec, containers)
assert.Equal(t, len(podSpec.Containers), 3)
podSpec, _ = getPodSpec()
assert.Equal(t, len(podSpec.Containers), 1)
UpdateContainers(podSpec, []corev1.Container{})
assert.Equal(t, len(podSpec.Containers), 1)
}
func TestUpdateContainerWithName(t *testing.T) {
for _, tc := range []struct {
name string
updateArg []corev1.Container
expectedContainers []corev1.Container
}{{
"One Container Image",
[]corev1.Container{
{Name: "bar", Image: "bar:bar"},
},
[]corev1.Container{
{},
{Name: "foo", Image: "foo:bar"},
{Name: "bar", Image: "bar:bar"},
}},
{
"One Container Env Var",
[]corev1.Container{
{Name: "bar", Image: "foo:bar", Env: []corev1.EnvVar{{Name: "A", Value: "B"}}},
},
[]corev1.Container{
{},
{Name: "foo", Image: "foo:bar"},
{Name: "bar", Image: "foo:bar", Env: []corev1.EnvVar{{Name: "A", Value: "B"}}},
}},
{
"New container",
[]corev1.Container{
{Name: "new", Image: "foo:new"},
},
[]corev1.Container{
{},
{Name: "foo", Image: "foo:bar"},
{Name: "bar", Image: "foo:bar"},
{Name: "new", Image: "foo:new"},
}},
} {
t.Run(tc.name, func(t *testing.T) {
initialPodSpec, _ := getPodSpec()
initialContainers := []corev1.Container{
{Name: "foo", Image: "foo:bar"},
{Name: "bar", Image: "foo:bar"},
}
initialPodSpec.Containers = append(initialPodSpec.Containers, initialContainers...)
UpdateContainers(initialPodSpec, tc.updateArg)
assert.DeepEqual(t, initialPodSpec.Containers, tc.expectedContainers)
})
}
}
func TestParseContainers(t *testing.T) {
rawInput := `
containers:
- image: first
name: foo
resources: {}
- image: second
name: bar
resources: {}`
stdinReader, stdinWriter, err := os.Pipe()
assert.NilError(t, err)
_, err = stdinWriter.Write([]byte(rawInput))
assert.NilError(t, err)
stdinWriter.Close()
origStdin := os.Stdin
defer func() { os.Stdin = origStdin }()
os.Stdin = stdinReader
fromFile, err := decodeContainersFromFile("-")
assert.NilError(t, err)
assert.Equal(t, len(fromFile.Containers), 2)
tempDir := t.TempDir()
fileName := filepath.Join(tempDir, "container.yaml")
ioutil.WriteFile(fileName, []byte(rawInput), test.FileModeReadWrite)
fromFile, err = decodeContainersFromFile(fileName)
assert.NilError(t, err)
assert.Equal(t, len(fromFile.Containers), 2)
_, err = decodeContainersFromFile("non-existing")
assert.Assert(t, err != nil)
assert.Assert(t, util.ContainsAll(err.Error(), "no", "file", "directory"))
}
func TestUpdateImagePullPolicy(t *testing.T) {
policyMap := make(map[string][]string)
policyMap["Always"] = []string{"always", "ALWAYS", "Always"}
policyMap["Never"] = []string{"never", "NEVER", "Never"}
policyMap["IfNotPresent"] = []string{"ifnotpresent", "IFNOTPRESENT", "IfNotPresent"}
for k, values := range policyMap {
expectedPodSpec := &corev1.PodSpec{
Containers: []corev1.Container{
{
Image: "repo/user/imageID:tag",
ImagePullPolicy: corev1.PullPolicy(k),
Command: []string{"/app/start"},
Args: []string{"myArg1"},
Ports: []corev1.ContainerPort{
{
ContainerPort: 8080,
},
},
},
},
}
for _, v := range values {
podSpec := &corev1.PodSpec{
Containers: []corev1.Container{
{
Image: "repo/user/imageID:tag",
Command: []string{"/app/start"},
Args: []string{"myArg1"},
Ports: []corev1.ContainerPort{
{
ContainerPort: 8080,
},
},
},
},
}
err := UpdateImagePullPolicy(podSpec, v)
assert.NilError(t, err, "update pull policy failed")
assert.DeepEqual(t, expectedPodSpec, podSpec)
}
}
}
func TestUpdateImagePullPolicyError(t *testing.T) {
podSpec := &corev1.PodSpec{
Containers: []corev1.Container{
{
Image: "repo/user/imageID:tag",
Command: []string{"/app/start"},
Args: []string{"myArg1"},
Ports: []corev1.ContainerPort{
{
ContainerPort: 8080,
},
},
},
},
}
err := UpdateImagePullPolicy(podSpec, "InvalidPolicy")
assert.Assert(t, util.ContainsAll(err.Error(), "invalid --pull-policy", "Valid arguments", "Always | Never | IfNotPresent"))
}