Secrets can populate environment variables
This commit is contained in:
@@ -418,13 +418,15 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
|
||||
|
||||
var (
|
||||
configMaps = make(map[string]*v1.ConfigMap)
|
||||
secrets = make(map[string]*v1.Secret)
|
||||
tmpEnv = make(map[string]string)
|
||||
)
|
||||
|
||||
// Env will override EnvFrom variables.
|
||||
// Process EnvFrom first then allow Env to replace existing values.
|
||||
for _, envFrom := range container.EnvFrom {
|
||||
if envFrom.ConfigMapRef != nil {
|
||||
switch {
|
||||
case envFrom.ConfigMapRef != nil:
|
||||
name := envFrom.ConfigMapRef.Name
|
||||
configMap, ok := configMaps[name]
|
||||
if !ok {
|
||||
@@ -432,12 +434,12 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
|
||||
return result, fmt.Errorf("Couldn't get configMap %v/%v, no kubeClient defined", pod.Namespace, name)
|
||||
}
|
||||
configMap, err = kl.kubeClient.Core().ConfigMaps(pod.Namespace).Get(name, metav1.GetOptions{})
|
||||
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
configMaps[name] = configMap
|
||||
}
|
||||
|
||||
for k, v := range configMap.Data {
|
||||
if len(envFrom.Prefix) > 0 {
|
||||
k = envFrom.Prefix + k
|
||||
@@ -445,14 +447,31 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
|
||||
if errMsgs := utilvalidation.IsCIdentifier(k); len(errMsgs) != 0 {
|
||||
return result, fmt.Errorf("Invalid environment variable name, %v, from configmap %v/%v: %s", k, pod.Namespace, name, errMsgs[0])
|
||||
}
|
||||
// Accesses apiserver+Pods.
|
||||
// So, the master may set service env vars, or kubelet may. In case both are doing
|
||||
// it, we delete the key from the kubelet-generated ones so we don't have duplicate
|
||||
// env vars.
|
||||
// TODO: remove this next line once all platforms use apiserver+Pods.
|
||||
delete(serviceEnv, k)
|
||||
tmpEnv[k] = v
|
||||
}
|
||||
case envFrom.SecretRef != nil:
|
||||
name := envFrom.SecretRef.Name
|
||||
secret, ok := secrets[name]
|
||||
if !ok {
|
||||
if kl.kubeClient == nil {
|
||||
return result, fmt.Errorf("Couldn't get secret %v/%v, no kubeClient defined", pod.Namespace, name)
|
||||
}
|
||||
secret, err = kl.kubeClient.Core().Secrets(pod.Namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
secrets[name] = secret
|
||||
}
|
||||
|
||||
for k, v := range secret.Data {
|
||||
if len(envFrom.Prefix) > 0 {
|
||||
k = envFrom.Prefix + k
|
||||
}
|
||||
if errMsgs := utilvalidation.IsCIdentifier(k); len(errMsgs) != 0 {
|
||||
return result, fmt.Errorf("Invalid environment variable name, %v, from secret %v/%v: %s", k, pod.Namespace, name, errMsgs[0])
|
||||
}
|
||||
tmpEnv[k] = string(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,17 +485,9 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
|
||||
// 2. Create the container's environment in the order variables are declared
|
||||
// 3. Add remaining service environment vars
|
||||
var (
|
||||
secrets = make(map[string]*v1.Secret)
|
||||
mappingFunc = expansion.MappingFuncFor(tmpEnv, serviceEnv)
|
||||
)
|
||||
for _, envVar := range container.Env {
|
||||
// Accesses apiserver+Pods.
|
||||
// So, the master may set service env vars, or kubelet may. In case both are doing
|
||||
// it, we delete the key from the kubelet-generated ones so we don't have duplicate
|
||||
// env vars.
|
||||
// TODO: remove this next line once all platforms use apiserver+Pods.
|
||||
delete(serviceEnv, envVar.Name)
|
||||
|
||||
runtimeVal := envVar.Value
|
||||
if runtimeVal != "" {
|
||||
// Step 1a: expand variable references
|
||||
@@ -548,7 +559,14 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
|
||||
|
||||
// Append remaining service env vars.
|
||||
for k, v := range serviceEnv {
|
||||
result = append(result, kubecontainer.EnvVar{Name: k, Value: v})
|
||||
// Accesses apiserver+Pods.
|
||||
// So, the master may set service env vars, or kubelet may. In case both are doing
|
||||
// it, we skip the key from the kubelet-generated ones so we don't have duplicate
|
||||
// env vars.
|
||||
// TODO: remove this next line once all platforms use apiserver+Pods.
|
||||
if _, present := tmpEnv[k]; !present {
|
||||
result = append(result, kubecontainer.EnvVar{Name: k, Value: v})
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -274,6 +274,7 @@ func TestMakeEnvironmentVariables(t *testing.T) {
|
||||
masterServiceNs string // the namespace to read master service info from
|
||||
nilLister bool // whether the lister should be nil
|
||||
configMap *v1.ConfigMap // an optional ConfigMap to pull from
|
||||
secret *v1.Secret // an optional Secret to pull from
|
||||
expectedEnvs []kubecontainer.EnvVar // a set of expected environment vars
|
||||
expectedError bool // does the test fail
|
||||
}{
|
||||
@@ -766,6 +767,160 @@ func TestMakeEnvironmentVariables(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "secret",
|
||||
ns: "test1",
|
||||
container: &v1.Container{
|
||||
EnvFrom: []v1.EnvFromSource{
|
||||
{
|
||||
SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test-secret"}},
|
||||
},
|
||||
{
|
||||
Prefix: "p_",
|
||||
SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test-secret"}},
|
||||
},
|
||||
},
|
||||
Env: []v1.EnvVar{
|
||||
{
|
||||
Name: "TEST_LITERAL",
|
||||
Value: "test-test-test",
|
||||
},
|
||||
{
|
||||
Name: "EXPANSION_TEST",
|
||||
Value: "$(REPLACE_ME)",
|
||||
},
|
||||
{
|
||||
Name: "DUPE_TEST",
|
||||
Value: "ENV_VAR",
|
||||
},
|
||||
},
|
||||
},
|
||||
masterServiceNs: "nothing",
|
||||
nilLister: false,
|
||||
secret: &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test1",
|
||||
Name: "test-secret",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"REPLACE_ME": []byte("FROM_SECRET"),
|
||||
"DUPE_TEST": []byte("SECRET"),
|
||||
},
|
||||
},
|
||||
expectedEnvs: []kubecontainer.EnvVar{
|
||||
{
|
||||
Name: "TEST_LITERAL",
|
||||
Value: "test-test-test",
|
||||
},
|
||||
{
|
||||
Name: "TEST_SERVICE_HOST",
|
||||
Value: "1.2.3.3",
|
||||
},
|
||||
{
|
||||
Name: "TEST_SERVICE_PORT",
|
||||
Value: "8083",
|
||||
},
|
||||
{
|
||||
Name: "TEST_PORT",
|
||||
Value: "tcp://1.2.3.3:8083",
|
||||
},
|
||||
{
|
||||
Name: "TEST_PORT_8083_TCP",
|
||||
Value: "tcp://1.2.3.3:8083",
|
||||
},
|
||||
{
|
||||
Name: "TEST_PORT_8083_TCP_PROTO",
|
||||
Value: "tcp",
|
||||
},
|
||||
{
|
||||
Name: "TEST_PORT_8083_TCP_PORT",
|
||||
Value: "8083",
|
||||
},
|
||||
{
|
||||
Name: "TEST_PORT_8083_TCP_ADDR",
|
||||
Value: "1.2.3.3",
|
||||
},
|
||||
{
|
||||
Name: "REPLACE_ME",
|
||||
Value: "FROM_SECRET",
|
||||
},
|
||||
{
|
||||
Name: "EXPANSION_TEST",
|
||||
Value: "FROM_SECRET",
|
||||
},
|
||||
{
|
||||
Name: "DUPE_TEST",
|
||||
Value: "ENV_VAR",
|
||||
},
|
||||
{
|
||||
Name: "p_REPLACE_ME",
|
||||
Value: "FROM_SECRET",
|
||||
},
|
||||
{
|
||||
Name: "p_DUPE_TEST",
|
||||
Value: "SECRET",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "secret_missing",
|
||||
ns: "test1",
|
||||
container: &v1.Container{
|
||||
EnvFrom: []v1.EnvFromSource{
|
||||
{SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test-secret"}}},
|
||||
},
|
||||
},
|
||||
masterServiceNs: "nothing",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "secret_invalid_keys",
|
||||
ns: "test1",
|
||||
container: &v1.Container{
|
||||
EnvFrom: []v1.EnvFromSource{
|
||||
{SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test-secret"}}},
|
||||
},
|
||||
},
|
||||
masterServiceNs: "nothing",
|
||||
secret: &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test1",
|
||||
Name: "test-secret",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"1234": []byte("abc"),
|
||||
},
|
||||
},
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "secret_invalid_keys_valid",
|
||||
ns: "test",
|
||||
container: &v1.Container{
|
||||
EnvFrom: []v1.EnvFromSource{
|
||||
{
|
||||
Prefix: "p_",
|
||||
SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: "test-secret"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
masterServiceNs: "",
|
||||
secret: &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test1",
|
||||
Name: "test-secret",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"1234": []byte("abc"),
|
||||
},
|
||||
},
|
||||
expectedEnvs: []kubecontainer.EnvVar{
|
||||
{
|
||||
Name: "p_1234",
|
||||
Value: "abc",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
@@ -786,6 +941,14 @@ func TestMakeEnvironmentVariables(t *testing.T) {
|
||||
return true, tc.configMap, err
|
||||
})
|
||||
|
||||
testKubelet.fakeKubeClient.AddReactor("get", "secrets", func(action core.Action) (bool, runtime.Object, error) {
|
||||
var err error
|
||||
if tc.secret == nil {
|
||||
err = errors.New("no secret defined")
|
||||
}
|
||||
return true, tc.secret, err
|
||||
})
|
||||
|
||||
testPod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: tc.ns,
|
||||
|
||||
Reference in New Issue
Block a user