mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-30 15:05:27 +00:00
Add support for ConfigMap keys in env vars
This commit is contained in:
parent
c059dfdb9b
commit
6cfd101251
@ -715,9 +715,12 @@ type EnvVar struct {
|
||||
}
|
||||
|
||||
// EnvVarSource represents a source for the value of an EnvVar.
|
||||
// Only one of its fields may be set.
|
||||
type EnvVarSource struct {
|
||||
// Required: Selects a field of the pod; only name and namespace are supported.
|
||||
FieldRef *ObjectFieldSelector `json:"fieldRef"`
|
||||
// Selects a field of the pod; only name and namespace are supported.
|
||||
FieldRef *ObjectFieldSelector `json:"fieldRef,omitempty"`
|
||||
// Selects a key of a ConfigMap.
|
||||
ConfigMapKeyRef *ConfigMapKeySelector `json:"configMapKeyRef,omitempty"`
|
||||
}
|
||||
|
||||
// ObjectFieldSelector selects an APIVersioned field of an object.
|
||||
@ -730,6 +733,14 @@ type ObjectFieldSelector struct {
|
||||
FieldPath string `json:"fieldPath"`
|
||||
}
|
||||
|
||||
// Selects a key from a ConfigMap.
|
||||
type ConfigMapKeySelector struct {
|
||||
// The ConfigMap to select from.
|
||||
LocalObjectReference `json:",inline"`
|
||||
// The key to select.
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
// HTTPGetAction describes an action based on HTTP Get requests.
|
||||
type HTTPGetAction struct {
|
||||
// Optional: Path to access on the HTTP server.
|
||||
|
@ -843,8 +843,10 @@ type EnvVar struct {
|
||||
|
||||
// EnvVarSource represents a source for the value of an EnvVar.
|
||||
type EnvVarSource struct {
|
||||
// Selects a field of the pod. Only name and namespace are supported.
|
||||
FieldRef *ObjectFieldSelector `json:"fieldRef"`
|
||||
// Selects a field of the pod; only name and namespace are supported.
|
||||
FieldRef *ObjectFieldSelector `json:"fieldRef,omitempty"`
|
||||
// Selects a key of a ConfigMap.
|
||||
ConfigMapKeyRef *ConfigMapKeySelector `json:"configMapKeyRef,omitempty"`
|
||||
}
|
||||
|
||||
// ObjectFieldSelector selects an APIVersioned field of an object.
|
||||
@ -855,6 +857,14 @@ type ObjectFieldSelector struct {
|
||||
FieldPath string `json:"fieldPath"`
|
||||
}
|
||||
|
||||
// Selects a key from a ConfigMap.
|
||||
type ConfigMapKeySelector struct {
|
||||
// The ConfigMap to select from.
|
||||
LocalObjectReference `json:",inline"`
|
||||
// The key to select.
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
// HTTPGetAction describes an action based on HTTP Get requests.
|
||||
type HTTPGetAction struct {
|
||||
// Path to access on the HTTP server.
|
||||
|
@ -968,6 +968,9 @@ func validateEnvVarValueFrom(ev api.EnvVar, fldPath *field.Path) field.ErrorList
|
||||
case ev.ValueFrom.FieldRef != nil:
|
||||
numSources++
|
||||
allErrs = append(allErrs, validateObjectFieldSelector(ev.ValueFrom.FieldRef, &validFieldPathExpressionsEnv, fldPath.Child("fieldRef"))...)
|
||||
case ev.ValueFrom.ConfigMapKeyRef != nil:
|
||||
numSources++
|
||||
allErrs = append(allErrs, validateConfigMapKeySelector(ev.ValueFrom.ConfigMapKeyRef, fldPath.Child("configMapKeyRef"))...)
|
||||
}
|
||||
|
||||
if len(ev.Value) != 0 && numSources != 0 {
|
||||
@ -996,6 +999,21 @@ func validateObjectFieldSelector(fs *api.ObjectFieldSelector, expressions *sets.
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateConfigMapKeySelector(s *api.ConfigMapKeySelector, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if len(s.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
|
||||
}
|
||||
if len(s.Key) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("key"), ""))
|
||||
} else if !IsSecretKey(s.Key) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("key"), s.Key, fmt.Sprintf("must have at most %d characters and match regex %s", validation.DNS1123SubdomainMaxLength, SecretKeyFmt)))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateVolumeMounts(mounts []api.VolumeMount, volumes sets.String, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
|
@ -40,6 +40,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/api/validation"
|
||||
apiextensions "k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/client/cache"
|
||||
"k8s.io/kubernetes/pkg/client/record"
|
||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
@ -1391,9 +1392,11 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *api.Pod, container *api.Contain
|
||||
// b. If a source is defined for an environment variable, resolve the source
|
||||
// 2. Create the container's environment in the order variables are declared
|
||||
// 3. Add remaining service environment vars
|
||||
|
||||
tmpEnv := make(map[string]string)
|
||||
mappingFunc := expansion.MappingFuncFor(tmpEnv, serviceEnv)
|
||||
var (
|
||||
tmpEnv = make(map[string]string)
|
||||
configMaps = make(map[string]*apiextensions.ConfigMap)
|
||||
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
|
||||
@ -1406,11 +1409,28 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *api.Pod, container *api.Contain
|
||||
if runtimeVal != "" {
|
||||
// Step 1a: expand variable references
|
||||
runtimeVal = expansion.Expand(runtimeVal, mappingFunc)
|
||||
} else if envVar.ValueFrom != nil && envVar.ValueFrom.FieldRef != nil {
|
||||
} else if envVar.ValueFrom != nil {
|
||||
// Step 1b: resolve alternate env var sources
|
||||
runtimeVal, err = kl.podFieldSelectorRuntimeValue(envVar.ValueFrom.FieldRef, pod)
|
||||
if err != nil {
|
||||
return result, err
|
||||
switch {
|
||||
case envVar.ValueFrom.FieldRef != nil:
|
||||
runtimeVal, err = kl.podFieldSelectorRuntimeValue(envVar.ValueFrom.FieldRef, pod)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
case envVar.ValueFrom.ConfigMapKeyRef != nil:
|
||||
name := envVar.ValueFrom.ConfigMapKeyRef.Name
|
||||
key := envVar.ValueFrom.ConfigMapKeyRef.Key
|
||||
configMap, ok := configMaps[name]
|
||||
if !ok {
|
||||
configMap, err = kl.kubeClient.Extensions().ConfigMaps(pod.Namespace).Get(name)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
}
|
||||
runtimeVal, ok = configMap.Data[key]
|
||||
if !ok {
|
||||
return result, fmt.Errorf("Couldn't find key %v in ConfigMap %v/%v", key, pod.Namespace, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
91
test/e2e/configmap.go
Normal file
91
test/e2e/configmap.go
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 e2e
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
var _ = Describe("ConfigMap", func() {
|
||||
f := NewFramework("configmap")
|
||||
|
||||
It("should be consumable via environment variable [Conformance]", func() {
|
||||
name := "configmap-test-" + string(util.NewUUID())
|
||||
configMap := &extensions.ConfigMap{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Namespace: f.Namespace.Name,
|
||||
Name: name,
|
||||
},
|
||||
Data: map[string]string{
|
||||
"data-1": "value-1",
|
||||
"data-2": "value-2",
|
||||
"data-3": "value-3",
|
||||
},
|
||||
}
|
||||
|
||||
By(fmt.Sprintf("Creating configMap %v/%v", f.Namespace.Name, configMap.Name))
|
||||
defer func() {
|
||||
By("Cleaning up the configMap")
|
||||
if err := f.Client.Extensions().ConfigMaps(f.Namespace.Name).Delete(configMap.Name); err != nil {
|
||||
Failf("unable to delete configMap %v: %v", configMap.Name, err)
|
||||
}
|
||||
}()
|
||||
var err error
|
||||
if configMap, err = f.Client.Extensions().ConfigMaps(f.Namespace.Name).Create(configMap); err != nil {
|
||||
Failf("unable to create test configMap %s: %v", configMap.Name, err)
|
||||
}
|
||||
|
||||
pod := &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "pod-configmaps-" + string(util.NewUUID()),
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: "env-test",
|
||||
Image: "gcr.io/google_containers/busybox",
|
||||
Command: []string{"sh", "-c", "env"},
|
||||
Env: []api.EnvVar{
|
||||
{
|
||||
Name: "CONFIG_DATA_1",
|
||||
ValueFrom: &api.EnvVarSource{
|
||||
ConfigMapKeyRef: &api.ConfigMapKeySelector{
|
||||
LocalObjectReference: api.LocalObjectReference{
|
||||
Name: name,
|
||||
},
|
||||
Key: "data-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RestartPolicy: api.RestartPolicyNever,
|
||||
},
|
||||
}
|
||||
|
||||
testContainerOutput("consume configMaps", f.Client, pod, 0, []string{
|
||||
"CONFIG_DATA_1=value-1",
|
||||
}, f.Namespace.Name)
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue
Block a user