Secrets can populate environment variables
This commit is contained in:
parent
6e268e6f83
commit
13d693d220
@ -386,11 +386,16 @@ func FuzzerFor(t *testing.T, version schema.GroupVersion, src rand.Source) *fuzz
|
|||||||
}
|
}
|
||||||
if c.RandBool() {
|
if c.RandBool() {
|
||||||
c.Fuzz(&ev.ConfigMapRef)
|
c.Fuzz(&ev.ConfigMapRef)
|
||||||
|
} else {
|
||||||
|
c.Fuzz(&ev.SecretRef)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
func(cm *api.ConfigMapEnvSource, c fuzz.Continue) {
|
func(cm *api.ConfigMapEnvSource, c fuzz.Continue) {
|
||||||
c.FuzzNoCustom(cm) // fuzz self without calling this function again
|
c.FuzzNoCustom(cm) // fuzz self without calling this function again
|
||||||
},
|
},
|
||||||
|
func(s *api.SecretEnvSource, c fuzz.Continue) {
|
||||||
|
c.FuzzNoCustom(s) // fuzz self without calling this function again
|
||||||
|
},
|
||||||
func(sc *api.SecurityContext, c fuzz.Continue) {
|
func(sc *api.SecurityContext, c fuzz.Continue) {
|
||||||
c.FuzzNoCustom(sc) // fuzz self without calling this function again
|
c.FuzzNoCustom(sc) // fuzz self without calling this function again
|
||||||
if c.RandBool() {
|
if c.RandBool() {
|
||||||
|
@ -1142,6 +1142,9 @@ type EnvFromSource struct {
|
|||||||
// The ConfigMap to select from.
|
// The ConfigMap to select from.
|
||||||
//+optional
|
//+optional
|
||||||
ConfigMapRef *ConfigMapEnvSource
|
ConfigMapRef *ConfigMapEnvSource
|
||||||
|
// The Secret to select from.
|
||||||
|
//+optional
|
||||||
|
SecretRef *SecretEnvSource
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigMapEnvSource selects a ConfigMap to populate the environment
|
// ConfigMapEnvSource selects a ConfigMap to populate the environment
|
||||||
@ -1154,6 +1157,16 @@ type ConfigMapEnvSource struct {
|
|||||||
LocalObjectReference
|
LocalObjectReference
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SecretEnvSource selects a Secret to populate the environment
|
||||||
|
// variables with.
|
||||||
|
//
|
||||||
|
// The contents of the target Secret's Data field will represent the
|
||||||
|
// key-value pairs as environment variables.
|
||||||
|
type SecretEnvSource struct {
|
||||||
|
// The Secret to select from.
|
||||||
|
LocalObjectReference
|
||||||
|
}
|
||||||
|
|
||||||
// HTTPHeader describes a custom header to be used in HTTP probes
|
// HTTPHeader describes a custom header to be used in HTTP probes
|
||||||
type HTTPHeader struct {
|
type HTTPHeader struct {
|
||||||
// The header field name
|
// The header field name
|
||||||
|
@ -1243,6 +1243,9 @@ type EnvFromSource struct {
|
|||||||
// The ConfigMap to select from
|
// The ConfigMap to select from
|
||||||
// +optional
|
// +optional
|
||||||
ConfigMapRef *ConfigMapEnvSource `json:"configMapRef,omitempty" protobuf:"bytes,2,opt,name=configMapRef"`
|
ConfigMapRef *ConfigMapEnvSource `json:"configMapRef,omitempty" protobuf:"bytes,2,opt,name=configMapRef"`
|
||||||
|
// The Secret to select from
|
||||||
|
// +optional
|
||||||
|
SecretRef *SecretEnvSource `json:"secretRef,omitempty" protobuf:"bytes,3,opt,name=secretRef"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigMapEnvSource selects a ConfigMap to populate the environment
|
// ConfigMapEnvSource selects a ConfigMap to populate the environment
|
||||||
@ -1255,6 +1258,16 @@ type ConfigMapEnvSource struct {
|
|||||||
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
|
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SecretEnvSource selects a Secret to populate the environment
|
||||||
|
// variables with.
|
||||||
|
//
|
||||||
|
// The contents of the target Secret's Data field will represent the
|
||||||
|
// key-value pairs as environment variables.
|
||||||
|
type SecretEnvSource struct {
|
||||||
|
// The Secret to select from.
|
||||||
|
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
|
||||||
|
}
|
||||||
|
|
||||||
// HTTPHeader describes a custom header to be used in HTTP probes
|
// HTTPHeader describes a custom header to be used in HTTP probes
|
||||||
type HTTPHeader struct {
|
type HTTPHeader struct {
|
||||||
// The header field name
|
// The header field name
|
||||||
|
@ -1260,9 +1260,22 @@ func validateEnvFrom(vars []api.EnvFromSource, fldPath *field.Path) field.ErrorL
|
|||||||
allErrs = append(allErrs, field.Invalid(idxPath.Child("prefix"), ev.Prefix, msg))
|
allErrs = append(allErrs, field.Invalid(idxPath.Child("prefix"), ev.Prefix, msg))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
numSources := 0
|
||||||
if ev.ConfigMapRef != nil {
|
if ev.ConfigMapRef != nil {
|
||||||
|
numSources++
|
||||||
allErrs = append(allErrs, validateConfigMapEnvSource(ev.ConfigMapRef, idxPath.Child("configMapRef"))...)
|
allErrs = append(allErrs, validateConfigMapEnvSource(ev.ConfigMapRef, idxPath.Child("configMapRef"))...)
|
||||||
}
|
}
|
||||||
|
if ev.SecretRef != nil {
|
||||||
|
numSources++
|
||||||
|
allErrs = append(allErrs, validateSecretEnvSource(ev.SecretRef, idxPath.Child("secretRef"))...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if numSources == 0 {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, "", "must specify one of: `configMapRef` or `secretRef`"))
|
||||||
|
} else if numSources > 1 {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, "", "may not have more than one field specified at a time"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
@ -1275,6 +1288,14 @@ func validateConfigMapEnvSource(configMapSource *api.ConfigMapEnvSource, fldPath
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateSecretEnvSource(secretSource *api.SecretEnvSource, fldPath *field.Path) field.ErrorList {
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
if len(secretSource.Name) == 0 {
|
||||||
|
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
|
||||||
|
}
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
var validContainerResourceDivisorForCPU = sets.NewString("1m", "1")
|
var validContainerResourceDivisorForCPU = sets.NewString("1m", "1")
|
||||||
var validContainerResourceDivisorForMemory = sets.NewString("1", "1k", "1M", "1G", "1T", "1P", "1E", "1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei")
|
var validContainerResourceDivisorForMemory = sets.NewString("1", "1k", "1M", "1G", "1T", "1P", "1E", "1Ki", "1Mi", "1Gi", "1Ti", "1Pi", "1Ei")
|
||||||
|
|
||||||
|
@ -2285,6 +2285,17 @@ func TestValidateEnvFrom(t *testing.T) {
|
|||||||
LocalObjectReference: api.LocalObjectReference{Name: "abc"},
|
LocalObjectReference: api.LocalObjectReference{Name: "abc"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
SecretRef: &api.SecretEnvSource{
|
||||||
|
LocalObjectReference: api.LocalObjectReference{Name: "abc"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Prefix: "pre_",
|
||||||
|
SecretRef: &api.SecretEnvSource{
|
||||||
|
LocalObjectReference: api.LocalObjectReference{Name: "abc"},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
if errs := validateEnvFrom(successCase, field.NewPath("field")); len(errs) != 0 {
|
if errs := validateEnvFrom(successCase, field.NewPath("field")); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
@ -2316,6 +2327,46 @@ func TestValidateEnvFrom(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expectedError: `field[0].prefix: Invalid value: "a.b": ` + idErrMsg,
|
expectedError: `field[0].prefix: Invalid value: "a.b": ` + idErrMsg,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "zero-length name",
|
||||||
|
envs: []api.EnvFromSource{
|
||||||
|
{
|
||||||
|
SecretRef: &api.SecretEnvSource{
|
||||||
|
LocalObjectReference: api.LocalObjectReference{Name: ""}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedError: "field[0].secretRef.name: Required value",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid prefix",
|
||||||
|
envs: []api.EnvFromSource{
|
||||||
|
{
|
||||||
|
Prefix: "a.b",
|
||||||
|
SecretRef: &api.SecretEnvSource{
|
||||||
|
LocalObjectReference: api.LocalObjectReference{Name: "abc"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedError: `field[0].prefix: Invalid value: "a.b": ` + idErrMsg,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no refs",
|
||||||
|
envs: []api.EnvFromSource{
|
||||||
|
{},
|
||||||
|
},
|
||||||
|
expectedError: "field: Invalid value: \"\": must specify one of: `configMapRef` or `secretRef`",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple refs",
|
||||||
|
envs: []api.EnvFromSource{
|
||||||
|
{
|
||||||
|
SecretRef: &api.SecretEnvSource{
|
||||||
|
LocalObjectReference: api.LocalObjectReference{Name: "abc"}},
|
||||||
|
ConfigMapRef: &api.ConfigMapEnvSource{
|
||||||
|
LocalObjectReference: api.LocalObjectReference{Name: "abc"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedError: "field: Invalid value: \"\": may not have more than one field specified at a time",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tc := range errorCases {
|
for _, tc := range errorCases {
|
||||||
if errs := validateEnvFrom(tc.envs, field.NewPath("field")); len(errs) == 0 {
|
if errs := validateEnvFrom(tc.envs, field.NewPath("field")); len(errs) == 0 {
|
||||||
|
@ -1052,10 +1052,19 @@ func describeContainerEnvFrom(container api.Container, resolverFn EnvVarResolver
|
|||||||
w.Write(LEVEL_2, "Environment Variables from:%s\n", none)
|
w.Write(LEVEL_2, "Environment Variables from:%s\n", none)
|
||||||
|
|
||||||
for _, e := range container.EnvFrom {
|
for _, e := range container.EnvFrom {
|
||||||
|
from := ""
|
||||||
|
name := ""
|
||||||
|
if e.ConfigMapRef != nil {
|
||||||
|
from = "ConfigMap"
|
||||||
|
name = e.ConfigMapRef.Name
|
||||||
|
} else if e.SecretRef != nil {
|
||||||
|
from = "Secret"
|
||||||
|
name = e.SecretRef.Name
|
||||||
|
}
|
||||||
if len(e.Prefix) == 0 {
|
if len(e.Prefix) == 0 {
|
||||||
w.Write(LEVEL_3, "%s\tConfigMap\n", e.ConfigMapRef.Name)
|
w.Write(LEVEL_3, "%s\t%s\n", name, from)
|
||||||
} else {
|
} else {
|
||||||
w.Write(LEVEL_3, "%s\tConfigMap with prefix '%s'\n", e.ConfigMapRef.Name, e.Prefix)
|
w.Write(LEVEL_3, "%s\t%s with prefix '%s'\n", name, from, e.Prefix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -306,6 +306,24 @@ func TestDescribeContainers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tConfigMap with prefix 'p_'"},
|
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tConfigMap with prefix 'p_'"},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
container: api.Container{Name: "test", Image: "image", Env: []api.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []api.EnvFromSource{{SecretRef: &api.SecretEnvSource{LocalObjectReference: api.LocalObjectReference{Name: "a123"}}}}},
|
||||||
|
status: api.ContainerStatus{
|
||||||
|
Name: "test",
|
||||||
|
Ready: true,
|
||||||
|
RestartCount: 7,
|
||||||
|
},
|
||||||
|
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tSecret"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
container: api.Container{Name: "test", Image: "image", Env: []api.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []api.EnvFromSource{{Prefix: "p_", SecretRef: &api.SecretEnvSource{LocalObjectReference: api.LocalObjectReference{Name: "a123"}}}}},
|
||||||
|
status: api.ContainerStatus{
|
||||||
|
Name: "test",
|
||||||
|
Ready: true,
|
||||||
|
RestartCount: 7,
|
||||||
|
},
|
||||||
|
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tSecret with prefix 'p_'"},
|
||||||
|
},
|
||||||
// Command
|
// Command
|
||||||
{
|
{
|
||||||
container: api.Container{Name: "test", Image: "image", Command: []string{"sleep", "1000"}},
|
container: api.Container{Name: "test", Image: "image", Command: []string{"sleep", "1000"}},
|
||||||
|
@ -418,13 +418,15 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
configMaps = make(map[string]*v1.ConfigMap)
|
configMaps = make(map[string]*v1.ConfigMap)
|
||||||
|
secrets = make(map[string]*v1.Secret)
|
||||||
tmpEnv = make(map[string]string)
|
tmpEnv = make(map[string]string)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Env will override EnvFrom variables.
|
// Env will override EnvFrom variables.
|
||||||
// Process EnvFrom first then allow Env to replace existing values.
|
// Process EnvFrom first then allow Env to replace existing values.
|
||||||
for _, envFrom := range container.EnvFrom {
|
for _, envFrom := range container.EnvFrom {
|
||||||
if envFrom.ConfigMapRef != nil {
|
switch {
|
||||||
|
case envFrom.ConfigMapRef != nil:
|
||||||
name := envFrom.ConfigMapRef.Name
|
name := envFrom.ConfigMapRef.Name
|
||||||
configMap, ok := configMaps[name]
|
configMap, ok := configMaps[name]
|
||||||
if !ok {
|
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)
|
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{})
|
configMap, err = kl.kubeClient.Core().ConfigMaps(pod.Namespace).Get(name, metav1.GetOptions{})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
configMaps[name] = configMap
|
configMaps[name] = configMap
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range configMap.Data {
|
for k, v := range configMap.Data {
|
||||||
if len(envFrom.Prefix) > 0 {
|
if len(envFrom.Prefix) > 0 {
|
||||||
k = envFrom.Prefix + k
|
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 {
|
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])
|
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
|
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
|
// 2. Create the container's environment in the order variables are declared
|
||||||
// 3. Add remaining service environment vars
|
// 3. Add remaining service environment vars
|
||||||
var (
|
var (
|
||||||
secrets = make(map[string]*v1.Secret)
|
|
||||||
mappingFunc = expansion.MappingFuncFor(tmpEnv, serviceEnv)
|
mappingFunc = expansion.MappingFuncFor(tmpEnv, serviceEnv)
|
||||||
)
|
)
|
||||||
for _, envVar := range container.Env {
|
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
|
runtimeVal := envVar.Value
|
||||||
if runtimeVal != "" {
|
if runtimeVal != "" {
|
||||||
// Step 1a: expand variable references
|
// Step 1a: expand variable references
|
||||||
@ -548,7 +559,14 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
|
|||||||
|
|
||||||
// Append remaining service env vars.
|
// Append remaining service env vars.
|
||||||
for k, v := range serviceEnv {
|
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
|
return result, nil
|
||||||
}
|
}
|
||||||
|
@ -274,6 +274,7 @@ func TestMakeEnvironmentVariables(t *testing.T) {
|
|||||||
masterServiceNs string // the namespace to read master service info from
|
masterServiceNs string // the namespace to read master service info from
|
||||||
nilLister bool // whether the lister should be nil
|
nilLister bool // whether the lister should be nil
|
||||||
configMap *v1.ConfigMap // an optional ConfigMap to pull from
|
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
|
expectedEnvs []kubecontainer.EnvVar // a set of expected environment vars
|
||||||
expectedError bool // does the test fail
|
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 {
|
for _, tc := range testCases {
|
||||||
@ -786,6 +941,14 @@ func TestMakeEnvironmentVariables(t *testing.T) {
|
|||||||
return true, tc.configMap, err
|
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{
|
testPod := &v1.Pod{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Namespace: tc.ns,
|
Namespace: tc.ns,
|
||||||
|
@ -193,8 +193,62 @@ var _ = framework.KubeDescribe("Secrets", func() {
|
|||||||
"SECRET_DATA=value-1",
|
"SECRET_DATA=value-1",
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("should be consumable via the environment [Conformance]", func() {
|
||||||
|
name := "secret-test-" + string(uuid.NewUUID())
|
||||||
|
secret := newEnvFromSecret(f.Namespace.Name, name)
|
||||||
|
By(fmt.Sprintf("creating secret %v/%v", f.Namespace.Name, secret.Name))
|
||||||
|
var err error
|
||||||
|
if secret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(secret); err != nil {
|
||||||
|
framework.Failf("unable to create test secret %s: %v", secret.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pod := &v1.Pod{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod-configmaps-" + string(uuid.NewUUID()),
|
||||||
|
},
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
Containers: []v1.Container{
|
||||||
|
{
|
||||||
|
Name: "env-test",
|
||||||
|
Image: "gcr.io/google_containers/busybox:1.24",
|
||||||
|
Command: []string{"sh", "-c", "env"},
|
||||||
|
EnvFrom: []v1.EnvFromSource{
|
||||||
|
{
|
||||||
|
SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Prefix: "p_",
|
||||||
|
SecretRef: &v1.SecretEnvSource{LocalObjectReference: v1.LocalObjectReference{Name: name}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RestartPolicy: v1.RestartPolicyNever,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
f.TestContainerOutput("consume secrets", pod, 0, []string{
|
||||||
|
"data_1=value-1", "data_2=value-2", "data_3=value-3",
|
||||||
|
"p_data_1=value-1", "p_data_2=value-2", "p_data_3=value-3",
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
func newEnvFromSecret(namespace, name string) *v1.Secret {
|
||||||
|
return &v1.Secret{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Namespace: namespace,
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
Data: map[string][]byte{
|
||||||
|
"data_1": []byte("value-1\n"),
|
||||||
|
"data_2": []byte("value-2\n"),
|
||||||
|
"data_3": []byte("value-3\n"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func secretForTest(namespace, name string) *v1.Secret {
|
func secretForTest(namespace, name string) *v1.Secret {
|
||||||
return &v1.Secret{
|
return &v1.Secret{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Loading…
Reference in New Issue
Block a user