mirror of https://github.com/k3s-io/k3s
Allow Optional ConfigMap and Secrets
- ConfigMaps and Secrets for Env or Volumes are allowed to be optionalpull/6/head
parent
13424d874b
commit
4e466040d9
|
@ -286,6 +286,10 @@ func FuzzerFor(t *testing.T, version schema.GroupVersion, src rand.Source) *fuzz
|
|||
func(s *api.SecretVolumeSource, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(s) // fuzz self without calling this function again
|
||||
|
||||
if c.RandBool() {
|
||||
opt := c.RandBool()
|
||||
s.Optional = &opt
|
||||
}
|
||||
// DefaultMode should always be set, it has a default
|
||||
// value and it is expected to be between 0 and 0777
|
||||
var mode int32
|
||||
|
@ -296,6 +300,10 @@ func FuzzerFor(t *testing.T, version schema.GroupVersion, src rand.Source) *fuzz
|
|||
func(cm *api.ConfigMapVolumeSource, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(cm) // fuzz self without calling this function again
|
||||
|
||||
if c.RandBool() {
|
||||
opt := c.RandBool()
|
||||
cm.Optional = &opt
|
||||
}
|
||||
// DefaultMode should always be set, it has a default
|
||||
// value and it is expected to be between 0 and 0777
|
||||
var mode int32
|
||||
|
@ -401,6 +409,10 @@ func FuzzerFor(t *testing.T, version schema.GroupVersion, src rand.Source) *fuzz
|
|||
},
|
||||
func(cm *api.ConfigMapEnvSource, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(cm) // fuzz self without calling this function again
|
||||
if c.RandBool() {
|
||||
opt := c.RandBool()
|
||||
cm.Optional = &opt
|
||||
}
|
||||
},
|
||||
func(s *api.SecretEnvSource, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(s) // fuzz self without calling this function again
|
||||
|
|
|
@ -729,8 +729,8 @@ type SecretVolumeSource struct {
|
|||
// key and content is the value. If specified, the listed keys will be
|
||||
// projected into the specified paths, and unlisted keys will not be
|
||||
// present. If a key is specified which is not present in the Secret,
|
||||
// the volume setup will error. Paths must be relative and may not contain
|
||||
// the '..' path or start with '..'.
|
||||
// the volume setup will error unless it is marked optional. Paths must be
|
||||
// relative and may not contain the '..' path or start with '..'.
|
||||
// +optional
|
||||
Items []KeyToPath
|
||||
// Mode bits to use on created files by default. Must be a value between
|
||||
|
@ -740,6 +740,9 @@ type SecretVolumeSource struct {
|
|||
// mode, like fsGroup, and the result can be other mode bits set.
|
||||
// +optional
|
||||
DefaultMode *int32
|
||||
// Specify whether the Secret or it's key must be defined
|
||||
// +optional
|
||||
Optional *bool
|
||||
}
|
||||
|
||||
// Represents an NFS mount that lasts the lifetime of a pod.
|
||||
|
@ -992,8 +995,8 @@ type ConfigMapVolumeSource struct {
|
|||
// key and content is the value. If specified, the listed keys will be
|
||||
// projected into the specified paths, and unlisted keys will not be
|
||||
// present. If a key is specified which is not present in the ConfigMap,
|
||||
// the volume setup will error. Paths must be relative and may not contain
|
||||
// the '..' path or start with '..'.
|
||||
// the volume setup will error unless it is marked optional. Paths must be
|
||||
// relative and may not contain the '..' path or start with '..'.
|
||||
// +optional
|
||||
Items []KeyToPath
|
||||
// Mode bits to use on created files by default. Must be a value between
|
||||
|
@ -1003,6 +1006,9 @@ type ConfigMapVolumeSource struct {
|
|||
// mode, like fsGroup, and the result can be other mode bits set.
|
||||
// +optional
|
||||
DefaultMode *int32
|
||||
// Specify whether the ConfigMap or it's keys must be defined
|
||||
// +optional
|
||||
Optional *bool
|
||||
}
|
||||
|
||||
// Maps a string key to a path within a volume.
|
||||
|
@ -1124,6 +1130,9 @@ type ConfigMapKeySelector struct {
|
|||
LocalObjectReference
|
||||
// The key to select.
|
||||
Key string
|
||||
// Specify whether the ConfigMap or it's key must be defined
|
||||
// +optional
|
||||
Optional *bool
|
||||
}
|
||||
|
||||
// SecretKeySelector selects a key of a Secret.
|
||||
|
@ -1132,6 +1141,9 @@ type SecretKeySelector struct {
|
|||
LocalObjectReference
|
||||
// The key of the secret to select from. Must be a valid secret key.
|
||||
Key string
|
||||
// Specify whether the Secret or it's key must be defined
|
||||
// +optional
|
||||
Optional *bool
|
||||
}
|
||||
|
||||
// EnvFromSource represents the source of a set of ConfigMaps
|
||||
|
@ -1155,6 +1167,9 @@ type EnvFromSource struct {
|
|||
type ConfigMapEnvSource struct {
|
||||
// The ConfigMap to select from.
|
||||
LocalObjectReference
|
||||
// Specify whether the ConfigMap must be defined
|
||||
// +optional
|
||||
Optional *bool
|
||||
}
|
||||
|
||||
// SecretEnvSource selects a Secret to populate the environment
|
||||
|
@ -1165,6 +1180,9 @@ type ConfigMapEnvSource struct {
|
|||
type SecretEnvSource struct {
|
||||
// The Secret to select from.
|
||||
LocalObjectReference
|
||||
// Specify whether the Secret must be defined
|
||||
// +optional
|
||||
Optional *bool
|
||||
}
|
||||
|
||||
// HTTPHeader describes a custom header to be used in HTTP probes
|
||||
|
|
|
@ -924,8 +924,8 @@ type SecretVolumeSource struct {
|
|||
// key and content is the value. If specified, the listed keys will be
|
||||
// projected into the specified paths, and unlisted keys will not be
|
||||
// present. If a key is specified which is not present in the Secret,
|
||||
// the volume setup will error. Paths must be relative and may not contain
|
||||
// the '..' path or start with '..'.
|
||||
// the volume setup will error unless it is marked optional. Paths must be
|
||||
// relative and may not contain the '..' path or start with '..'.
|
||||
// +optional
|
||||
Items []KeyToPath `json:"items,omitempty" protobuf:"bytes,2,rep,name=items"`
|
||||
// Optional: mode bits to use on created files by default. Must be a
|
||||
|
@ -935,6 +935,9 @@ type SecretVolumeSource struct {
|
|||
// mode, like fsGroup, and the result can be other mode bits set.
|
||||
// +optional
|
||||
DefaultMode *int32 `json:"defaultMode,omitempty" protobuf:"bytes,3,opt,name=defaultMode"`
|
||||
// Specify whether the Secret or it's keys must be defined
|
||||
// +optional
|
||||
Optional *bool `json:"optional,omitempty" protobuf:"varint,4,opt,name=optional"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -1081,8 +1084,8 @@ type ConfigMapVolumeSource struct {
|
|||
// key and content is the value. If specified, the listed keys will be
|
||||
// projected into the specified paths, and unlisted keys will not be
|
||||
// present. If a key is specified which is not present in the ConfigMap,
|
||||
// the volume setup will error. Paths must be relative and may not contain
|
||||
// the '..' path or start with '..'.
|
||||
// the volume setup will error unless it is marked optional. Paths must be
|
||||
// relative and may not contain the '..' path or start with '..'.
|
||||
// +optional
|
||||
Items []KeyToPath `json:"items,omitempty" protobuf:"bytes,2,rep,name=items"`
|
||||
// Optional: mode bits to use on created files by default. Must be a
|
||||
|
@ -1092,6 +1095,9 @@ type ConfigMapVolumeSource struct {
|
|||
// mode, like fsGroup, and the result can be other mode bits set.
|
||||
// +optional
|
||||
DefaultMode *int32 `json:"defaultMode,omitempty" protobuf:"varint,3,opt,name=defaultMode"`
|
||||
// Specify whether the ConfigMap or it's keys must be defined
|
||||
// +optional
|
||||
Optional *bool `json:"optional,omitempty" protobuf:"varint,4,opt,name=optional"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -1225,6 +1231,9 @@ type ConfigMapKeySelector struct {
|
|||
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
|
||||
// The key to select.
|
||||
Key string `json:"key" protobuf:"bytes,2,opt,name=key"`
|
||||
// Specify whether the ConfigMap or it's key must be defined
|
||||
// +optional
|
||||
Optional *bool `json:"optional,omitempty" protobuf:"varint,3,opt,name=optional"`
|
||||
}
|
||||
|
||||
// SecretKeySelector selects a key of a Secret.
|
||||
|
@ -1233,6 +1242,9 @@ type SecretKeySelector struct {
|
|||
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
|
||||
// The key of the secret to select from. Must be a valid secret key.
|
||||
Key string `json:"key" protobuf:"bytes,2,opt,name=key"`
|
||||
// Specify whether the Secret or it's key must be defined
|
||||
// +optional
|
||||
Optional *bool `json:"optional,omitempty" protobuf:"varint,3,opt,name=optional"`
|
||||
}
|
||||
|
||||
// EnvFromSource represents the source of a set of ConfigMaps
|
||||
|
@ -1256,6 +1268,9 @@ type EnvFromSource struct {
|
|||
type ConfigMapEnvSource struct {
|
||||
// The ConfigMap to select from.
|
||||
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
|
||||
// Specify whether the ConfigMap must be defined
|
||||
// +optional
|
||||
Optional *bool `json:"optional,omitempty" protobuf:"varint,2,opt,name=optional"`
|
||||
}
|
||||
|
||||
// SecretEnvSource selects a Secret to populate the environment
|
||||
|
@ -1266,6 +1281,9 @@ type ConfigMapEnvSource struct {
|
|||
type SecretEnvSource struct {
|
||||
// The Secret to select from.
|
||||
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
|
||||
// Specify whether the Secret must be defined
|
||||
// +optional
|
||||
Optional *bool `json:"optional,omitempty" protobuf:"varint,2,opt,name=optional"`
|
||||
}
|
||||
|
||||
// HTTPHeader describes a custom header to be used in HTTP probes
|
||||
|
|
|
@ -650,13 +650,19 @@ func printGitRepoVolumeSource(git *api.GitRepoVolumeSource, w *PrefixWriter) {
|
|||
}
|
||||
|
||||
func printSecretVolumeSource(secret *api.SecretVolumeSource, w *PrefixWriter) {
|
||||
optional := secret.Optional != nil && *secret.Optional
|
||||
w.Write(LEVEL_2, "Type:\tSecret (a volume populated by a Secret)\n"+
|
||||
" SecretName:\t%v\n", secret.SecretName)
|
||||
" SecretName:\t%v\n",
|
||||
" Optional:\t%v\n",
|
||||
secret.SecretName, optional)
|
||||
}
|
||||
|
||||
func printConfigMapVolumeSource(configMap *api.ConfigMapVolumeSource, w *PrefixWriter) {
|
||||
optional := configMap.Optional != nil && *configMap.Optional
|
||||
w.Write(LEVEL_2, "Type:\tConfigMap (a volume populated by a ConfigMap)\n"+
|
||||
" Name:\t%v\n", configMap.Name)
|
||||
" Name:\t%v\n"+
|
||||
" Optional:\t%v\n",
|
||||
configMap.Name, optional)
|
||||
}
|
||||
|
||||
func printNFSVolumeSource(nfs *api.NFSVolumeSource, w *PrefixWriter) {
|
||||
|
@ -1037,9 +1043,11 @@ func describeContainerEnvVars(container api.Container, resolverFn EnvVarResolver
|
|||
}
|
||||
w.Write(LEVEL_3, "%s:\t%s (%s)\n", e.Name, valueFrom, resource)
|
||||
case e.ValueFrom.SecretKeyRef != nil:
|
||||
w.Write(LEVEL_3, "%s:\t<set to the key '%s' in secret '%s'>\n", e.Name, e.ValueFrom.SecretKeyRef.Key, e.ValueFrom.SecretKeyRef.Name)
|
||||
optional := e.ValueFrom.SecretKeyRef.Optional != nil && *e.ValueFrom.SecretKeyRef.Optional
|
||||
w.Write(LEVEL_3, "%s:\t<set to the key '%s' in secret '%s'>\tOptional: %t\n", e.Name, e.ValueFrom.SecretKeyRef.Key, e.ValueFrom.SecretKeyRef.Name, optional)
|
||||
case e.ValueFrom.ConfigMapKeyRef != nil:
|
||||
w.Write(LEVEL_3, "%s:\t<set to the key '%s' of config map '%s'>\n", e.Name, e.ValueFrom.ConfigMapKeyRef.Key, e.ValueFrom.ConfigMapKeyRef.Name)
|
||||
optional := e.ValueFrom.ConfigMapKeyRef.Optional != nil && *e.ValueFrom.ConfigMapKeyRef.Optional
|
||||
w.Write(LEVEL_3, "%s:\t<set to the key '%s' of config map '%s'>\tOptional: %t\n", e.Name, e.ValueFrom.ConfigMapKeyRef.Key, e.ValueFrom.ConfigMapKeyRef.Name, optional)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1054,17 +1062,20 @@ func describeContainerEnvFrom(container api.Container, resolverFn EnvVarResolver
|
|||
for _, e := range container.EnvFrom {
|
||||
from := ""
|
||||
name := ""
|
||||
optional := false
|
||||
if e.ConfigMapRef != nil {
|
||||
from = "ConfigMap"
|
||||
name = e.ConfigMapRef.Name
|
||||
optional = e.ConfigMapRef.Optional != nil && *e.ConfigMapRef.Optional
|
||||
} else if e.SecretRef != nil {
|
||||
from = "Secret"
|
||||
name = e.SecretRef.Name
|
||||
optional = e.SecretRef.Optional != nil && *e.SecretRef.Optional
|
||||
}
|
||||
if len(e.Prefix) == 0 {
|
||||
w.Write(LEVEL_3, "%s\t%s\n", name, from)
|
||||
w.Write(LEVEL_3, "%s\t%s\tOptional: %t\n", name, from, optional)
|
||||
} else {
|
||||
w.Write(LEVEL_3, "%s\t%s with prefix '%s'\n", name, from, e.Prefix)
|
||||
w.Write(LEVEL_3, "%s\t%s with prefix '%s'\tOptional: %t\n", name, from, e.Prefix, optional)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -201,6 +201,7 @@ func VerifyDatesInOrder(
|
|||
}
|
||||
|
||||
func TestDescribeContainers(t *testing.T) {
|
||||
trueVal := true
|
||||
testCases := []struct {
|
||||
container api.Container
|
||||
status api.ContainerStatus
|
||||
|
@ -295,7 +296,7 @@ func TestDescribeContainers(t *testing.T) {
|
|||
Ready: true,
|
||||
RestartCount: 7,
|
||||
},
|
||||
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tConfigMap"},
|
||||
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tConfigMap\tOptional: false"},
|
||||
},
|
||||
{
|
||||
container: api.Container{Name: "test", Image: "image", Env: []api.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []api.EnvFromSource{{Prefix: "p_", ConfigMapRef: &api.ConfigMapEnvSource{LocalObjectReference: api.LocalObjectReference{Name: "a123"}}}}},
|
||||
|
@ -304,16 +305,25 @@ func TestDescribeContainers(t *testing.T) {
|
|||
Ready: true,
|
||||
RestartCount: 7,
|
||||
},
|
||||
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_'\tOptional: false"},
|
||||
},
|
||||
{
|
||||
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"}}}}},
|
||||
container: api.Container{Name: "test", Image: "image", Env: []api.EnvVar{{Name: "envname", Value: "xyz"}}, EnvFrom: []api.EnvFromSource{{ConfigMapRef: &api.ConfigMapEnvSource{Optional: &trueVal, 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"},
|
||||
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tConfigMap\tOptional: true"},
|
||||
},
|
||||
{
|
||||
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"}, Optional: &trueVal}}}},
|
||||
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\tOptional: true"},
|
||||
},
|
||||
{
|
||||
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"}}}}},
|
||||
|
@ -322,7 +332,7 @@ func TestDescribeContainers(t *testing.T) {
|
|||
Ready: true,
|
||||
RestartCount: 7,
|
||||
},
|
||||
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tSecret with prefix 'p_'"},
|
||||
expectedElements: []string{"test", "State", "Waiting", "Ready", "True", "Restart Count", "7", "Image", "image", "envname", "xyz", "a123\tSecret with prefix 'p_'\tOptional: false"},
|
||||
},
|
||||
// Command
|
||||
{
|
||||
|
|
|
@ -33,6 +33,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
@ -427,14 +428,20 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
|
|||
for _, envFrom := range container.EnvFrom {
|
||||
switch {
|
||||
case envFrom.ConfigMapRef != nil:
|
||||
name := envFrom.ConfigMapRef.Name
|
||||
cm := envFrom.ConfigMapRef
|
||||
name := cm.Name
|
||||
configMap, ok := configMaps[name]
|
||||
if !ok {
|
||||
if kl.kubeClient == nil {
|
||||
return result, fmt.Errorf("Couldn't get configMap %v/%v, no kubeClient defined", pod.Namespace, name)
|
||||
}
|
||||
optional := cm.Optional != nil && *cm.Optional
|
||||
configMap, err = kl.kubeClient.Core().ConfigMaps(pod.Namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) && optional {
|
||||
// ignore error when marked optional
|
||||
continue
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
configMaps[name] = configMap
|
||||
|
@ -450,14 +457,20 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
|
|||
tmpEnv[k] = v
|
||||
}
|
||||
case envFrom.SecretRef != nil:
|
||||
name := envFrom.SecretRef.Name
|
||||
s := envFrom.SecretRef
|
||||
name := s.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)
|
||||
}
|
||||
optional := s.Optional != nil && *s.Optional
|
||||
secret, err = kl.kubeClient.Core().Secrets(pod.Namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) && optional {
|
||||
// ignore error when marked optional
|
||||
continue
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
secrets[name] = secret
|
||||
|
@ -510,8 +523,10 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
|
|||
return result, err
|
||||
}
|
||||
case envVar.ValueFrom.ConfigMapKeyRef != nil:
|
||||
name := envVar.ValueFrom.ConfigMapKeyRef.Name
|
||||
key := envVar.ValueFrom.ConfigMapKeyRef.Key
|
||||
cm := envVar.ValueFrom.ConfigMapKeyRef
|
||||
name := cm.Name
|
||||
key := cm.Key
|
||||
optional := cm.Optional != nil && *cm.Optional
|
||||
configMap, ok := configMaps[name]
|
||||
if !ok {
|
||||
if kl.kubeClient == nil {
|
||||
|
@ -519,17 +534,26 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
|
|||
}
|
||||
configMap, err = kl.kubeClient.Core().ConfigMaps(pod.Namespace).Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) && optional {
|
||||
// ignore error when marked optional
|
||||
continue
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
configMaps[name] = configMap
|
||||
}
|
||||
runtimeVal, ok = configMap.Data[key]
|
||||
if !ok {
|
||||
if optional {
|
||||
continue
|
||||
}
|
||||
return result, fmt.Errorf("Couldn't find key %v in ConfigMap %v/%v", key, pod.Namespace, name)
|
||||
}
|
||||
case envVar.ValueFrom.SecretKeyRef != nil:
|
||||
name := envVar.ValueFrom.SecretKeyRef.Name
|
||||
key := envVar.ValueFrom.SecretKeyRef.Key
|
||||
s := envVar.ValueFrom.SecretKeyRef
|
||||
name := s.Name
|
||||
key := s.Key
|
||||
optional := s.Optional != nil && *s.Optional
|
||||
secret, ok := secrets[name]
|
||||
if !ok {
|
||||
if kl.kubeClient == nil {
|
||||
|
@ -537,17 +561,30 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
|
|||
}
|
||||
secret, err = kl.secretManager.GetSecret(pod.Namespace, name)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) && optional {
|
||||
// ignore error when marked optional
|
||||
continue
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
secrets[name] = secret
|
||||
}
|
||||
runtimeValBytes, ok := secret.Data[key]
|
||||
if !ok {
|
||||
if optional {
|
||||
continue
|
||||
}
|
||||
return result, fmt.Errorf("Couldn't find key %v in Secret %v/%v", key, pod.Namespace, name)
|
||||
}
|
||||
runtimeVal = string(runtimeValBytes)
|
||||
}
|
||||
}
|
||||
// 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)
|
||||
|
||||
tmpEnv[envVar.Name] = runtimeVal
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
@ -257,6 +258,7 @@ func buildService(name, namespace, clusterIP, protocol string, port int) *v1.Ser
|
|||
}
|
||||
|
||||
func TestMakeEnvironmentVariables(t *testing.T) {
|
||||
trueVal := true
|
||||
services := []*v1.Service{
|
||||
buildService("kubernetes", v1.NamespaceDefault, "1.2.3.1", "TCP", 8081),
|
||||
buildService("test", "test1", "1.2.3.3", "TCP", 8083),
|
||||
|
@ -616,6 +618,106 @@ func TestMakeEnvironmentVariables(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "configmapkeyref_missing_optional",
|
||||
ns: "test",
|
||||
container: &v1.Container{
|
||||
Env: []v1.EnvVar{
|
||||
{
|
||||
Name: "POD_NAME",
|
||||
ValueFrom: &v1.EnvVarSource{
|
||||
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{Name: "missing-config-map"},
|
||||
Key: "key",
|
||||
Optional: &trueVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
masterServiceNs: "nothing",
|
||||
expectedEnvs: nil,
|
||||
},
|
||||
{
|
||||
name: "configmapkeyref_missing_key_optional",
|
||||
ns: "test",
|
||||
container: &v1.Container{
|
||||
Env: []v1.EnvVar{
|
||||
{
|
||||
Name: "POD_NAME",
|
||||
ValueFrom: &v1.EnvVarSource{
|
||||
ConfigMapKeyRef: &v1.ConfigMapKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{Name: "test-config-map"},
|
||||
Key: "key",
|
||||
Optional: &trueVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
masterServiceNs: "nothing",
|
||||
nilLister: true,
|
||||
configMap: &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test1",
|
||||
Name: "test-configmap",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"a": "b",
|
||||
},
|
||||
},
|
||||
expectedEnvs: nil,
|
||||
},
|
||||
{
|
||||
name: "secretkeyref_missing_optional",
|
||||
ns: "test",
|
||||
container: &v1.Container{
|
||||
Env: []v1.EnvVar{
|
||||
{
|
||||
Name: "POD_NAME",
|
||||
ValueFrom: &v1.EnvVarSource{
|
||||
SecretKeyRef: &v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{Name: "missing-secret"},
|
||||
Key: "key",
|
||||
Optional: &trueVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
masterServiceNs: "nothing",
|
||||
expectedEnvs: nil,
|
||||
},
|
||||
{
|
||||
name: "secretkeyref_missing_key_optional",
|
||||
ns: "test",
|
||||
container: &v1.Container{
|
||||
Env: []v1.EnvVar{
|
||||
{
|
||||
Name: "POD_NAME",
|
||||
ValueFrom: &v1.EnvVarSource{
|
||||
SecretKeyRef: &v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{Name: "test-secret"},
|
||||
Key: "key",
|
||||
Optional: &trueVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
masterServiceNs: "nothing",
|
||||
nilLister: true,
|
||||
secret: &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "test1",
|
||||
Name: "test-secret",
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"a": []byte("b"),
|
||||
},
|
||||
},
|
||||
expectedEnvs: nil,
|
||||
},
|
||||
{
|
||||
name: "configmap",
|
||||
ns: "test1",
|
||||
|
@ -722,6 +824,19 @@ func TestMakeEnvironmentVariables(t *testing.T) {
|
|||
masterServiceNs: "nothing",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "configmap_missing_optional",
|
||||
ns: "test",
|
||||
container: &v1.Container{
|
||||
EnvFrom: []v1.EnvFromSource{
|
||||
{ConfigMapRef: &v1.ConfigMapEnvSource{
|
||||
Optional: &trueVal,
|
||||
LocalObjectReference: v1.LocalObjectReference{Name: "missing-config-map"}}},
|
||||
},
|
||||
},
|
||||
masterServiceNs: "nothing",
|
||||
expectedEnvs: nil,
|
||||
},
|
||||
{
|
||||
name: "configmap_invalid_keys",
|
||||
ns: "test1",
|
||||
|
@ -876,6 +991,19 @@ func TestMakeEnvironmentVariables(t *testing.T) {
|
|||
masterServiceNs: "nothing",
|
||||
expectedError: true,
|
||||
},
|
||||
{
|
||||
name: "secret_missing_optional",
|
||||
ns: "test",
|
||||
container: &v1.Container{
|
||||
EnvFrom: []v1.EnvFromSource{
|
||||
{SecretRef: &v1.SecretEnvSource{
|
||||
LocalObjectReference: v1.LocalObjectReference{Name: "missing-secret"},
|
||||
Optional: &trueVal}},
|
||||
},
|
||||
},
|
||||
masterServiceNs: "nothing",
|
||||
expectedEnvs: nil,
|
||||
},
|
||||
{
|
||||
name: "secret_invalid_keys",
|
||||
ns: "test1",
|
||||
|
@ -940,10 +1068,17 @@ func TestMakeEnvironmentVariables(t *testing.T) {
|
|||
testKubelet.fakeKubeClient.AddReactor("get", "configmaps", func(action core.Action) (bool, runtime.Object, error) {
|
||||
var err error
|
||||
if tc.configMap == nil {
|
||||
err = errors.New("no configmap defined")
|
||||
err = apierrors.NewNotFound(action.GetResource().GroupResource(), "configmap-name")
|
||||
}
|
||||
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 = apierrors.NewNotFound(action.GetResource().GroupResource(), "secret-name")
|
||||
}
|
||||
return true, tc.secret, err
|
||||
})
|
||||
|
||||
testKubelet.fakeKubeClient.AddReactor("get", "secrets", func(action core.Action) (bool, runtime.Object, error) {
|
||||
var err error
|
||||
|
|
|
@ -173,8 +173,12 @@ func newTestKubeletWithImageList(
|
|||
kubelet.cadvisor = mockCadvisor
|
||||
|
||||
fakeMirrorClient := podtest.NewFakeMirrorClient()
|
||||
fakeSecretManager := secret.NewFakeManager()
|
||||
kubelet.podManager = kubepod.NewBasicPodManager(fakeMirrorClient, fakeSecretManager)
|
||||
secretManager, err := secret.NewSimpleSecretManager(kubelet.kubeClient)
|
||||
if err != nil {
|
||||
t.Fatalf("can't create a secret manager: %v", err)
|
||||
}
|
||||
kubelet.secretManager = secretManager
|
||||
kubelet.podManager = kubepod.NewBasicPodManager(fakeMirrorClient, kubelet.secretManager)
|
||||
kubelet.statusManager = status.NewManager(fakeKubeClient, kubelet.podManager)
|
||||
kubelet.containerRefManager = kubecontainer.NewRefManager()
|
||||
diskSpaceManager, err := newDiskSpaceManager(mockCadvisor, DiskSpacePolicy{})
|
||||
|
@ -249,7 +253,7 @@ func newTestKubeletWithImageList(
|
|||
|
||||
plug := &volumetest.FakeVolumePlugin{PluginName: "fake", Host: nil}
|
||||
kubelet.volumePluginMgr, err =
|
||||
NewInitializedVolumePluginMgr(kubelet, fakeSecretManager, []volume.VolumePlugin{plug})
|
||||
NewInitializedVolumePluginMgr(kubelet, kubelet.secretManager, []volume.VolumePlugin{plug})
|
||||
require.NoError(t, err, "Failed to initialize VolumePluginMgr")
|
||||
|
||||
kubelet.mounter = &mount.FakeMounter{}
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
|
@ -170,10 +171,19 @@ func (b *configMapVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||
return fmt.Errorf("Cannot setup configMap volume %v because kube client is not configured", b.volName)
|
||||
}
|
||||
|
||||
optional := b.source.Optional != nil && *b.source.Optional
|
||||
configMap, err := kubeClient.Core().ConfigMaps(b.pod.Namespace).Get(b.source.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
glog.Errorf("Couldn't get configMap %v/%v: %v", b.pod.Namespace, b.source.Name, err)
|
||||
return err
|
||||
if !(errors.IsNotFound(err) && optional) {
|
||||
glog.Errorf("Couldn't get configMap %v/%v: %v", b.pod.Namespace, b.source.Name, err)
|
||||
return err
|
||||
}
|
||||
configMap = &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: b.pod.Namespace,
|
||||
Name: b.source.Name,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
totalBytes := totalBytes(configMap)
|
||||
|
@ -183,7 +193,7 @@ func (b *configMapVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||
len(configMap.Data),
|
||||
totalBytes)
|
||||
|
||||
payload, err := makePayload(b.source.Items, configMap, b.source.DefaultMode)
|
||||
payload, err := makePayload(b.source.Items, configMap, b.source.DefaultMode, optional)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -210,7 +220,7 @@ func (b *configMapVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func makePayload(mappings []v1.KeyToPath, configMap *v1.ConfigMap, defaultMode *int32) (map[string]volumeutil.FileProjection, error) {
|
||||
func makePayload(mappings []v1.KeyToPath, configMap *v1.ConfigMap, defaultMode *int32, optional bool) (map[string]volumeutil.FileProjection, error) {
|
||||
if defaultMode == nil {
|
||||
return nil, fmt.Errorf("No defaultMode used, not even the default value for it")
|
||||
}
|
||||
|
@ -228,6 +238,9 @@ func makePayload(mappings []v1.KeyToPath, configMap *v1.ConfigMap, defaultMode *
|
|||
for _, ktp := range mappings {
|
||||
content, ok := configMap.Data[ktp.Key]
|
||||
if !ok {
|
||||
if optional {
|
||||
continue
|
||||
}
|
||||
err_msg := "references non-existent config key"
|
||||
glog.Errorf(err_msg)
|
||||
return nil, fmt.Errorf(err_msg)
|
||||
|
|
|
@ -43,6 +43,7 @@ func TestMakePayload(t *testing.T) {
|
|||
mappings []v1.KeyToPath
|
||||
configMap *v1.ConfigMap
|
||||
mode int32
|
||||
optional bool
|
||||
payload map[string]util.FileProjection
|
||||
success bool
|
||||
}{
|
||||
|
@ -215,10 +216,29 @@ func TestMakePayload(t *testing.T) {
|
|||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "optional non existent key",
|
||||
mappings: []v1.KeyToPath{
|
||||
{
|
||||
Key: "zab",
|
||||
Path: "path/to/foo.txt",
|
||||
},
|
||||
},
|
||||
configMap: &v1.ConfigMap{
|
||||
Data: map[string]string{
|
||||
"foo": "foo",
|
||||
"bar": "bar",
|
||||
},
|
||||
},
|
||||
mode: 0644,
|
||||
optional: true,
|
||||
payload: map[string]util.FileProjection{},
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
actualPayload, err := makePayload(tc.mappings, tc.configMap, &tc.mode)
|
||||
actualPayload, err := makePayload(tc.mappings, tc.configMap, &tc.mode, tc.optional)
|
||||
if err != nil && tc.success {
|
||||
t.Errorf("%v: unexpected failure making payload: %v", tc.name, err)
|
||||
continue
|
||||
|
@ -388,6 +408,143 @@ func TestPluginReboot(t *testing.T) {
|
|||
doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t)
|
||||
}
|
||||
|
||||
func TestPluginOptional(t *testing.T) {
|
||||
var (
|
||||
testPodUID = types.UID("test_pod_uid")
|
||||
testVolumeName = "test_volume_name"
|
||||
testNamespace = "test_configmap_namespace"
|
||||
testName = "test_configmap_name"
|
||||
trueVal = true
|
||||
|
||||
volumeSpec = volumeSpec(testVolumeName, testName, 0644)
|
||||
client = fake.NewSimpleClientset()
|
||||
pluginMgr = volume.VolumePluginMgr{}
|
||||
tempDir, host = newTestHost(t, client)
|
||||
)
|
||||
volumeSpec.VolumeSource.ConfigMap.Optional = &trueVal
|
||||
|
||||
defer os.RemoveAll(tempDir)
|
||||
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
|
||||
|
||||
plugin, err := pluginMgr.FindPluginByName(configMapPluginName)
|
||||
if err != nil {
|
||||
t.Errorf("Can't find the plugin by name")
|
||||
}
|
||||
|
||||
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, UID: testPodUID}}
|
||||
mounter, err := plugin.NewMounter(volume.NewSpecFromVolume(volumeSpec), pod, volume.VolumeOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Failed to make a new Mounter: %v", err)
|
||||
}
|
||||
if mounter == nil {
|
||||
t.Errorf("Got a nil Mounter")
|
||||
}
|
||||
|
||||
vName, err := plugin.GetVolumeName(volume.NewSpecFromVolume(volumeSpec))
|
||||
if err != nil {
|
||||
t.Errorf("Failed to GetVolumeName: %v", err)
|
||||
}
|
||||
if vName != "test_volume_name/test_configmap_name" {
|
||||
t.Errorf("Got unexpect VolumeName %v", vName)
|
||||
}
|
||||
|
||||
volumePath := mounter.GetPath()
|
||||
if !strings.HasSuffix(volumePath, fmt.Sprintf("pods/test_pod_uid/volumes/kubernetes.io~configmap/test_volume_name")) {
|
||||
t.Errorf("Got unexpected path: %s", volumePath)
|
||||
}
|
||||
|
||||
fsGroup := int64(1001)
|
||||
err = mounter.SetUp(&fsGroup)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to setup volume: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(volumePath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
t.Errorf("SetUp() failed, volume path not created: %s", volumePath)
|
||||
} else {
|
||||
t.Errorf("SetUp() failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
infos, err := ioutil.ReadDir(volumePath)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't find volume path, %s", volumePath)
|
||||
}
|
||||
if len(infos) != 0 {
|
||||
t.Errorf("empty directory, %s, not found", volumePath)
|
||||
}
|
||||
doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t)
|
||||
}
|
||||
|
||||
func TestPluginKeysOptional(t *testing.T) {
|
||||
var (
|
||||
testPodUID = types.UID("test_pod_uid")
|
||||
testVolumeName = "test_volume_name"
|
||||
testNamespace = "test_configmap_namespace"
|
||||
testName = "test_configmap_name"
|
||||
trueVal = true
|
||||
|
||||
volumeSpec = volumeSpec(testVolumeName, testName, 0644)
|
||||
configMap = configMap(testNamespace, testName)
|
||||
client = fake.NewSimpleClientset(&configMap)
|
||||
pluginMgr = volume.VolumePluginMgr{}
|
||||
tempDir, host = newTestHost(t, client)
|
||||
)
|
||||
volumeSpec.VolumeSource.ConfigMap.Items = []v1.KeyToPath{
|
||||
{Key: "data-1", Path: "data-1"},
|
||||
{Key: "data-2", Path: "data-2"},
|
||||
{Key: "data-3", Path: "data-3"},
|
||||
{Key: "missing", Path: "missing"},
|
||||
}
|
||||
volumeSpec.VolumeSource.ConfigMap.Optional = &trueVal
|
||||
|
||||
defer os.RemoveAll(tempDir)
|
||||
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
|
||||
|
||||
plugin, err := pluginMgr.FindPluginByName(configMapPluginName)
|
||||
if err != nil {
|
||||
t.Errorf("Can't find the plugin by name")
|
||||
}
|
||||
|
||||
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, UID: testPodUID}}
|
||||
mounter, err := plugin.NewMounter(volume.NewSpecFromVolume(volumeSpec), pod, volume.VolumeOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Failed to make a new Mounter: %v", err)
|
||||
}
|
||||
if mounter == nil {
|
||||
t.Errorf("Got a nil Mounter")
|
||||
}
|
||||
|
||||
vName, err := plugin.GetVolumeName(volume.NewSpecFromVolume(volumeSpec))
|
||||
if err != nil {
|
||||
t.Errorf("Failed to GetVolumeName: %v", err)
|
||||
}
|
||||
if vName != "test_volume_name/test_configmap_name" {
|
||||
t.Errorf("Got unexpect VolumeName %v", vName)
|
||||
}
|
||||
|
||||
volumePath := mounter.GetPath()
|
||||
if !strings.HasSuffix(volumePath, fmt.Sprintf("pods/test_pod_uid/volumes/kubernetes.io~configmap/test_volume_name")) {
|
||||
t.Errorf("Got unexpected path: %s", volumePath)
|
||||
}
|
||||
|
||||
fsGroup := int64(1001)
|
||||
err = mounter.SetUp(&fsGroup)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to setup volume: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(volumePath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
t.Errorf("SetUp() failed, volume path not created: %s", volumePath)
|
||||
} else {
|
||||
t.Errorf("SetUp() failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
doTestConfigMapDataInVolume(volumePath, configMap, t)
|
||||
doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t)
|
||||
}
|
||||
|
||||
func volumeSpec(volumeName, configMapName string, defaultMode int32) *v1.Volume {
|
||||
return &v1.Volume{
|
||||
Name: volumeName,
|
||||
|
|
|
@ -22,6 +22,8 @@ import (
|
|||
"runtime"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
ioutil "k8s.io/kubernetes/pkg/util/io"
|
||||
|
@ -191,10 +193,19 @@ func (b *secretVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||
return err
|
||||
}
|
||||
|
||||
optional := b.source.Optional != nil && *b.source.Optional
|
||||
secret, err := b.getSecret(b.pod.Namespace, b.source.SecretName)
|
||||
if err != nil {
|
||||
glog.Errorf("Couldn't get secret %v/%v", b.pod.Namespace, b.source.SecretName)
|
||||
return err
|
||||
if !(errors.IsNotFound(err) && optional) {
|
||||
glog.Errorf("Couldn't get secret %v/%v", b.pod.Namespace, b.source.SecretName)
|
||||
return err
|
||||
}
|
||||
secret = &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: b.pod.Namespace,
|
||||
Name: b.source.SecretName,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
totalBytes := totalSecretBytes(secret)
|
||||
|
@ -204,7 +215,7 @@ func (b *secretVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||
len(secret.Data),
|
||||
totalBytes)
|
||||
|
||||
payload, err := makePayload(b.source.Items, secret, b.source.DefaultMode)
|
||||
payload, err := makePayload(b.source.Items, secret, b.source.DefaultMode, optional)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -231,7 +242,7 @@ func (b *secretVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func makePayload(mappings []v1.KeyToPath, secret *v1.Secret, defaultMode *int32) (map[string]volumeutil.FileProjection, error) {
|
||||
func makePayload(mappings []v1.KeyToPath, secret *v1.Secret, defaultMode *int32, optional bool) (map[string]volumeutil.FileProjection, error) {
|
||||
if defaultMode == nil {
|
||||
return nil, fmt.Errorf("No defaultMode used, not even the default value for it")
|
||||
}
|
||||
|
@ -249,6 +260,9 @@ func makePayload(mappings []v1.KeyToPath, secret *v1.Secret, defaultMode *int32)
|
|||
for _, ktp := range mappings {
|
||||
content, ok := secret.Data[ktp.Key]
|
||||
if !ok {
|
||||
if optional {
|
||||
continue
|
||||
}
|
||||
err_msg := "references non-existent secret key"
|
||||
glog.Errorf(err_msg)
|
||||
return nil, fmt.Errorf(err_msg)
|
||||
|
|
|
@ -46,6 +46,7 @@ func TestMakePayload(t *testing.T) {
|
|||
mappings []v1.KeyToPath
|
||||
secret *v1.Secret
|
||||
mode int32
|
||||
optional bool
|
||||
payload map[string]util.FileProjection
|
||||
success bool
|
||||
}{
|
||||
|
@ -218,10 +219,29 @@ func TestMakePayload(t *testing.T) {
|
|||
},
|
||||
success: true,
|
||||
},
|
||||
{
|
||||
name: "optional non existent key",
|
||||
mappings: []v1.KeyToPath{
|
||||
{
|
||||
Key: "zab",
|
||||
Path: "path/to/foo.txt",
|
||||
},
|
||||
},
|
||||
secret: &v1.Secret{
|
||||
Data: map[string][]byte{
|
||||
"foo": []byte("foo"),
|
||||
"bar": []byte("bar"),
|
||||
},
|
||||
},
|
||||
mode: 0644,
|
||||
optional: true,
|
||||
payload: map[string]util.FileProjection{},
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
actualPayload, err := makePayload(tc.mappings, tc.secret, &tc.mode)
|
||||
actualPayload, err := makePayload(tc.mappings, tc.secret, &tc.mode, tc.optional)
|
||||
if err != nil && tc.success {
|
||||
t.Errorf("%v: unexpected failure making payload: %v", tc.name, err)
|
||||
continue
|
||||
|
@ -398,6 +418,154 @@ func TestPluginReboot(t *testing.T) {
|
|||
doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t)
|
||||
}
|
||||
|
||||
func TestPluginOptional(t *testing.T) {
|
||||
var (
|
||||
testPodUID = types.UID("test_pod_uid")
|
||||
testVolumeName = "test_volume_name"
|
||||
testNamespace = "test_secret_namespace"
|
||||
testName = "test_secret_name"
|
||||
trueVal = true
|
||||
|
||||
volumeSpec = volumeSpec(testVolumeName, testName, 0644)
|
||||
client = fake.NewSimpleClientset()
|
||||
pluginMgr = volume.VolumePluginMgr{}
|
||||
rootDir, host = newTestHost(t, client)
|
||||
)
|
||||
volumeSpec.Secret.Optional = &trueVal
|
||||
defer os.RemoveAll(rootDir)
|
||||
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
|
||||
|
||||
plugin, err := pluginMgr.FindPluginByName(secretPluginName)
|
||||
if err != nil {
|
||||
t.Errorf("Can't find the plugin by name")
|
||||
}
|
||||
|
||||
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, UID: testPodUID}}
|
||||
mounter, err := plugin.NewMounter(volume.NewSpecFromVolume(volumeSpec), pod, volume.VolumeOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Failed to make a new Mounter: %v", err)
|
||||
}
|
||||
if mounter == nil {
|
||||
t.Errorf("Got a nil Mounter")
|
||||
}
|
||||
|
||||
volumePath := mounter.GetPath()
|
||||
if !strings.HasSuffix(volumePath, fmt.Sprintf("pods/test_pod_uid/volumes/kubernetes.io~secret/test_volume_name")) {
|
||||
t.Errorf("Got unexpected path: %s", volumePath)
|
||||
}
|
||||
|
||||
err = mounter.SetUp(nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to setup volume: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(volumePath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
t.Errorf("SetUp() failed, volume path not created: %s", volumePath)
|
||||
} else {
|
||||
t.Errorf("SetUp() failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// secret volume should create its own empty wrapper path
|
||||
podWrapperMetadataDir := fmt.Sprintf("%v/pods/test_pod_uid/plugins/kubernetes.io~empty-dir/wrapped_test_volume_name", rootDir)
|
||||
|
||||
if _, err := os.Stat(podWrapperMetadataDir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
t.Errorf("SetUp() failed, empty-dir wrapper path is not created: %s", podWrapperMetadataDir)
|
||||
} else {
|
||||
t.Errorf("SetUp() failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
infos, err := ioutil.ReadDir(volumePath)
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't find volume path, %s", volumePath)
|
||||
}
|
||||
if len(infos) != 0 {
|
||||
t.Errorf("empty directory, %s, not found", volumePath)
|
||||
}
|
||||
|
||||
defer doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t)
|
||||
}
|
||||
|
||||
func TestPluginOptionalKeys(t *testing.T) {
|
||||
var (
|
||||
testPodUID = types.UID("test_pod_uid")
|
||||
testVolumeName = "test_volume_name"
|
||||
testNamespace = "test_secret_namespace"
|
||||
testName = "test_secret_name"
|
||||
trueVal = true
|
||||
|
||||
volumeSpec = volumeSpec(testVolumeName, testName, 0644)
|
||||
secret = secret(testNamespace, testName)
|
||||
client = fake.NewSimpleClientset(&secret)
|
||||
pluginMgr = volume.VolumePluginMgr{}
|
||||
rootDir, host = newTestHost(t, client)
|
||||
)
|
||||
volumeSpec.VolumeSource.Secret.Items = []v1.KeyToPath{
|
||||
{Key: "data-1", Path: "data-1"},
|
||||
{Key: "data-2", Path: "data-2"},
|
||||
{Key: "data-3", Path: "data-3"},
|
||||
{Key: "missing", Path: "missing"},
|
||||
}
|
||||
volumeSpec.Secret.Optional = &trueVal
|
||||
defer os.RemoveAll(rootDir)
|
||||
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
|
||||
|
||||
plugin, err := pluginMgr.FindPluginByName(secretPluginName)
|
||||
if err != nil {
|
||||
t.Errorf("Can't find the plugin by name")
|
||||
}
|
||||
|
||||
pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, UID: testPodUID}}
|
||||
mounter, err := plugin.NewMounter(volume.NewSpecFromVolume(volumeSpec), pod, volume.VolumeOptions{})
|
||||
if err != nil {
|
||||
t.Errorf("Failed to make a new Mounter: %v", err)
|
||||
}
|
||||
if mounter == nil {
|
||||
t.Errorf("Got a nil Mounter")
|
||||
}
|
||||
|
||||
volumePath := mounter.GetPath()
|
||||
if !strings.HasSuffix(volumePath, fmt.Sprintf("pods/test_pod_uid/volumes/kubernetes.io~secret/test_volume_name")) {
|
||||
t.Errorf("Got unexpected path: %s", volumePath)
|
||||
}
|
||||
|
||||
err = mounter.SetUp(nil)
|
||||
if err != nil {
|
||||
t.Errorf("Failed to setup volume: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(volumePath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
t.Errorf("SetUp() failed, volume path not created: %s", volumePath)
|
||||
} else {
|
||||
t.Errorf("SetUp() failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// secret volume should create its own empty wrapper path
|
||||
podWrapperMetadataDir := fmt.Sprintf("%v/pods/test_pod_uid/plugins/kubernetes.io~empty-dir/wrapped_test_volume_name", rootDir)
|
||||
|
||||
if _, err := os.Stat(podWrapperMetadataDir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
t.Errorf("SetUp() failed, empty-dir wrapper path is not created: %s", podWrapperMetadataDir)
|
||||
} else {
|
||||
t.Errorf("SetUp() failed: %v", err)
|
||||
}
|
||||
}
|
||||
doTestSecretDataInVolume(volumePath, secret, t)
|
||||
defer doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t)
|
||||
|
||||
// Metrics only supported on linux
|
||||
metrics, err := mounter.GetMetrics()
|
||||
if runtime.GOOS == "linux" {
|
||||
assert.NotEmpty(t, metrics)
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
t.Skipf("Volume metrics not supported on %s", runtime.GOOS)
|
||||
}
|
||||
}
|
||||
|
||||
func volumeSpec(volumeName, secretName string, defaultMode int32) *v1.Volume {
|
||||
return &v1.Volume{
|
||||
Name: volumeName,
|
||||
|
|
|
@ -19,6 +19,7 @@ package common
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -154,6 +155,189 @@ var _ = framework.KubeDescribe("ConfigMap", func() {
|
|||
Eventually(pollLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-2"))
|
||||
})
|
||||
|
||||
It("optional updates should be reflected in volume [Conformance] [Volume]", func() {
|
||||
|
||||
// We may have to wait or a full sync period to elapse before the
|
||||
// Kubelet projects the update into the volume and the container picks
|
||||
// it up. This timeout is based on the default Kubelet sync period (1
|
||||
// minute) plus additional time for fudge factor.
|
||||
const podLogTimeout = 300 * time.Second
|
||||
trueVal := true
|
||||
|
||||
volumeMountPath := "/etc/configmap-volumes"
|
||||
|
||||
deleteName := "cm-test-opt-del-" + string(uuid.NewUUID())
|
||||
deleteContainerName := "delcm-volume-test"
|
||||
deleteVolumeName := "deletecm-volume"
|
||||
deleteConfigMap := &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: f.Namespace.Name,
|
||||
Name: deleteName,
|
||||
},
|
||||
Data: map[string]string{
|
||||
"data-1": "value-1",
|
||||
},
|
||||
}
|
||||
|
||||
updateName := "cm-test-opt-upd-" + string(uuid.NewUUID())
|
||||
updateContainerName := "updcm-volume-test"
|
||||
updateVolumeName := "updatecm-volume"
|
||||
updateConfigMap := &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: f.Namespace.Name,
|
||||
Name: updateName,
|
||||
},
|
||||
Data: map[string]string{
|
||||
"data-1": "value-1",
|
||||
},
|
||||
}
|
||||
|
||||
createName := "cm-test-opt-create-" + string(uuid.NewUUID())
|
||||
createContainerName := "createcm-volume-test"
|
||||
createVolumeName := "createcm-volume"
|
||||
createConfigMap := &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: f.Namespace.Name,
|
||||
Name: createName,
|
||||
},
|
||||
Data: map[string]string{
|
||||
"data-1": "value-1",
|
||||
},
|
||||
}
|
||||
|
||||
By(fmt.Sprintf("Creating configMap with name %s", deleteConfigMap.Name))
|
||||
var err error
|
||||
if deleteConfigMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(deleteConfigMap); err != nil {
|
||||
framework.Failf("unable to create test configMap %s: %v", deleteConfigMap.Name, err)
|
||||
}
|
||||
|
||||
By(fmt.Sprintf("Creating configMap with name %s", updateConfigMap.Name))
|
||||
if updateConfigMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(updateConfigMap); err != nil {
|
||||
framework.Failf("unable to create test configMap %s: %v", updateConfigMap.Name, err)
|
||||
}
|
||||
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod-configmaps-" + string(uuid.NewUUID()),
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: deleteVolumeName,
|
||||
VolumeSource: v1.VolumeSource{
|
||||
ConfigMap: &v1.ConfigMapVolumeSource{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: deleteName,
|
||||
},
|
||||
Optional: &trueVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: updateVolumeName,
|
||||
VolumeSource: v1.VolumeSource{
|
||||
ConfigMap: &v1.ConfigMapVolumeSource{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: updateName,
|
||||
},
|
||||
Optional: &trueVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: createVolumeName,
|
||||
VolumeSource: v1.VolumeSource{
|
||||
ConfigMap: &v1.ConfigMapVolumeSource{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: createName,
|
||||
},
|
||||
Optional: &trueVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: deleteContainerName,
|
||||
Image: "gcr.io/google_containers/mounttest:0.7",
|
||||
Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/configmap-volumes/delete/data-1"},
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{
|
||||
Name: deleteVolumeName,
|
||||
MountPath: path.Join(volumeMountPath, "delete"),
|
||||
ReadOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: updateContainerName,
|
||||
Image: "gcr.io/google_containers/mounttest:0.7",
|
||||
Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/configmap-volumes/update/data-3"},
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{
|
||||
Name: updateVolumeName,
|
||||
MountPath: path.Join(volumeMountPath, "update"),
|
||||
ReadOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: createContainerName,
|
||||
Image: "gcr.io/google_containers/mounttest:0.7",
|
||||
Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/configmap-volumes/create/data-1"},
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{
|
||||
Name: createVolumeName,
|
||||
MountPath: path.Join(volumeMountPath, "create"),
|
||||
ReadOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RestartPolicy: v1.RestartPolicyNever,
|
||||
},
|
||||
}
|
||||
By("Creating the pod")
|
||||
f.PodClient().CreateSync(pod)
|
||||
|
||||
pollCreateLogs := func() (string, error) {
|
||||
return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, createContainerName)
|
||||
}
|
||||
Eventually(pollCreateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/configmap-volumes/create/data-1"))
|
||||
|
||||
pollUpdateLogs := func() (string, error) {
|
||||
return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, updateContainerName)
|
||||
}
|
||||
Eventually(pollUpdateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/configmap-volumes/update/data-3"))
|
||||
|
||||
pollDeleteLogs := func() (string, error) {
|
||||
return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, deleteContainerName)
|
||||
}
|
||||
Eventually(pollDeleteLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-1"))
|
||||
|
||||
By(fmt.Sprintf("Deleting configmap %v", deleteConfigMap.Name))
|
||||
err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Delete(deleteConfigMap.Name, &v1.DeleteOptions{})
|
||||
Expect(err).NotTo(HaveOccurred(), "Failed to delete configmap %q in namespace %q", deleteConfigMap.Name, f.Namespace.Name)
|
||||
|
||||
By(fmt.Sprintf("Updating configmap %v", updateConfigMap.Name))
|
||||
updateConfigMap.ResourceVersion = "" // to force update
|
||||
delete(updateConfigMap.Data, "data-1")
|
||||
updateConfigMap.Data["data-3"] = "value-3"
|
||||
_, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Update(updateConfigMap)
|
||||
Expect(err).NotTo(HaveOccurred(), "Failed to update configmap %q in namespace %q", updateConfigMap.Name, f.Namespace.Name)
|
||||
|
||||
By(fmt.Sprintf("Creating configMap with name %s", createConfigMap.Name))
|
||||
if createConfigMap, err = f.ClientSet.Core().ConfigMaps(f.Namespace.Name).Create(createConfigMap); err != nil {
|
||||
framework.Failf("unable to create test configMap %s: %v", createConfigMap.Name, err)
|
||||
}
|
||||
|
||||
By("waiting to observe update in volume")
|
||||
|
||||
Eventually(pollCreateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-1"))
|
||||
Eventually(pollUpdateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-3"))
|
||||
Eventually(pollDeleteLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/configmap-volumes/delete/data-1"))
|
||||
})
|
||||
|
||||
It("should be consumable via environment variable [Conformance]", func() {
|
||||
name := "configmap-test-" + string(uuid.NewUUID())
|
||||
configMap := newConfigMap(f, name)
|
||||
|
|
|
@ -19,6 +19,8 @@ package common
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
|
@ -26,6 +28,7 @@ import (
|
|||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = framework.KubeDescribe("Secrets", func() {
|
||||
|
@ -150,6 +153,183 @@ var _ = framework.KubeDescribe("Secrets", func() {
|
|||
})
|
||||
})
|
||||
|
||||
It("optional updates should be reflected in volume [Conformance] [Volume]", func() {
|
||||
|
||||
// We may have to wait or a full sync period to elapse before the
|
||||
// Kubelet projects the update into the volume and the container picks
|
||||
// it up. This timeout is based on the default Kubelet sync period (1
|
||||
// minute) plus additional time for fudge factor.
|
||||
const podLogTimeout = 300 * time.Second
|
||||
trueVal := true
|
||||
|
||||
volumeMountPath := "/etc/secret-volumes"
|
||||
|
||||
deleteName := "s-test-opt-del-" + string(uuid.NewUUID())
|
||||
deleteContainerName := "dels-volume-test"
|
||||
deleteVolumeName := "deletes-volume"
|
||||
deleteSecret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: f.Namespace.Name,
|
||||
Name: deleteName,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"data-1": []byte("value-1"),
|
||||
},
|
||||
}
|
||||
|
||||
updateName := "s-test-opt-upd-" + string(uuid.NewUUID())
|
||||
updateContainerName := "upds-volume-test"
|
||||
updateVolumeName := "updates-volume"
|
||||
updateSecret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: f.Namespace.Name,
|
||||
Name: updateName,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"data-1": []byte("value-1"),
|
||||
},
|
||||
}
|
||||
|
||||
createName := "s-test-opt-create-" + string(uuid.NewUUID())
|
||||
createContainerName := "creates-volume-test"
|
||||
createVolumeName := "creates-volume"
|
||||
createSecret := &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: f.Namespace.Name,
|
||||
Name: createName,
|
||||
},
|
||||
Data: map[string][]byte{
|
||||
"data-1": []byte("value-1"),
|
||||
},
|
||||
}
|
||||
|
||||
By(fmt.Sprintf("Creating secret with name %s", deleteSecret.Name))
|
||||
var err error
|
||||
if deleteSecret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(deleteSecret); err != nil {
|
||||
framework.Failf("unable to create test secret %s: %v", deleteSecret.Name, err)
|
||||
}
|
||||
|
||||
By(fmt.Sprintf("Creating secret with name %s", updateSecret.Name))
|
||||
if updateSecret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(updateSecret); err != nil {
|
||||
framework.Failf("unable to create test secret %s: %v", updateSecret.Name, err)
|
||||
}
|
||||
|
||||
pod := &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod-secrets-" + string(uuid.NewUUID()),
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Volumes: []v1.Volume{
|
||||
{
|
||||
Name: deleteVolumeName,
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Secret: &v1.SecretVolumeSource{
|
||||
SecretName: deleteName,
|
||||
Optional: &trueVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: updateVolumeName,
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Secret: &v1.SecretVolumeSource{
|
||||
SecretName: updateName,
|
||||
Optional: &trueVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: createVolumeName,
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Secret: &v1.SecretVolumeSource{
|
||||
SecretName: createName,
|
||||
Optional: &trueVal,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Name: deleteContainerName,
|
||||
Image: "gcr.io/google_containers/mounttest:0.7",
|
||||
Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/secret-volumes/delete/data-1"},
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{
|
||||
Name: deleteVolumeName,
|
||||
MountPath: path.Join(volumeMountPath, "delete"),
|
||||
ReadOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: updateContainerName,
|
||||
Image: "gcr.io/google_containers/mounttest:0.7",
|
||||
Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/secret-volumes/update/data-3"},
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{
|
||||
Name: updateVolumeName,
|
||||
MountPath: path.Join(volumeMountPath, "update"),
|
||||
ReadOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: createContainerName,
|
||||
Image: "gcr.io/google_containers/mounttest:0.7",
|
||||
Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/secret-volumes/create/data-1"},
|
||||
VolumeMounts: []v1.VolumeMount{
|
||||
{
|
||||
Name: createVolumeName,
|
||||
MountPath: path.Join(volumeMountPath, "create"),
|
||||
ReadOnly: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
RestartPolicy: v1.RestartPolicyNever,
|
||||
},
|
||||
}
|
||||
By("Creating the pod")
|
||||
f.PodClient().CreateSync(pod)
|
||||
|
||||
pollCreateLogs := func() (string, error) {
|
||||
return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, createContainerName)
|
||||
}
|
||||
Eventually(pollCreateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/secret-volumes/create/data-1"))
|
||||
|
||||
pollUpdateLogs := func() (string, error) {
|
||||
return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, updateContainerName)
|
||||
}
|
||||
Eventually(pollUpdateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/secret-volumes/update/data-3"))
|
||||
|
||||
pollDeleteLogs := func() (string, error) {
|
||||
return framework.GetPodLogs(f.ClientSet, f.Namespace.Name, pod.Name, deleteContainerName)
|
||||
}
|
||||
Eventually(pollDeleteLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-1"))
|
||||
|
||||
By(fmt.Sprintf("Deleting secret %v", deleteSecret.Name))
|
||||
err = f.ClientSet.Core().Secrets(f.Namespace.Name).Delete(deleteSecret.Name, &v1.DeleteOptions{})
|
||||
Expect(err).NotTo(HaveOccurred(), "Failed to delete secret %q in namespace %q", deleteSecret.Name, f.Namespace.Name)
|
||||
|
||||
By(fmt.Sprintf("Updating secret %v", updateSecret.Name))
|
||||
updateSecret.ResourceVersion = "" // to force update
|
||||
delete(updateSecret.Data, "data-1")
|
||||
updateSecret.Data["data-3"] = []byte("value-3")
|
||||
_, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Update(updateSecret)
|
||||
Expect(err).NotTo(HaveOccurred(), "Failed to update secret %q in namespace %q", updateSecret.Name, f.Namespace.Name)
|
||||
|
||||
By(fmt.Sprintf("Creating secret with name %s", createSecret.Name))
|
||||
if createSecret, err = f.ClientSet.Core().Secrets(f.Namespace.Name).Create(createSecret); err != nil {
|
||||
framework.Failf("unable to create test secret %s: %v", createSecret.Name, err)
|
||||
}
|
||||
|
||||
By("waiting to observe update in volume")
|
||||
|
||||
Eventually(pollCreateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-1"))
|
||||
Eventually(pollUpdateLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("value-3"))
|
||||
Eventually(pollDeleteLogs, podLogTimeout, framework.Poll).Should(ContainSubstring("Error reading file /etc/secret-volumes/delete/data-1"))
|
||||
})
|
||||
|
||||
It("should be consumable from pods in env vars [Conformance]", func() {
|
||||
name := "secret-test-" + string(uuid.NewUUID())
|
||||
secret := secretForTest(f.Namespace.Name, name)
|
||||
|
|
Loading…
Reference in New Issue