mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 20:53:33 +00:00
Allow Optional ConfigMap and Secrets
- ConfigMaps and Secrets for Env or Volumes are allowed to be optional
This commit is contained in:
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) {
|
func(s *api.SecretVolumeSource, c fuzz.Continue) {
|
||||||
c.FuzzNoCustom(s) // fuzz self without calling this function again
|
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
|
// DefaultMode should always be set, it has a default
|
||||||
// value and it is expected to be between 0 and 0777
|
// value and it is expected to be between 0 and 0777
|
||||||
var mode int32
|
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) {
|
func(cm *api.ConfigMapVolumeSource, c fuzz.Continue) {
|
||||||
c.FuzzNoCustom(cm) // fuzz self without calling this function again
|
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
|
// DefaultMode should always be set, it has a default
|
||||||
// value and it is expected to be between 0 and 0777
|
// value and it is expected to be between 0 and 0777
|
||||||
var mode int32
|
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) {
|
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
|
||||||
|
if c.RandBool() {
|
||||||
|
opt := c.RandBool()
|
||||||
|
cm.Optional = &opt
|
||||||
|
}
|
||||||
},
|
},
|
||||||
func(s *api.SecretEnvSource, c fuzz.Continue) {
|
func(s *api.SecretEnvSource, c fuzz.Continue) {
|
||||||
c.FuzzNoCustom(s) // fuzz self without calling this function again
|
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
|
// key and content is the value. If specified, the listed keys will be
|
||||||
// projected into the specified paths, and unlisted keys will not 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,
|
// 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 volume setup will error unless it is marked optional. Paths must be
|
||||||
// the '..' path or start with '..'.
|
// relative and may not contain the '..' path or start with '..'.
|
||||||
// +optional
|
// +optional
|
||||||
Items []KeyToPath
|
Items []KeyToPath
|
||||||
// Mode bits to use on created files by default. Must be a value between
|
// 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.
|
// mode, like fsGroup, and the result can be other mode bits set.
|
||||||
// +optional
|
// +optional
|
||||||
DefaultMode *int32
|
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.
|
// 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
|
// key and content is the value. If specified, the listed keys will be
|
||||||
// projected into the specified paths, and unlisted keys will not 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,
|
// 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 volume setup will error unless it is marked optional. Paths must be
|
||||||
// the '..' path or start with '..'.
|
// relative and may not contain the '..' path or start with '..'.
|
||||||
// +optional
|
// +optional
|
||||||
Items []KeyToPath
|
Items []KeyToPath
|
||||||
// Mode bits to use on created files by default. Must be a value between
|
// 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.
|
// mode, like fsGroup, and the result can be other mode bits set.
|
||||||
// +optional
|
// +optional
|
||||||
DefaultMode *int32
|
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.
|
// Maps a string key to a path within a volume.
|
||||||
@ -1124,6 +1130,9 @@ type ConfigMapKeySelector struct {
|
|||||||
LocalObjectReference
|
LocalObjectReference
|
||||||
// The key to select.
|
// The key to select.
|
||||||
Key string
|
Key string
|
||||||
|
// Specify whether the ConfigMap or it's key must be defined
|
||||||
|
// +optional
|
||||||
|
Optional *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecretKeySelector selects a key of a Secret.
|
// SecretKeySelector selects a key of a Secret.
|
||||||
@ -1132,6 +1141,9 @@ type SecretKeySelector struct {
|
|||||||
LocalObjectReference
|
LocalObjectReference
|
||||||
// The key of the secret to select from. Must be a valid secret key.
|
// The key of the secret to select from. Must be a valid secret key.
|
||||||
Key string
|
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
|
// EnvFromSource represents the source of a set of ConfigMaps
|
||||||
@ -1155,6 +1167,9 @@ type EnvFromSource struct {
|
|||||||
type ConfigMapEnvSource struct {
|
type ConfigMapEnvSource struct {
|
||||||
// The ConfigMap to select from.
|
// The ConfigMap to select from.
|
||||||
LocalObjectReference
|
LocalObjectReference
|
||||||
|
// Specify whether the ConfigMap must be defined
|
||||||
|
// +optional
|
||||||
|
Optional *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// SecretEnvSource selects a Secret to populate the environment
|
// SecretEnvSource selects a Secret to populate the environment
|
||||||
@ -1165,6 +1180,9 @@ type ConfigMapEnvSource struct {
|
|||||||
type SecretEnvSource struct {
|
type SecretEnvSource struct {
|
||||||
// The Secret to select from.
|
// The Secret to select from.
|
||||||
LocalObjectReference
|
LocalObjectReference
|
||||||
|
// Specify whether the Secret must be defined
|
||||||
|
// +optional
|
||||||
|
Optional *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// HTTPHeader describes a custom header to be used in HTTP probes
|
// 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
|
// key and content is the value. If specified, the listed keys will be
|
||||||
// projected into the specified paths, and unlisted keys will not 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,
|
// 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 volume setup will error unless it is marked optional. Paths must be
|
||||||
// the '..' path or start with '..'.
|
// relative and may not contain the '..' path or start with '..'.
|
||||||
// +optional
|
// +optional
|
||||||
Items []KeyToPath `json:"items,omitempty" protobuf:"bytes,2,rep,name=items"`
|
Items []KeyToPath `json:"items,omitempty" protobuf:"bytes,2,rep,name=items"`
|
||||||
// Optional: mode bits to use on created files by default. Must be a
|
// 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.
|
// mode, like fsGroup, and the result can be other mode bits set.
|
||||||
// +optional
|
// +optional
|
||||||
DefaultMode *int32 `json:"defaultMode,omitempty" protobuf:"bytes,3,opt,name=defaultMode"`
|
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 (
|
const (
|
||||||
@ -1081,8 +1084,8 @@ type ConfigMapVolumeSource struct {
|
|||||||
// key and content is the value. If specified, the listed keys will be
|
// key and content is the value. If specified, the listed keys will be
|
||||||
// projected into the specified paths, and unlisted keys will not 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,
|
// 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 volume setup will error unless it is marked optional. Paths must be
|
||||||
// the '..' path or start with '..'.
|
// relative and may not contain the '..' path or start with '..'.
|
||||||
// +optional
|
// +optional
|
||||||
Items []KeyToPath `json:"items,omitempty" protobuf:"bytes,2,rep,name=items"`
|
Items []KeyToPath `json:"items,omitempty" protobuf:"bytes,2,rep,name=items"`
|
||||||
// Optional: mode bits to use on created files by default. Must be a
|
// 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.
|
// mode, like fsGroup, and the result can be other mode bits set.
|
||||||
// +optional
|
// +optional
|
||||||
DefaultMode *int32 `json:"defaultMode,omitempty" protobuf:"varint,3,opt,name=defaultMode"`
|
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 (
|
const (
|
||||||
@ -1225,6 +1231,9 @@ type ConfigMapKeySelector struct {
|
|||||||
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
|
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
|
||||||
// The key to select.
|
// The key to select.
|
||||||
Key string `json:"key" protobuf:"bytes,2,opt,name=key"`
|
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.
|
// SecretKeySelector selects a key of a Secret.
|
||||||
@ -1233,6 +1242,9 @@ type SecretKeySelector struct {
|
|||||||
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
|
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
|
||||||
// The key of the secret to select from. Must be a valid secret key.
|
// The key of the secret to select from. Must be a valid secret key.
|
||||||
Key string `json:"key" protobuf:"bytes,2,opt,name=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
|
// EnvFromSource represents the source of a set of ConfigMaps
|
||||||
@ -1256,6 +1268,9 @@ type EnvFromSource struct {
|
|||||||
type ConfigMapEnvSource struct {
|
type ConfigMapEnvSource struct {
|
||||||
// The ConfigMap to select from.
|
// The ConfigMap to select from.
|
||||||
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
|
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
|
// SecretEnvSource selects a Secret to populate the environment
|
||||||
@ -1266,6 +1281,9 @@ type ConfigMapEnvSource struct {
|
|||||||
type SecretEnvSource struct {
|
type SecretEnvSource struct {
|
||||||
// The Secret to select from.
|
// The Secret to select from.
|
||||||
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
|
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
|
// 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) {
|
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"+
|
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) {
|
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"+
|
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) {
|
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)
|
w.Write(LEVEL_3, "%s:\t%s (%s)\n", e.Name, valueFrom, resource)
|
||||||
case e.ValueFrom.SecretKeyRef != nil:
|
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:
|
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 {
|
for _, e := range container.EnvFrom {
|
||||||
from := ""
|
from := ""
|
||||||
name := ""
|
name := ""
|
||||||
|
optional := false
|
||||||
if e.ConfigMapRef != nil {
|
if e.ConfigMapRef != nil {
|
||||||
from = "ConfigMap"
|
from = "ConfigMap"
|
||||||
name = e.ConfigMapRef.Name
|
name = e.ConfigMapRef.Name
|
||||||
|
optional = e.ConfigMapRef.Optional != nil && *e.ConfigMapRef.Optional
|
||||||
} else if e.SecretRef != nil {
|
} else if e.SecretRef != nil {
|
||||||
from = "Secret"
|
from = "Secret"
|
||||||
name = e.SecretRef.Name
|
name = e.SecretRef.Name
|
||||||
|
optional = e.SecretRef.Optional != nil && *e.SecretRef.Optional
|
||||||
}
|
}
|
||||||
if len(e.Prefix) == 0 {
|
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 {
|
} 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) {
|
func TestDescribeContainers(t *testing.T) {
|
||||||
|
trueVal := true
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
container api.Container
|
container api.Container
|
||||||
status api.ContainerStatus
|
status api.ContainerStatus
|
||||||
@ -295,7 +296,7 @@ func TestDescribeContainers(t *testing.T) {
|
|||||||
Ready: true,
|
Ready: true,
|
||||||
RestartCount: 7,
|
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"}}}}},
|
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,
|
Ready: true,
|
||||||
RestartCount: 7,
|
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{
|
status: api.ContainerStatus{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Ready: true,
|
Ready: true,
|
||||||
RestartCount: 7,
|
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"}}}}},
|
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,
|
Ready: true,
|
||||||
RestartCount: 7,
|
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
|
// Command
|
||||||
{
|
{
|
||||||
|
@ -33,6 +33,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
@ -427,14 +428,20 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
|
|||||||
for _, envFrom := range container.EnvFrom {
|
for _, envFrom := range container.EnvFrom {
|
||||||
switch {
|
switch {
|
||||||
case envFrom.ConfigMapRef != nil:
|
case envFrom.ConfigMapRef != nil:
|
||||||
name := envFrom.ConfigMapRef.Name
|
cm := envFrom.ConfigMapRef
|
||||||
|
name := cm.Name
|
||||||
configMap, ok := configMaps[name]
|
configMap, ok := configMaps[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
if kl.kubeClient == nil {
|
if kl.kubeClient == nil {
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
optional := cm.Optional != nil && *cm.Optional
|
||||||
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 {
|
||||||
|
if errors.IsNotFound(err) && optional {
|
||||||
|
// ignore error when marked optional
|
||||||
|
continue
|
||||||
|
}
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
configMaps[name] = configMap
|
configMaps[name] = configMap
|
||||||
@ -450,14 +457,20 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
|
|||||||
tmpEnv[k] = v
|
tmpEnv[k] = v
|
||||||
}
|
}
|
||||||
case envFrom.SecretRef != nil:
|
case envFrom.SecretRef != nil:
|
||||||
name := envFrom.SecretRef.Name
|
s := envFrom.SecretRef
|
||||||
|
name := s.Name
|
||||||
secret, ok := secrets[name]
|
secret, ok := secrets[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
if kl.kubeClient == nil {
|
if kl.kubeClient == nil {
|
||||||
return result, fmt.Errorf("Couldn't get secret %v/%v, no kubeClient defined", pod.Namespace, name)
|
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{})
|
secret, err = kl.kubeClient.Core().Secrets(pod.Namespace).Get(name, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) && optional {
|
||||||
|
// ignore error when marked optional
|
||||||
|
continue
|
||||||
|
}
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
secrets[name] = secret
|
secrets[name] = secret
|
||||||
@ -510,8 +523,10 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *v1.Pod, container *v1.Container
|
|||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
case envVar.ValueFrom.ConfigMapKeyRef != nil:
|
case envVar.ValueFrom.ConfigMapKeyRef != nil:
|
||||||
name := envVar.ValueFrom.ConfigMapKeyRef.Name
|
cm := envVar.ValueFrom.ConfigMapKeyRef
|
||||||
key := envVar.ValueFrom.ConfigMapKeyRef.Key
|
name := cm.Name
|
||||||
|
key := cm.Key
|
||||||
|
optional := cm.Optional != nil && *cm.Optional
|
||||||
configMap, ok := configMaps[name]
|
configMap, ok := configMaps[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
if kl.kubeClient == nil {
|
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{})
|
configMap, err = kl.kubeClient.Core().ConfigMaps(pod.Namespace).Get(name, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) && optional {
|
||||||
|
// ignore error when marked optional
|
||||||
|
continue
|
||||||
|
}
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
configMaps[name] = configMap
|
configMaps[name] = configMap
|
||||||
}
|
}
|
||||||
runtimeVal, ok = configMap.Data[key]
|
runtimeVal, ok = configMap.Data[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
if optional {
|
||||||
|
continue
|
||||||
|
}
|
||||||
return result, fmt.Errorf("Couldn't find key %v in ConfigMap %v/%v", key, pod.Namespace, name)
|
return result, fmt.Errorf("Couldn't find key %v in ConfigMap %v/%v", key, pod.Namespace, name)
|
||||||
}
|
}
|
||||||
case envVar.ValueFrom.SecretKeyRef != nil:
|
case envVar.ValueFrom.SecretKeyRef != nil:
|
||||||
name := envVar.ValueFrom.SecretKeyRef.Name
|
s := envVar.ValueFrom.SecretKeyRef
|
||||||
key := envVar.ValueFrom.SecretKeyRef.Key
|
name := s.Name
|
||||||
|
key := s.Key
|
||||||
|
optional := s.Optional != nil && *s.Optional
|
||||||
secret, ok := secrets[name]
|
secret, ok := secrets[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
if kl.kubeClient == nil {
|
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)
|
secret, err = kl.secretManager.GetSecret(pod.Namespace, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if errors.IsNotFound(err) && optional {
|
||||||
|
// ignore error when marked optional
|
||||||
|
continue
|
||||||
|
}
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
secrets[name] = secret
|
secrets[name] = secret
|
||||||
}
|
}
|
||||||
runtimeValBytes, ok := secret.Data[key]
|
runtimeValBytes, ok := secret.Data[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
if optional {
|
||||||
|
continue
|
||||||
|
}
|
||||||
return result, fmt.Errorf("Couldn't find key %v in Secret %v/%v", key, pod.Namespace, name)
|
return result, fmt.Errorf("Couldn't find key %v in Secret %v/%v", key, pod.Namespace, name)
|
||||||
}
|
}
|
||||||
runtimeVal = string(runtimeValBytes)
|
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
|
tmpEnv[envVar.Name] = runtimeVal
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"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) {
|
func TestMakeEnvironmentVariables(t *testing.T) {
|
||||||
|
trueVal := true
|
||||||
services := []*v1.Service{
|
services := []*v1.Service{
|
||||||
buildService("kubernetes", v1.NamespaceDefault, "1.2.3.1", "TCP", 8081),
|
buildService("kubernetes", v1.NamespaceDefault, "1.2.3.1", "TCP", 8081),
|
||||||
buildService("test", "test1", "1.2.3.3", "TCP", 8083),
|
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",
|
name: "configmap",
|
||||||
ns: "test1",
|
ns: "test1",
|
||||||
@ -722,6 +824,19 @@ func TestMakeEnvironmentVariables(t *testing.T) {
|
|||||||
masterServiceNs: "nothing",
|
masterServiceNs: "nothing",
|
||||||
expectedError: true,
|
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",
|
name: "configmap_invalid_keys",
|
||||||
ns: "test1",
|
ns: "test1",
|
||||||
@ -876,6 +991,19 @@ func TestMakeEnvironmentVariables(t *testing.T) {
|
|||||||
masterServiceNs: "nothing",
|
masterServiceNs: "nothing",
|
||||||
expectedError: true,
|
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",
|
name: "secret_invalid_keys",
|
||||||
ns: "test1",
|
ns: "test1",
|
||||||
@ -940,10 +1068,17 @@ func TestMakeEnvironmentVariables(t *testing.T) {
|
|||||||
testKubelet.fakeKubeClient.AddReactor("get", "configmaps", func(action core.Action) (bool, runtime.Object, error) {
|
testKubelet.fakeKubeClient.AddReactor("get", "configmaps", func(action core.Action) (bool, runtime.Object, error) {
|
||||||
var err error
|
var err error
|
||||||
if tc.configMap == nil {
|
if tc.configMap == nil {
|
||||||
err = errors.New("no configmap defined")
|
err = apierrors.NewNotFound(action.GetResource().GroupResource(), "configmap-name")
|
||||||
}
|
}
|
||||||
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 = 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) {
|
testKubelet.fakeKubeClient.AddReactor("get", "secrets", func(action core.Action) (bool, runtime.Object, error) {
|
||||||
var err error
|
var err error
|
||||||
|
@ -173,8 +173,12 @@ func newTestKubeletWithImageList(
|
|||||||
kubelet.cadvisor = mockCadvisor
|
kubelet.cadvisor = mockCadvisor
|
||||||
|
|
||||||
fakeMirrorClient := podtest.NewFakeMirrorClient()
|
fakeMirrorClient := podtest.NewFakeMirrorClient()
|
||||||
fakeSecretManager := secret.NewFakeManager()
|
secretManager, err := secret.NewSimpleSecretManager(kubelet.kubeClient)
|
||||||
kubelet.podManager = kubepod.NewBasicPodManager(fakeMirrorClient, fakeSecretManager)
|
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.statusManager = status.NewManager(fakeKubeClient, kubelet.podManager)
|
||||||
kubelet.containerRefManager = kubecontainer.NewRefManager()
|
kubelet.containerRefManager = kubecontainer.NewRefManager()
|
||||||
diskSpaceManager, err := newDiskSpaceManager(mockCadvisor, DiskSpacePolicy{})
|
diskSpaceManager, err := newDiskSpaceManager(mockCadvisor, DiskSpacePolicy{})
|
||||||
@ -249,7 +253,7 @@ func newTestKubeletWithImageList(
|
|||||||
|
|
||||||
plug := &volumetest.FakeVolumePlugin{PluginName: "fake", Host: nil}
|
plug := &volumetest.FakeVolumePlugin{PluginName: "fake", Host: nil}
|
||||||
kubelet.volumePluginMgr, err =
|
kubelet.volumePluginMgr, err =
|
||||||
NewInitializedVolumePluginMgr(kubelet, fakeSecretManager, []volume.VolumePlugin{plug})
|
NewInitializedVolumePluginMgr(kubelet, kubelet.secretManager, []volume.VolumePlugin{plug})
|
||||||
require.NoError(t, err, "Failed to initialize VolumePluginMgr")
|
require.NoError(t, err, "Failed to initialize VolumePluginMgr")
|
||||||
|
|
||||||
kubelet.mounter = &mount.FakeMounter{}
|
kubelet.mounter = &mount.FakeMounter{}
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/kubernetes/pkg/api/v1"
|
"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)
|
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{})
|
configMap, err := kubeClient.Core().ConfigMaps(b.pod.Namespace).Get(b.source.Name, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("Couldn't get configMap %v/%v: %v", b.pod.Namespace, b.source.Name, err)
|
if !(errors.IsNotFound(err) && optional) {
|
||||||
return err
|
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)
|
totalBytes := totalBytes(configMap)
|
||||||
@ -183,7 +193,7 @@ func (b *configMapVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||||||
len(configMap.Data),
|
len(configMap.Data),
|
||||||
totalBytes)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -210,7 +220,7 @@ func (b *configMapVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||||||
return nil
|
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 {
|
if defaultMode == nil {
|
||||||
return nil, fmt.Errorf("No defaultMode used, not even the default value for it")
|
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 {
|
for _, ktp := range mappings {
|
||||||
content, ok := configMap.Data[ktp.Key]
|
content, ok := configMap.Data[ktp.Key]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
if optional {
|
||||||
|
continue
|
||||||
|
}
|
||||||
err_msg := "references non-existent config key"
|
err_msg := "references non-existent config key"
|
||||||
glog.Errorf(err_msg)
|
glog.Errorf(err_msg)
|
||||||
return nil, fmt.Errorf(err_msg)
|
return nil, fmt.Errorf(err_msg)
|
||||||
|
@ -43,6 +43,7 @@ func TestMakePayload(t *testing.T) {
|
|||||||
mappings []v1.KeyToPath
|
mappings []v1.KeyToPath
|
||||||
configMap *v1.ConfigMap
|
configMap *v1.ConfigMap
|
||||||
mode int32
|
mode int32
|
||||||
|
optional bool
|
||||||
payload map[string]util.FileProjection
|
payload map[string]util.FileProjection
|
||||||
success bool
|
success bool
|
||||||
}{
|
}{
|
||||||
@ -215,10 +216,29 @@ func TestMakePayload(t *testing.T) {
|
|||||||
},
|
},
|
||||||
success: true,
|
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 {
|
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 {
|
if err != nil && tc.success {
|
||||||
t.Errorf("%v: unexpected failure making payload: %v", tc.name, err)
|
t.Errorf("%v: unexpected failure making payload: %v", tc.name, err)
|
||||||
continue
|
continue
|
||||||
@ -388,6 +408,143 @@ func TestPluginReboot(t *testing.T) {
|
|||||||
doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, 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 {
|
func volumeSpec(volumeName, configMapName string, defaultMode int32) *v1.Volume {
|
||||||
return &v1.Volume{
|
return &v1.Volume{
|
||||||
Name: volumeName,
|
Name: volumeName,
|
||||||
|
@ -22,6 +22,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"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/apimachinery/pkg/types"
|
||||||
"k8s.io/kubernetes/pkg/api/v1"
|
"k8s.io/kubernetes/pkg/api/v1"
|
||||||
ioutil "k8s.io/kubernetes/pkg/util/io"
|
ioutil "k8s.io/kubernetes/pkg/util/io"
|
||||||
@ -191,10 +193,19 @@ func (b *secretVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
optional := b.source.Optional != nil && *b.source.Optional
|
||||||
secret, err := b.getSecret(b.pod.Namespace, b.source.SecretName)
|
secret, err := b.getSecret(b.pod.Namespace, b.source.SecretName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("Couldn't get secret %v/%v", b.pod.Namespace, b.source.SecretName)
|
if !(errors.IsNotFound(err) && optional) {
|
||||||
return err
|
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)
|
totalBytes := totalSecretBytes(secret)
|
||||||
@ -204,7 +215,7 @@ func (b *secretVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||||||
len(secret.Data),
|
len(secret.Data),
|
||||||
totalBytes)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -231,7 +242,7 @@ func (b *secretVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||||||
return nil
|
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 {
|
if defaultMode == nil {
|
||||||
return nil, fmt.Errorf("No defaultMode used, not even the default value for it")
|
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 {
|
for _, ktp := range mappings {
|
||||||
content, ok := secret.Data[ktp.Key]
|
content, ok := secret.Data[ktp.Key]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
if optional {
|
||||||
|
continue
|
||||||
|
}
|
||||||
err_msg := "references non-existent secret key"
|
err_msg := "references non-existent secret key"
|
||||||
glog.Errorf(err_msg)
|
glog.Errorf(err_msg)
|
||||||
return nil, fmt.Errorf(err_msg)
|
return nil, fmt.Errorf(err_msg)
|
||||||
|
@ -46,6 +46,7 @@ func TestMakePayload(t *testing.T) {
|
|||||||
mappings []v1.KeyToPath
|
mappings []v1.KeyToPath
|
||||||
secret *v1.Secret
|
secret *v1.Secret
|
||||||
mode int32
|
mode int32
|
||||||
|
optional bool
|
||||||
payload map[string]util.FileProjection
|
payload map[string]util.FileProjection
|
||||||
success bool
|
success bool
|
||||||
}{
|
}{
|
||||||
@ -218,10 +219,29 @@ func TestMakePayload(t *testing.T) {
|
|||||||
},
|
},
|
||||||
success: true,
|
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 {
|
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 {
|
if err != nil && tc.success {
|
||||||
t.Errorf("%v: unexpected failure making payload: %v", tc.name, err)
|
t.Errorf("%v: unexpected failure making payload: %v", tc.name, err)
|
||||||
continue
|
continue
|
||||||
@ -398,6 +418,154 @@ func TestPluginReboot(t *testing.T) {
|
|||||||
doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, 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 {
|
func volumeSpec(volumeName, secretName string, defaultMode int32) *v1.Volume {
|
||||||
return &v1.Volume{
|
return &v1.Volume{
|
||||||
Name: volumeName,
|
Name: volumeName,
|
||||||
|
@ -19,6 +19,7 @@ package common
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
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"))
|
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() {
|
It("should be consumable via environment variable [Conformance]", func() {
|
||||||
name := "configmap-test-" + string(uuid.NewUUID())
|
name := "configmap-test-" + string(uuid.NewUUID())
|
||||||
configMap := newConfigMap(f, name)
|
configMap := newConfigMap(f, name)
|
||||||
|
@ -19,6 +19,8 @@ package common
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/kubernetes/pkg/api/v1"
|
"k8s.io/kubernetes/pkg/api/v1"
|
||||||
@ -26,6 +28,7 @@ import (
|
|||||||
"k8s.io/kubernetes/test/e2e/framework"
|
"k8s.io/kubernetes/test/e2e/framework"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = framework.KubeDescribe("Secrets", func() {
|
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() {
|
It("should be consumable from pods in env vars [Conformance]", func() {
|
||||||
name := "secret-test-" + string(uuid.NewUUID())
|
name := "secret-test-" + string(uuid.NewUUID())
|
||||||
secret := secretForTest(f.Namespace.Name, name)
|
secret := secretForTest(f.Namespace.Name, name)
|
||||||
|
Loading…
Reference in New Issue
Block a user