env: fix resource reference Optional nil pointer (#491)
Signed-off-by: Paulo Pires <pjpires@gmail.com>
This commit is contained in:
committed by
Robbie Zhang
parent
b44d5b9f22
commit
323c02d468
@@ -227,7 +227,7 @@ loop:
|
|||||||
vf := env.ValueFrom.ConfigMapKeyRef
|
vf := env.ValueFrom.ConfigMapKeyRef
|
||||||
// Check whether the key reference is optional.
|
// Check whether the key reference is optional.
|
||||||
// This will control whether we fail when unable to read the requested key.
|
// This will control whether we fail when unable to read the requested key.
|
||||||
optional := vf != nil && *vf.Optional
|
optional := vf != nil && vf.Optional != nil && *vf.Optional
|
||||||
// Try to grab the referenced configmap.
|
// Try to grab the referenced configmap.
|
||||||
m, err := rm.GetConfigMap(vf.Name, pod.Namespace)
|
m, err := rm.GetConfigMap(vf.Name, pod.Namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -279,7 +279,7 @@ loop:
|
|||||||
vf := env.ValueFrom.SecretKeyRef
|
vf := env.ValueFrom.SecretKeyRef
|
||||||
// Check whether the key reference is optional.
|
// Check whether the key reference is optional.
|
||||||
// This will control whether we fail when unable to read the requested key.
|
// This will control whether we fail when unable to read the requested key.
|
||||||
optional := vf != nil && *vf.Optional
|
optional := vf != nil && vf.Optional != nil && *vf.Optional
|
||||||
// Try to grab the referenced secret.
|
// Try to grab the referenced secret.
|
||||||
s, err := rm.GetSecret(vf.Name, pod.Namespace)
|
s, err := rm.GetSecret(vf.Name, pod.Namespace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ import (
|
|||||||
const (
|
const (
|
||||||
// defaultEventRecorderBufferSize is the default buffer size to use when creating fake event recorders.
|
// defaultEventRecorderBufferSize is the default buffer size to use when creating fake event recorders.
|
||||||
defaultEventRecorderBufferSize = 5
|
defaultEventRecorderBufferSize = 5
|
||||||
|
// envVarName1 is a string that can be used as the name of an environment value.
|
||||||
|
envVarName1 = "FOO"
|
||||||
|
// envVarValue1 is a string meant to be used as the value of the "envVarName1" environment value.
|
||||||
|
envVarValue1 = "foo_value"
|
||||||
|
// envVarName1 is a string that can be used as the name of an environment value.
|
||||||
|
envVarName2 = "BAR"
|
||||||
// invalidKey1 is a key that cannot be used as the name of an environment variable (since it starts with a digit).
|
// invalidKey1 is a key that cannot be used as the name of an environment variable (since it starts with a digit).
|
||||||
invalidKey1 = "1INVALID"
|
invalidKey1 = "1INVALID"
|
||||||
// invalidKey2 is a key that cannot be used as the name of an environment variable (since it starts with a digit).
|
// invalidKey2 is a key that cannot be used as the name of an environment variable (since it starts with a digit).
|
||||||
@@ -79,9 +85,159 @@ var (
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestPopulatePodWithInitContainers populates the environment of a pod with four containers (two init containers, two containers).
|
// TestPopulatePodWithInitContainersUsingEnv populates the environment of a pod with four containers (two init containers, two containers) using ".env".
|
||||||
// Then, it checks that the resulting environment for each container contains the expected environment variables.
|
// Then, it checks that the resulting environment for each container contains the expected environment variables.
|
||||||
func TestPopulatePodWithInitContainers(t *testing.T) {
|
func TestPopulatePodWithInitContainersUsingEnv(t *testing.T) {
|
||||||
|
rm := testutil.FakeResourceManager(configMap1, configMap2, secret1, secret2)
|
||||||
|
er := testutil.FakeEventRecorder(defaultEventRecorderBufferSize)
|
||||||
|
|
||||||
|
// Create a pod object having two init containers and two containers.
|
||||||
|
// The containers' environment is to be populated both from directly-provided values and from keys in two configmaps and two secrets.
|
||||||
|
pod := &corev1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: "pod-0",
|
||||||
|
},
|
||||||
|
Spec: corev1.PodSpec{
|
||||||
|
InitContainers: []corev1.Container{
|
||||||
|
{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{
|
||||||
|
Name: envVarName1,
|
||||||
|
Value: envVarValue1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: envVarName2,
|
||||||
|
ValueFrom: &corev1.EnvVarSource{
|
||||||
|
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
|
||||||
|
LocalObjectReference: corev1.LocalObjectReference{
|
||||||
|
Name: configMap1.Name,
|
||||||
|
},
|
||||||
|
Key: keyFoo,
|
||||||
|
// This scenario has been observed before https://github.com/virtual-kubelet/virtual-kubelet/issues/444#issuecomment-449611851.
|
||||||
|
Optional: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{
|
||||||
|
Name: envVarName1,
|
||||||
|
Value: envVarValue1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: envVarName2,
|
||||||
|
ValueFrom: &corev1.EnvVarSource{
|
||||||
|
SecretKeyRef: &corev1.SecretKeySelector{
|
||||||
|
LocalObjectReference: corev1.LocalObjectReference{
|
||||||
|
Name: secret1.Name,
|
||||||
|
},
|
||||||
|
Key: keyBaz,
|
||||||
|
Optional: &bFalse,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Containers: []corev1.Container{
|
||||||
|
{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{
|
||||||
|
Name: envVarName1,
|
||||||
|
Value: envVarValue1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: envVarName2,
|
||||||
|
ValueFrom: &corev1.EnvVarSource{
|
||||||
|
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
|
||||||
|
LocalObjectReference: corev1.LocalObjectReference{
|
||||||
|
Name: configMap1.Name,
|
||||||
|
},
|
||||||
|
Key: keyFoo,
|
||||||
|
// This scenario has been observed before https://github.com/virtual-kubelet/virtual-kubelet/issues/444#issuecomment-449611851.
|
||||||
|
Optional: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{
|
||||||
|
Name: envVarName1,
|
||||||
|
Value: envVarValue1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: envVarName2,
|
||||||
|
ValueFrom: &corev1.EnvVarSource{
|
||||||
|
SecretKeyRef: &corev1.SecretKeySelector{
|
||||||
|
LocalObjectReference: corev1.LocalObjectReference{
|
||||||
|
Name: secret1.Name,
|
||||||
|
},
|
||||||
|
Key: keyBaz,
|
||||||
|
Optional: &bFalse,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the pod's environment.
|
||||||
|
err := populateEnvironmentVariables(context.Background(), pod, rm, er)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Make sure that all the containers' environments contain all the expected keys and values.
|
||||||
|
assert.ElementsMatch(t, pod.Spec.InitContainers[0].Env, []corev1.EnvVar{
|
||||||
|
{
|
||||||
|
Name: envVarName1,
|
||||||
|
Value: envVarValue1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: envVarName2,
|
||||||
|
Value: configMap1.Data[keyFoo],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert.ElementsMatch(t, pod.Spec.InitContainers[1].Env, []corev1.EnvVar{
|
||||||
|
{
|
||||||
|
Name: envVarName1,
|
||||||
|
Value: envVarValue1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: envVarName2,
|
||||||
|
Value: string(secret1.Data[keyBaz]),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert.ElementsMatch(t, pod.Spec.Containers[0].Env, []corev1.EnvVar{
|
||||||
|
{
|
||||||
|
Name: envVarName1,
|
||||||
|
Value: envVarValue1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: envVarName2,
|
||||||
|
Value: configMap1.Data[keyFoo],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert.ElementsMatch(t, pod.Spec.Containers[1].Env, []corev1.EnvVar{
|
||||||
|
{
|
||||||
|
Name: envVarName1,
|
||||||
|
Value: envVarValue1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: envVarName2,
|
||||||
|
Value: string(secret1.Data[keyBaz]),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPopulatePodWithInitContainersUsingEnvFrom populates the environment of a pod with four containers (two init containers, two containers) using ".envFrom".
|
||||||
|
// Then, it checks that the resulting environment for each container contains the expected environment variables.
|
||||||
|
func TestPopulatePodWithInitContainersUsingEnvFrom(t *testing.T) {
|
||||||
rm := testutil.FakeResourceManager(configMap1, configMap2, secret1, secret2)
|
rm := testutil.FakeResourceManager(configMap1, configMap2, secret1, secret2)
|
||||||
er := testutil.FakeEventRecorder(defaultEventRecorderBufferSize)
|
er := testutil.FakeEventRecorder(defaultEventRecorderBufferSize)
|
||||||
|
|
||||||
@@ -490,3 +646,101 @@ func TestEnvFromInexistentSecrets(t *testing.T) {
|
|||||||
assert.Contains(t, event2, ReasonMandatorySecretNotFound)
|
assert.Contains(t, event2, ReasonMandatorySecretNotFound)
|
||||||
assert.Contains(t, event2, missingSecret2Name)
|
assert.Contains(t, event2, missingSecret2Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestEnvReferencingInexistentConfigMapKey tries populates the environment of a container using a keys from a configmaps that does not exist.
|
||||||
|
// Then, it checks that the expected event has been recorded.
|
||||||
|
func TestEnvReferencingInexistentConfigMapKey(t *testing.T) {
|
||||||
|
rm := testutil.FakeResourceManager()
|
||||||
|
er := testutil.FakeEventRecorder(defaultEventRecorderBufferSize)
|
||||||
|
|
||||||
|
missingConfigMapName := "missing-config-map"
|
||||||
|
|
||||||
|
// Create a pod object having a single container and referencing a key from a configmap that does not exist.
|
||||||
|
pod := &corev1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: "pod-0",
|
||||||
|
},
|
||||||
|
Spec: corev1.PodSpec{
|
||||||
|
Containers: []corev1.Container{
|
||||||
|
{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{
|
||||||
|
Name: "envvar",
|
||||||
|
ValueFrom: &corev1.EnvVarSource{
|
||||||
|
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
|
||||||
|
LocalObjectReference: corev1.LocalObjectReference{
|
||||||
|
Name: missingConfigMapName,
|
||||||
|
},
|
||||||
|
Key: "key",
|
||||||
|
// This scenario has been observed before https://github.com/virtual-kubelet/virtual-kubelet/issues/444#issuecomment-449611851.
|
||||||
|
// A nil value of optional means "mandatory", hence we should expect "populateEnvironmentVariables" to return an error.
|
||||||
|
Optional: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the pods's environment.
|
||||||
|
err := populateEnvironmentVariables(context.Background(), pod, rm, er)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
// Make sure that two events have been recorded with the correct reason and message.
|
||||||
|
assert.Len(t, er.Events, 1)
|
||||||
|
event1 := <-er.Events
|
||||||
|
assert.Contains(t, event1, ReasonMandatoryConfigMapNotFound)
|
||||||
|
assert.Contains(t, event1, missingConfigMapName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestEnvReferencingInexistentSecretKey tries populates the environment of a container using a keys from a secret that does not exist.
|
||||||
|
// Then, it checks that the expected event has been recorded.
|
||||||
|
func TestEnvReferencingInexistentSecretKey(t *testing.T) {
|
||||||
|
rm := testutil.FakeResourceManager()
|
||||||
|
er := testutil.FakeEventRecorder(defaultEventRecorderBufferSize)
|
||||||
|
|
||||||
|
missingSecretName := "missing-secret"
|
||||||
|
|
||||||
|
// Create a pod object having a single container and referencing a key from a secret that does not exist.
|
||||||
|
pod := &corev1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: "pod-0",
|
||||||
|
},
|
||||||
|
Spec: corev1.PodSpec{
|
||||||
|
Containers: []corev1.Container{
|
||||||
|
{
|
||||||
|
Env: []corev1.EnvVar{
|
||||||
|
{
|
||||||
|
Name: "envvar",
|
||||||
|
ValueFrom: &corev1.EnvVarSource{
|
||||||
|
SecretKeyRef: &corev1.SecretKeySelector{
|
||||||
|
LocalObjectReference: corev1.LocalObjectReference{
|
||||||
|
Name: missingSecretName,
|
||||||
|
},
|
||||||
|
Key: "key",
|
||||||
|
// This scenario has been observed before https://github.com/virtual-kubelet/virtual-kubelet/issues/444#issuecomment-449611851.
|
||||||
|
// A nil value of optional means "mandatory", hence we should expect "populateEnvironmentVariables" to return an error.
|
||||||
|
Optional: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the pods's environment.
|
||||||
|
err := populateEnvironmentVariables(context.Background(), pod, rm, er)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
// Make sure that two events have been recorded with the correct reason and message.
|
||||||
|
assert.Len(t, er.Events, 1)
|
||||||
|
event1 := <-er.Events
|
||||||
|
assert.Contains(t, event1, ReasonMandatorySecretNotFound)
|
||||||
|
assert.Contains(t, event1, missingSecretName)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user