Add mode permission bits to configmap, secrets and downwardAPI

This implements the proposal in:
docs/proposals/secret-configmap-downwarapi-file-mode.md

Fixes: #28317.

The mounttest image is updated so it returns the permissions of the linked file
and not the symlink itself.
This commit is contained in:
Rodrigo Campos 2016-07-10 21:48:28 -03:00 committed by Paul Morie
parent 2bc5414de6
commit 568f4c2e63
19 changed files with 1306 additions and 313 deletions

View File

@ -251,15 +251,59 @@ func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source)
policies := []api.RestartPolicy{api.RestartPolicyAlways, api.RestartPolicyNever, api.RestartPolicyOnFailure}
*rp = policies[c.Rand.Intn(len(policies))]
},
// Only api.DownwardAPIVolumeFile needs to have a specific func since FieldRef has to be
// api.DownwardAPIVolumeFile needs to have a specific func since FieldRef has to be
// defaulted to a version otherwise roundtrip will fail
// For the remaining volume plugins the default fuzzer is enough.
func(m *api.DownwardAPIVolumeFile, c fuzz.Continue) {
m.Path = c.RandString()
versions := []string{"v1"}
m.FieldRef = &api.ObjectFieldSelector{}
m.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))]
m.FieldRef.FieldPath = c.RandString()
c.Fuzz(m.Mode)
if m.Mode != nil {
*m.Mode &= 0777
}
},
func(s *api.SecretVolumeSource, c fuzz.Continue) {
c.FuzzNoCustom(s) // fuzz self without calling this function again
// DefaultMode should always be set, it has a default
// value and it is expected to be between 0 and 0777
var mode int32
c.Fuzz(&mode)
mode &= 0777
s.DefaultMode = &mode
},
func(cm *api.ConfigMapVolumeSource, c fuzz.Continue) {
c.FuzzNoCustom(cm) // fuzz self without calling this function again
// DefaultMode should always be set, it has a default
// value and it is expected to be between 0 and 0777
var mode int32
c.Fuzz(&mode)
mode &= 0777
cm.DefaultMode = &mode
},
func(d *api.DownwardAPIVolumeSource, c fuzz.Continue) {
c.FuzzNoCustom(d) // fuzz self without calling this function again
// DefaultMode should always be set, it has a default
// value and it is expected to be between 0 and 0777
var mode int32
c.Fuzz(&mode)
mode &= 0777
d.DefaultMode = &mode
},
func(k *api.KeyToPath, c fuzz.Continue) {
c.FuzzNoCustom(k) // fuzz self without calling this function again
k.Key = c.RandString()
k.Path = c.RandString()
// Mode is not mandatory, but if it is set, it should be
// a value between 0 and 0777
if k.Mode != nil {
*k.Mode &= 0777
}
},
func(vs *api.VolumeSource, c fuzz.Continue) {
// Exactly one of the fields must be set.

View File

@ -607,6 +607,12 @@ type SecretVolumeSource struct {
// the volume setup will error. Paths must be relative and may not contain
// the '..' path or start with '..'.
Items []KeyToPath `json:"items,omitempty"`
// Mode bits to use on created files by default. Must be a value between
// 0 and 0777.
// Directories within the path are not affected by this setting.
// This might be in conflict with other options that affect the file
// mode, like fsGroup, and the result can be other mode bits set.
DefaultMode *int32 `json:"defaultMode,omitempty"`
}
// Represents an NFS mount that lasts the lifetime of a pod.
@ -708,6 +714,12 @@ type FlockerVolumeSource struct {
type DownwardAPIVolumeSource struct {
// Items is a list of DownwardAPIVolume file
Items []DownwardAPIVolumeFile `json:"items,omitempty"`
// Mode bits to use on created files by default. Must be a value between
// 0 and 0777.
// Directories within the path are not affected by this setting.
// This might be in conflict with other options that affect the file
// mode, like fsGroup, and the result can be other mode bits set.
DefaultMode *int32 `json:"defaultMode,omitempty"`
}
// Represents a single file containing information from the downward API
@ -719,6 +731,11 @@ type DownwardAPIVolumeFile struct {
// Selects a resource of the container: only resources limits and requests
// (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.
ResourceFieldRef *ResourceFieldSelector `json:"resourceFieldRef,omitempty"`
// Optional: mode bits to use on this file, must be a value between 0
// and 0777. If not specified, the volume defaultMode will be used.
// This might be in conflict with other options that affect the file
// mode, like fsGroup, and the result can be other mode bits set.
Mode *int32 `json:"mode,omitempty"`
}
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
@ -758,6 +775,12 @@ type ConfigMapVolumeSource struct {
// the volume setup will error. Paths must be relative and may not contain
// the '..' path or start with '..'.
Items []KeyToPath `json:"items,omitempty"`
// Mode bits to use on created files by default. Must be a value between
// 0 and 0777.
// Directories within the path are not affected by this setting.
// This might be in conflict with other options that affect the file
// mode, like fsGroup, and the result can be other mode bits set.
DefaultMode *int32 `json:"defaultMode,omitempty"`
}
// Maps a string key to a path within a volume.
@ -770,6 +793,11 @@ type KeyToPath struct {
// May not contain the path element '..'.
// May not start with the string '..'.
Path string `json:"path"`
// Optional: mode bits to use on this file, should be a value between 0
// and 0777. If not specified, the volume defaultMode will be used.
// This might be in conflict with other options that affect the file
// mode, like fsGroup, and the result can be other mode bits set.
Mode *int32 `json:"mode,omitempty"`
}
// ContainerPort represents a network port in a single container

View File

@ -35,6 +35,9 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error {
SetDefaults_Pod,
SetDefaults_PodSpec,
SetDefaults_Probe,
SetDefaults_SecretVolumeSource,
SetDefaults_ConfigMapVolumeSource,
SetDefaults_DownwardAPIVolumeSource,
SetDefaults_Secret,
SetDefaults_PersistentVolume,
SetDefaults_PersistentVolumeClaim,
@ -174,6 +177,24 @@ func SetDefaults_Probe(obj *Probe) {
obj.FailureThreshold = 3
}
}
func SetDefaults_SecretVolumeSource(obj *SecretVolumeSource) {
if obj.DefaultMode == nil {
perm := int32(SecretVolumeSourceDefaultMode)
obj.DefaultMode = &perm
}
}
func SetDefaults_ConfigMapVolumeSource(obj *ConfigMapVolumeSource) {
if obj.DefaultMode == nil {
perm := int32(ConfigMapVolumeSourceDefaultMode)
obj.DefaultMode = &perm
}
}
func SetDefaults_DownwardAPIVolumeSource(obj *DownwardAPIVolumeSource) {
if obj.DefaultMode == nil {
perm := int32(DownwardAPIVolumeSourceDefaultMode)
obj.DefaultMode = &perm
}
}
func SetDefaults_Secret(obj *Secret) {
if obj.Type == "" {
obj.Type = SecretTypeOpaque

View File

@ -242,6 +242,72 @@ func TestSetDefaultService(t *testing.T) {
}
}
func TestSetDefaultSecretVolumeSource(t *testing.T) {
s := versioned.PodSpec{}
s.Volumes = []versioned.Volume{
{
VolumeSource: versioned.VolumeSource{
Secret: &versioned.SecretVolumeSource{},
},
},
}
pod := &versioned.Pod{
Spec: s,
}
output := roundTrip(t, runtime.Object(pod))
pod2 := output.(*versioned.Pod)
defaultMode := pod2.Spec.Volumes[0].VolumeSource.Secret.DefaultMode
expectedMode := versioned.SecretVolumeSourceDefaultMode
if defaultMode == nil || *defaultMode != expectedMode {
t.Errorf("Expected secret DefaultMode %v, got %v", expectedMode, defaultMode)
}
}
func TestSetDefaultConfigMapVolumeSource(t *testing.T) {
s := versioned.PodSpec{}
s.Volumes = []versioned.Volume{
{
VolumeSource: versioned.VolumeSource{
ConfigMap: &versioned.ConfigMapVolumeSource{},
},
},
}
pod := &versioned.Pod{
Spec: s,
}
output := roundTrip(t, runtime.Object(pod))
pod2 := output.(*versioned.Pod)
defaultMode := pod2.Spec.Volumes[0].VolumeSource.ConfigMap.DefaultMode
expectedMode := versioned.ConfigMapVolumeSourceDefaultMode
if defaultMode == nil || *defaultMode != expectedMode {
t.Errorf("Expected ConfigMap DefaultMode %v, got %v", expectedMode, defaultMode)
}
}
func TestSetDefaultDownwardAPIVolumeSource(t *testing.T) {
s := versioned.PodSpec{}
s.Volumes = []versioned.Volume{
{
VolumeSource: versioned.VolumeSource{
DownwardAPI: &versioned.DownwardAPIVolumeSource{},
},
},
}
pod := &versioned.Pod{
Spec: s,
}
output := roundTrip(t, runtime.Object(pod))
pod2 := output.(*versioned.Pod)
defaultMode := pod2.Spec.Volumes[0].VolumeSource.DownwardAPI.DefaultMode
expectedMode := versioned.DownwardAPIVolumeSourceDefaultMode
if defaultMode == nil || *defaultMode != expectedMode {
t.Errorf("Expected DownwardAPI DefaultMode %v, got %v", expectedMode, defaultMode)
}
}
func TestSetDefaultSecret(t *testing.T) {
s := &versioned.Secret{}
obj2 := roundTrip(t, runtime.Object(s))

View File

@ -770,8 +770,18 @@ type SecretVolumeSource struct {
// the volume setup will error. Paths must be relative and may not contain
// the '..' path or start with '..'.
Items []KeyToPath `json:"items,omitempty" protobuf:"bytes,2,rep,name=items"`
// Optional: mode bits to use on created files by default. Must be a
// value between 0 and 0777. Defaults to 0644.
// Directories within the path are not affected by this setting.
// This might be in conflict with other options that affect the file
// mode, like fsGroup, and the result can be other mode bits set.
DefaultMode *int32 `json:"defaultMode,omitempty" protobuf:"bytes,3,opt,name=defaultMode"`
}
const (
SecretVolumeSourceDefaultMode int32 = 0644
)
// Represents an NFS mount that lasts the lifetime of a pod.
// NFS volumes do not support ownership management or SELinux relabeling.
type NFSVolumeSource struct {
@ -869,8 +879,18 @@ type ConfigMapVolumeSource struct {
// the volume setup will error. Paths must be relative and may not contain
// the '..' path or start with '..'.
Items []KeyToPath `json:"items,omitempty" protobuf:"bytes,2,rep,name=items"`
// Optional: mode bits to use on created files by default. Must be a
// value between 0 and 0777. Defaults to 0644.
// Directories within the path are not affected by this setting.
// This might be in conflict with other options that affect the file
// mode, like fsGroup, and the result can be other mode bits set.
DefaultMode *int32 `json:"defaultMode,omitempty" protobuf:"varint,3,opt,name=defaultMode"`
}
const (
ConfigMapVolumeSourceDefaultMode int32 = 0644
)
// Maps a string key to a path within a volume.
type KeyToPath struct {
// The key to project.
@ -881,6 +901,11 @@ type KeyToPath struct {
// May not contain the path element '..'.
// May not start with the string '..'.
Path string `json:"path" protobuf:"bytes,2,opt,name=path"`
// Optional: mode bits to use on this file, must be a value between 0
// and 0777. If not specified, the volume defaultMode will be used.
// This might be in conflict with other options that affect the file
// mode, like fsGroup, and the result can be other mode bits set.
Mode *int32 `json:"mode,omitempty" protobuf:"varint,3,opt,name=mode"`
}
// ContainerPort represents a network port in a single container.
@ -3287,8 +3312,18 @@ type ComponentStatusList struct {
type DownwardAPIVolumeSource struct {
// Items is a list of downward API volume file
Items []DownwardAPIVolumeFile `json:"items,omitempty" protobuf:"bytes,1,rep,name=items"`
// Optional: mode bits to use on created files by default. Must be a
// value between 0 and 0777. Defaults to 0644.
// Directories within the path are not affected by this setting.
// This might be in conflict with other options that affect the file
// mode, like fsGroup, and the result can be other mode bits set.
DefaultMode *int32 `json:"defaultMode,omitempty" protobuf:"varint,2,opt,name=defaultMode"`
}
const (
DownwardAPIVolumeSourceDefaultMode int32 = 0644
)
// DownwardAPIVolumeFile represents information to create the file containing the pod field
type DownwardAPIVolumeFile struct {
// Required: Path is the relative path name of the file to be created. Must not be absolute or contain the '..' path. Must be utf-8 encoded. The first item of the relative path must not start with '..'
@ -3298,6 +3333,11 @@ type DownwardAPIVolumeFile struct {
// Selects a resource of the container: only resources limits and requests
// (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.
ResourceFieldRef *ResourceFieldSelector `json:"resourceFieldRef,omitempty" protobuf:"bytes,3,opt,name=resourceFieldRef"`
// Optional: mode bits to use on this file, must be a value between 0
// and 0777. If not specified, the volume defaultMode will be used.
// This might be in conflict with other options that affect the file
// mode, like fsGroup, and the result can be other mode bits set.
Mode *int32 `json:"mode,omitempty" protobuf:"varint,4,opt,name=mode"`
}
// SecurityContext holds security configuration that will be applied to a container.

View File

@ -52,6 +52,7 @@ const fieldImmutableErrorMsg string = `field is immutable`
const isNotIntegerErrorMsg string = `must be an integer`
var pdPartitionErrorMsg string = validation.InclusiveRangeError(1, 255)
var volumeModeErrorMsg string = "must be a number between 0 and 0777 (octal), both inclusive"
const totalAnnotationSizeLimitB int = 256 * (1 << 10) // 256 kB
@ -660,6 +661,12 @@ func validateSecretVolumeSource(secretSource *api.SecretVolumeSource, fldPath *f
if len(secretSource.SecretName) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("secretName"), ""))
}
secretMode := secretSource.DefaultMode
if secretMode != nil && (*secretMode > 0777 || *secretMode < 0) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *secretMode, volumeModeErrorMsg))
}
itemsPath := fldPath.Child("items")
for i, kp := range secretSource.Items {
itemPath := itemsPath.Index(i)
@ -673,6 +680,12 @@ func validateConfigMapVolumeSource(configMapSource *api.ConfigMapVolumeSource, f
if len(configMapSource.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
}
configMapMode := configMapSource.DefaultMode
if configMapMode != nil && (*configMapMode > 0777 || *configMapMode < 0) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *configMapMode, volumeModeErrorMsg))
}
itemsPath := fldPath.Child("items")
for i, kp := range configMapSource.Items {
itemPath := itemsPath.Index(i)
@ -690,6 +703,10 @@ func validateKeyToPath(kp *api.KeyToPath, fldPath *field.Path) field.ErrorList {
allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
}
allErrs = append(allErrs, validateLocalNonReservedPath(kp.Path, fldPath.Child("path"))...)
if kp.Mode != nil && (*kp.Mode > 0777 || *kp.Mode < 0) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("mode"), *kp.Mode, volumeModeErrorMsg))
}
return allErrs
}
@ -745,6 +762,12 @@ var validDownwardAPIFieldPathExpressions = sets.NewString(
func validateDownwardAPIVolumeSource(downwardAPIVolume *api.DownwardAPIVolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
downwardAPIMode := downwardAPIVolume.DefaultMode
if downwardAPIMode != nil && (*downwardAPIMode > 0777 || *downwardAPIMode < 0) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *downwardAPIMode, volumeModeErrorMsg))
}
for _, file := range downwardAPIVolume.Items {
if len(file.Path) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
@ -760,6 +783,9 @@ func validateDownwardAPIVolumeSource(downwardAPIVolume *api.DownwardAPIVolumeSou
} else {
allErrs = append(allErrs, field.Required(fldPath, "one of fieldRef and resourceFieldRef is required"))
}
if file.Mode != nil && (*file.Mode > 0777 || *file.Mode < 0) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("mode"), *file.Mode, volumeModeErrorMsg))
}
}
return allErrs
}

View File

@ -794,6 +794,10 @@ func TestValidateKeyToPath(t *testing.T) {
kp: api.KeyToPath{Key: "k", Path: "p/..p/p../p..p"},
ok: true,
},
{
kp: api.KeyToPath{Key: "k", Path: "p", Mode: newInt32(0644)},
ok: true,
},
{
kp: api.KeyToPath{Key: "", Path: "p"},
ok: false,
@ -824,6 +828,16 @@ func TestValidateKeyToPath(t *testing.T) {
ok: false,
errtype: field.ErrorTypeInvalid,
},
{
kp: api.KeyToPath{Key: "k", Path: "p", Mode: newInt32(01000)},
ok: false,
errtype: field.ErrorTypeInvalid,
},
{
kp: api.KeyToPath{Key: "k", Path: "p", Mode: newInt32(-1)},
ok: false,
errtype: field.ErrorTypeInvalid,
},
}
for i, tc := range testCases {
@ -1129,7 +1143,19 @@ func TestValidateVolumes(t *testing.T) {
},
},
{
name: "valid Secret with projection",
name: "valid Secret with defaultMode",
vol: api.Volume{
Name: "secret",
VolumeSource: api.VolumeSource{
Secret: &api.SecretVolumeSource{
SecretName: "my-secret",
DefaultMode: newInt32(0644),
},
},
},
},
{
name: "valid Secret with projection and mode",
vol: api.Volume{
Name: "secret",
VolumeSource: api.VolumeSource{
@ -1138,6 +1164,7 @@ func TestValidateVolumes(t *testing.T) {
Items: []api.KeyToPath{{
Key: "key",
Path: "filename",
Mode: newInt32(0644),
}},
},
},
@ -1200,6 +1227,34 @@ func TestValidateVolumes(t *testing.T) {
errtype: field.ErrorTypeInvalid,
errfield: "secret.items[0].path",
},
{
name: "secret with invalid positive defaultMode",
vol: api.Volume{
Name: "secret",
VolumeSource: api.VolumeSource{
Secret: &api.SecretVolumeSource{
SecretName: "s",
DefaultMode: newInt32(01000),
},
},
},
errtype: field.ErrorTypeInvalid,
errfield: "secret.defaultMode",
},
{
name: "secret with invalid negative defaultMode",
vol: api.Volume{
Name: "secret",
VolumeSource: api.VolumeSource{
Secret: &api.SecretVolumeSource{
SecretName: "s",
DefaultMode: newInt32(-1),
},
},
},
errtype: field.ErrorTypeInvalid,
errfield: "secret.defaultMode",
},
// ConfigMap
{
name: "valid ConfigMap",
@ -1215,7 +1270,21 @@ func TestValidateVolumes(t *testing.T) {
},
},
{
name: "valid ConfigMap with projection",
name: "valid ConfigMap with defaultMode",
vol: api.Volume{
Name: "cfgmap",
VolumeSource: api.VolumeSource{
ConfigMap: &api.ConfigMapVolumeSource{
LocalObjectReference: api.LocalObjectReference{
Name: "my-cfgmap",
},
DefaultMode: newInt32(0644),
},
},
},
},
{
name: "valid ConfigMap with projection and mode",
vol: api.Volume{
Name: "cfgmap",
VolumeSource: api.VolumeSource{
@ -1225,6 +1294,7 @@ func TestValidateVolumes(t *testing.T) {
Items: []api.KeyToPath{{
Key: "key",
Path: "filename",
Mode: newInt32(0644),
}},
},
},
@ -1288,6 +1358,34 @@ func TestValidateVolumes(t *testing.T) {
errtype: field.ErrorTypeInvalid,
errfield: "configMap.items[0].path",
},
{
name: "configmap with invalid positive defaultMode",
vol: api.Volume{
Name: "cfgmap",
VolumeSource: api.VolumeSource{
ConfigMap: &api.ConfigMapVolumeSource{
LocalObjectReference: api.LocalObjectReference{Name: "c"},
DefaultMode: newInt32(01000),
},
},
},
errtype: field.ErrorTypeInvalid,
errfield: "configMap.defaultMode",
},
{
name: "configmap with invalid negative defaultMode",
vol: api.Volume{
Name: "cfgmap",
VolumeSource: api.VolumeSource{
ConfigMap: &api.ConfigMapVolumeSource{
LocalObjectReference: api.LocalObjectReference{Name: "c"},
DefaultMode: newInt32(-1),
},
},
},
errtype: field.ErrorTypeInvalid,
errfield: "configMap.defaultMode",
},
// Glusterfs
{
name: "valid Glusterfs",
@ -1551,6 +1649,75 @@ func TestValidateVolumes(t *testing.T) {
},
},
},
{
name: "downapi valid defaultMode",
vol: api.Volume{
Name: "downapi",
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
DefaultMode: newInt32(0644),
},
},
},
},
{
name: "downapi valid item mode",
vol: api.Volume{
Name: "downapi",
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
Items: []api.DownwardAPIVolumeFile{{
Mode: newInt32(0644),
Path: "path",
FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.labels",
},
}},
},
},
},
},
{
name: "downapi invalid positive item mode",
vol: api.Volume{
Name: "downapi",
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
Items: []api.DownwardAPIVolumeFile{{
Mode: newInt32(01000),
Path: "path",
FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.labels",
},
}},
},
},
},
errtype: field.ErrorTypeInvalid,
errfield: "downwardAPI.mode",
},
{
name: "downapi invalid negative item mode",
vol: api.Volume{
Name: "downapi",
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
Items: []api.DownwardAPIVolumeFile{{
Mode: newInt32(-1),
Path: "path",
FieldRef: &api.ObjectFieldSelector{
APIVersion: "v1",
FieldPath: "metadata.labels",
},
}},
},
},
},
errtype: field.ErrorTypeInvalid,
errfield: "downwardAPI.mode",
},
{
name: "downapi empty metatada path",
vol: api.Volume{
@ -1673,6 +1840,32 @@ func TestValidateVolumes(t *testing.T) {
errfield: "downwardAPI",
errdetail: "fieldRef and resourceFieldRef can not be specified simultaneously",
},
{
name: "downapi invalid positive defaultMode",
vol: api.Volume{
Name: "downapi",
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
DefaultMode: newInt32(01000),
},
},
},
errtype: field.ErrorTypeInvalid,
errfield: "downwardAPI.defaultMode",
},
{
name: "downapi invalid negative defaultMode",
vol: api.Volume{
Name: "downapi",
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
DefaultMode: newInt32(-1),
},
},
},
errtype: field.ErrorTypeInvalid,
errfield: "downwardAPI.defaultMode",
},
// FC
{
name: "valid FC",

View File

@ -175,7 +175,7 @@ func (b *configMapVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
len(configMap.Data),
totalBytes)
payload, err := makePayload(b.source.Items, configMap)
payload, err := makePayload(b.source.Items, configMap, b.source.DefaultMode)
if err != nil {
return err
}
@ -202,22 +202,36 @@ func (b *configMapVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
return nil
}
func makePayload(mappings []api.KeyToPath, configMap *api.ConfigMap) (map[string][]byte, error) {
payload := make(map[string][]byte, len(configMap.Data))
func makePayload(mappings []api.KeyToPath, configMap *api.ConfigMap, defaultMode *int32) (map[string]volumeutil.FileProjection, error) {
if defaultMode == nil {
return nil, fmt.Errorf("No defaultMode used, not even the default value for it")
}
payload := make(map[string]volumeutil.FileProjection, len(configMap.Data))
var fileProjection volumeutil.FileProjection
if len(mappings) == 0 {
for name, data := range configMap.Data {
payload[name] = []byte(data)
fileProjection.Data = []byte(data)
fileProjection.Mode = *defaultMode
payload[name] = fileProjection
}
} else {
for _, ktp := range mappings {
content, ok := configMap.Data[ktp.Key]
if !ok {
glog.Errorf("references non-existent config key")
return nil, fmt.Errorf("references non-existent config key")
err_msg := "references non-existent config key"
glog.Errorf(err_msg)
return nil, fmt.Errorf(err_msg)
}
payload[ktp.Path] = []byte(content)
fileProjection.Data = []byte(content)
if ktp.Mode != nil {
fileProjection.Mode = *ktp.Mode
} else {
fileProjection.Mode = *defaultMode
}
payload[ktp.Path] = fileProjection
}
}

View File

@ -36,11 +36,13 @@ import (
)
func TestMakePayload(t *testing.T) {
case_mapping_mode := int32(0400)
cases := []struct {
name string
mappings []api.KeyToPath
configMap *api.ConfigMap
payload map[string][]byte
mode int32
payload map[string]util.FileProjection
success bool
}{
{
@ -51,9 +53,10 @@ func TestMakePayload(t *testing.T) {
"bar": "bar",
},
},
payload: map[string][]byte{
"foo": []byte("foo"),
"bar": []byte("bar"),
mode: 0644,
payload: map[string]util.FileProjection{
"foo": {Data: []byte("foo"), Mode: 0644},
"bar": {Data: []byte("bar"), Mode: 0644},
},
success: true,
},
@ -71,8 +74,9 @@ func TestMakePayload(t *testing.T) {
"bar": "bar",
},
},
payload: map[string][]byte{
"path/to/foo.txt": []byte("foo"),
mode: 0644,
payload: map[string]util.FileProjection{
"path/to/foo.txt": {Data: []byte("foo"), Mode: 0644},
},
success: true,
},
@ -90,8 +94,9 @@ func TestMakePayload(t *testing.T) {
"bar": "bar",
},
},
payload: map[string][]byte{
"path/to/1/2/3/foo.txt": []byte("foo"),
mode: 0644,
payload: map[string]util.FileProjection{
"path/to/1/2/3/foo.txt": {Data: []byte("foo"), Mode: 0644},
},
success: true,
},
@ -109,8 +114,9 @@ func TestMakePayload(t *testing.T) {
"bar": "bar",
},
},
payload: map[string][]byte{
"path/to/1/2/3/foo.txt": []byte("foo"),
mode: 0644,
payload: map[string]util.FileProjection{
"path/to/1/2/3/foo.txt": {Data: []byte("foo"), Mode: 0644},
},
success: true,
},
@ -132,9 +138,10 @@ func TestMakePayload(t *testing.T) {
"bar": "bar",
},
},
payload: map[string][]byte{
"path/to/1/2/3/foo.txt": []byte("foo"),
"another/path/to/the/esteemed/bar.bin": []byte("bar"),
mode: 0644,
payload: map[string]util.FileProjection{
"path/to/1/2/3/foo.txt": {Data: []byte("foo"), Mode: 0644},
"another/path/to/the/esteemed/bar.bin": {Data: []byte("bar"), Mode: 0644},
},
success: true,
},
@ -152,12 +159,65 @@ func TestMakePayload(t *testing.T) {
"bar": "bar",
},
},
mode: 0644,
success: false,
},
{
name: "mapping with Mode",
mappings: []api.KeyToPath{
{
Key: "foo",
Path: "foo.txt",
Mode: &case_mapping_mode,
},
{
Key: "bar",
Path: "bar.bin",
Mode: &case_mapping_mode,
},
},
configMap: &api.ConfigMap{
Data: map[string]string{
"foo": "foo",
"bar": "bar",
},
},
mode: 0644,
payload: map[string]util.FileProjection{
"foo.txt": {Data: []byte("foo"), Mode: case_mapping_mode},
"bar.bin": {Data: []byte("bar"), Mode: case_mapping_mode},
},
success: true,
},
{
name: "mapping with defaultMode",
mappings: []api.KeyToPath{
{
Key: "foo",
Path: "foo.txt",
},
{
Key: "bar",
Path: "bar.bin",
},
},
configMap: &api.ConfigMap{
Data: map[string]string{
"foo": "foo",
"bar": "bar",
},
},
mode: 0644,
payload: map[string]util.FileProjection{
"foo.txt": {Data: []byte("foo"), Mode: 0644},
"bar.bin": {Data: []byte("bar"), Mode: 0644},
},
success: true,
},
}
for _, tc := range cases {
actualPayload, err := makePayload(tc.mappings, tc.configMap)
actualPayload, err := makePayload(tc.mappings, tc.configMap, &tc.mode)
if err != nil && tc.success {
t.Errorf("%v: unexpected failure making payload: %v", tc.name, err)
continue
@ -214,7 +274,7 @@ func TestPlugin(t *testing.T) {
testNamespace = "test_configmap_namespace"
testName = "test_configmap_name"
volumeSpec = volumeSpec(testVolumeName, testName)
volumeSpec = volumeSpec(testVolumeName, testName, 0644)
configMap = configMap(testNamespace, testName)
client = fake.NewSimpleClientset(&configMap)
pluginMgr = volume.VolumePluginMgr{}
@ -277,7 +337,7 @@ func TestPluginReboot(t *testing.T) {
testNamespace = "test_configmap_namespace"
testName = "test_configmap_name"
volumeSpec = volumeSpec(testVolumeName, testName)
volumeSpec = volumeSpec(testVolumeName, testName, 0644)
configMap = configMap(testNamespace, testName)
client = fake.NewSimpleClientset(&configMap)
pluginMgr = volume.VolumePluginMgr{}
@ -324,7 +384,7 @@ func TestPluginReboot(t *testing.T) {
doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t)
}
func volumeSpec(volumeName, configMapName string) *api.Volume {
func volumeSpec(volumeName, configMapName string, defaultMode int32) *api.Volume {
return &api.Volume{
Name: volumeName,
VolumeSource: api.VolumeSource{
@ -332,6 +392,7 @@ func volumeSpec(volumeName, configMapName string) *api.Volume {
LocalObjectReference: api.LocalObjectReference{
Name: configMapName,
},
DefaultMode: &defaultMode,
},
},
}

View File

@ -92,6 +92,7 @@ func (plugin *downwardAPIPlugin) NewMounter(spec *volume.Spec, pod *api.Pod, opt
}
return &downwardAPIVolumeMounter{
downwardAPIVolume: v,
source: *spec.Volume.DownwardAPI,
opts: &opts,
}, nil
}
@ -130,6 +131,7 @@ type downwardAPIVolume struct {
// and dumps it in files
type downwardAPIVolumeMounter struct {
*downwardAPIVolume
source api.DownwardAPIVolumeSource
opts *volume.VolumeOptions
}
@ -166,7 +168,7 @@ func (b *downwardAPIVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
return err
}
data, err := b.collectData()
data, err := b.collectData(b.source.DefaultMode)
if err != nil {
glog.Errorf("Error preparing data for downwardAPI volume %v for pod %v/%v: %s", b.volName, b.pod.Namespace, b.pod.Name, err.Error())
return err
@ -193,16 +195,27 @@ func (b *downwardAPIVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
// collectData collects requested downwardAPI in data map.
// Map's key is the requested name of file to dump
// Map's value is the (sorted) content of the field to be dumped in the file.
func (d *downwardAPIVolume) collectData() (map[string][]byte, error) {
func (d *downwardAPIVolume) collectData(defaultMode *int32) (map[string]volumeutil.FileProjection, error) {
if defaultMode == nil {
return nil, fmt.Errorf("No defaultMode used, not even the default value for it")
}
errlist := []error{}
data := make(map[string][]byte)
data := make(map[string]volumeutil.FileProjection)
for _, fileInfo := range d.items {
var fileProjection volumeutil.FileProjection
fPath := path.Clean(fileInfo.Path)
if fileInfo.Mode != nil {
fileProjection.Mode = *fileInfo.Mode
} else {
fileProjection.Mode = *defaultMode
}
if fileInfo.FieldRef != nil {
if values, err := fieldpath.ExtractFieldPathAsString(d.pod, fileInfo.FieldRef.FieldPath); err != nil {
glog.Errorf("Unable to extract field %s: %s", fileInfo.FieldRef.FieldPath, err.Error())
errlist = append(errlist, err)
} else {
data[path.Clean(fileInfo.Path)] = []byte(sortLines(values))
fileProjection.Data = []byte(sortLines(values))
}
} else if fileInfo.ResourceFieldRef != nil {
containerName := fileInfo.ResourceFieldRef.ContainerName
@ -213,9 +226,11 @@ func (d *downwardAPIVolume) collectData() (map[string][]byte, error) {
glog.Errorf("Unable to extract field %s: %s", fileInfo.ResourceFieldRef.Resource, err.Error())
errlist = append(errlist, err)
} else {
data[path.Clean(fileInfo.Path)] = []byte(sortLines(values))
fileProjection.Data = []byte(sortLines(values))
}
}
data[fPath] = fileProjection
}
return data, utilerrors.NewAggregate(errlist)
}

View File

@ -103,10 +103,12 @@ func TestLabels(t *testing.T) {
defer os.RemoveAll(rootDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
defaultMode := int32(0644)
volumeSpec := &api.Volume{
Name: testVolumeName,
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
DefaultMode: &defaultMode,
Items: []api.DownwardAPIVolumeFile{
{Path: "labels", FieldRef: &api.ObjectFieldSelector{
FieldPath: "metadata.labels"}}}},
@ -167,10 +169,12 @@ func TestAnnotations(t *testing.T) {
"a1": "value1",
"a2": "value2"}
defaultMode := int32(0644)
volumeSpec := &api.Volume{
Name: testVolumeName,
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
DefaultMode: &defaultMode,
Items: []api.DownwardAPIVolumeFile{
{Path: "annotations", FieldRef: &api.ObjectFieldSelector{
FieldPath: "metadata.annotations"}}}},
@ -230,10 +234,12 @@ func TestName(t *testing.T) {
testName = "test_metadata_name"
)
defaultMode := int32(0644)
volumeSpec := &api.Volume{
Name: testVolumeName,
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
DefaultMode: &defaultMode,
Items: []api.DownwardAPIVolumeFile{
{Path: "name_file_name", FieldRef: &api.ObjectFieldSelector{
FieldPath: "metadata.name"}}}},
@ -293,10 +299,12 @@ func TestNamespace(t *testing.T) {
testName = "test_metadata_name"
)
defaultMode := int32(0644)
volumeSpec := &api.Volume{
Name: testVolumeName,
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
DefaultMode: &defaultMode,
Items: []api.DownwardAPIVolumeFile{
{Path: "namespace_file_name", FieldRef: &api.ObjectFieldSelector{
FieldPath: "metadata.namespace"}}}},
@ -371,10 +379,12 @@ func TestWriteTwiceNoUpdate(t *testing.T) {
defer os.RemoveAll(tmpDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
defaultMode := int32(0644)
volumeSpec := &api.Volume{
Name: testVolumeName,
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
DefaultMode: &defaultMode,
Items: []api.DownwardAPIVolumeFile{
{Path: "labels", FieldRef: &api.ObjectFieldSelector{
FieldPath: "metadata.labels"}}}},
@ -457,10 +467,12 @@ func TestWriteTwiceWithUpdate(t *testing.T) {
defer os.RemoveAll(tmpDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
defaultMode := int32(0644)
volumeSpec := &api.Volume{
Name: testVolumeName,
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
DefaultMode: &defaultMode,
Items: []api.DownwardAPIVolumeFile{
{Path: "labels", FieldRef: &api.ObjectFieldSelector{
FieldPath: "metadata.labels"}}}},
@ -563,10 +575,12 @@ func TestWriteWithUnixPath(t *testing.T) {
defer os.RemoveAll(tmpDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
defaultMode := int32(0644)
volumeSpec := &api.Volume{
Name: testVolumeName,
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
DefaultMode: &defaultMode,
Items: []api.DownwardAPIVolumeFile{
{Path: "this/is/mine/labels", FieldRef: &api.ObjectFieldSelector{
FieldPath: "metadata.labels"}},
@ -643,10 +657,12 @@ func TestWriteWithUnixPathBadPath(t *testing.T) {
t.Errorf("Can't find the plugin by name")
}
defaultMode := int32(0644)
volumeSpec := &api.Volume{
Name: testVolumeName,
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
DefaultMode: &defaultMode,
Items: []api.DownwardAPIVolumeFile{
{
Path: "this//labels",
@ -684,3 +700,138 @@ func TestWriteWithUnixPathBadPath(t *testing.T) {
t.Errorf("Found `%s` expected %s", data, fieldpath.FormatMap(labels))
}
}
func TestDefaultMode(t *testing.T) {
var (
testPodUID = types.UID("test_pod_uid")
testVolumeName = "test_name"
testNamespace = "test_metadata_namespace"
testName = "test_metadata_name"
)
defaultMode := int32(0644)
volumeSpec := &api.Volume{
Name: testVolumeName,
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
DefaultMode: &defaultMode,
Items: []api.DownwardAPIVolumeFile{
{Path: "name_file_name", FieldRef: &api.ObjectFieldSelector{
FieldPath: "metadata.name"}}}},
},
}
clientset := fake.NewSimpleClientset(&api.Pod{
ObjectMeta: api.ObjectMeta{
Name: testName,
Namespace: testNamespace,
},
})
pluginMgr := volume.VolumePluginMgr{}
tmpDir, host := newTestHost(t, clientset)
defer os.RemoveAll(tmpDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
if err != nil {
t.Errorf("Can't find the plugin by name")
}
pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: testPodUID, Name: testName}}
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()
err = mounter.SetUp(nil)
if err != nil {
t.Errorf("Failed to setup volume: %v", err)
}
fileInfo, err := os.Stat(path.Join(volumePath, "name_file_name"))
if err != nil {
t.Errorf(err.Error())
}
actualMode := fileInfo.Mode()
expectedMode := os.FileMode(defaultMode)
if actualMode != expectedMode {
t.Errorf("Found mode `%v` expected %v", actualMode, expectedMode)
}
CleanEverything(plugin, testVolumeName, volumePath, testPodUID, t)
}
func TestItemMode(t *testing.T) {
var (
testPodUID = types.UID("test_pod_uid")
testVolumeName = "test_name"
testNamespace = "test_metadata_namespace"
testName = "test_metadata_name"
)
defaultMode := int32(0644)
itemMode := int32(0400)
volumeSpec := &api.Volume{
Name: testVolumeName,
VolumeSource: api.VolumeSource{
DownwardAPI: &api.DownwardAPIVolumeSource{
DefaultMode: &defaultMode,
Items: []api.DownwardAPIVolumeFile{
{
Path: "name_file_name", FieldRef: &api.ObjectFieldSelector{FieldPath: "metadata.name"},
Mode: &itemMode,
},
},
},
},
}
clientset := fake.NewSimpleClientset(&api.Pod{
ObjectMeta: api.ObjectMeta{
Name: testName,
Namespace: testNamespace,
},
})
pluginMgr := volume.VolumePluginMgr{}
tmpDir, host := newTestHost(t, clientset)
defer os.RemoveAll(tmpDir)
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
if err != nil {
t.Errorf("Can't find the plugin by name")
}
pod := &api.Pod{ObjectMeta: api.ObjectMeta{UID: testPodUID, Name: testName}}
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()
err = mounter.SetUp(nil)
if err != nil {
t.Errorf("Failed to setup volume: %v", err)
}
fileInfo, err := os.Stat(path.Join(volumePath, "name_file_name"))
if err != nil {
t.Errorf(err.Error())
}
actualMode := fileInfo.Mode()
expectedMode := os.FileMode(itemMode)
if actualMode != expectedMode {
t.Errorf("Found mode `%v` expected %v", actualMode, expectedMode)
}
CleanEverything(plugin, testVolumeName, volumePath, testPodUID, t)
}

View File

@ -190,7 +190,7 @@ func (b *secretVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
len(secret.Data),
totalBytes)
payload, err := makePayload(b.source.Items, secret)
payload, err := makePayload(b.source.Items, secret, b.source.DefaultMode)
if err != nil {
return err
}
@ -217,25 +217,38 @@ func (b *secretVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
return nil
}
func makePayload(mappings []api.KeyToPath, secret *api.Secret) (map[string][]byte, error) {
payload := make(map[string][]byte, len(secret.Data))
func makePayload(mappings []api.KeyToPath, secret *api.Secret, defaultMode *int32) (map[string]volumeutil.FileProjection, error) {
if defaultMode == nil {
return nil, fmt.Errorf("No defaultMode used, not even the default value for it")
}
payload := make(map[string]volumeutil.FileProjection, len(secret.Data))
var fileProjection volumeutil.FileProjection
if len(mappings) == 0 {
for name, data := range secret.Data {
payload[name] = []byte(data)
fileProjection.Data = []byte(data)
fileProjection.Mode = *defaultMode
payload[name] = fileProjection
}
} else {
for _, ktp := range mappings {
content, ok := secret.Data[ktp.Key]
if !ok {
glog.Errorf("references non-existent secret key")
return nil, fmt.Errorf("references non-existent secret key")
err_msg := "references non-existent secret key"
glog.Errorf(err_msg)
return nil, fmt.Errorf(err_msg)
}
payload[ktp.Path] = []byte(content)
fileProjection.Data = []byte(content)
if ktp.Mode != nil {
fileProjection.Mode = *ktp.Mode
} else {
fileProjection.Mode = *defaultMode
}
payload[ktp.Path] = fileProjection
}
}
return payload, nil
}

View File

@ -39,11 +39,13 @@ import (
)
func TestMakePayload(t *testing.T) {
case_mapping_mode := int32(0400)
cases := []struct {
name string
mappings []api.KeyToPath
secret *api.Secret
payload map[string][]byte
mode int32
payload map[string]util.FileProjection
success bool
}{
{
@ -54,9 +56,10 @@ func TestMakePayload(t *testing.T) {
"bar": []byte("bar"),
},
},
payload: map[string][]byte{
"foo": []byte("foo"),
"bar": []byte("bar"),
mode: 0644,
payload: map[string]util.FileProjection{
"foo": {Data: []byte("foo"), Mode: 0644},
"bar": {Data: []byte("bar"), Mode: 0644},
},
success: true,
},
@ -74,8 +77,9 @@ func TestMakePayload(t *testing.T) {
"bar": []byte("bar"),
},
},
payload: map[string][]byte{
"path/to/foo.txt": []byte("foo"),
mode: 0644,
payload: map[string]util.FileProjection{
"path/to/foo.txt": {Data: []byte("foo"), Mode: 0644},
},
success: true,
},
@ -93,8 +97,9 @@ func TestMakePayload(t *testing.T) {
"bar": []byte("bar"),
},
},
payload: map[string][]byte{
"path/to/1/2/3/foo.txt": []byte("foo"),
mode: 0644,
payload: map[string]util.FileProjection{
"path/to/1/2/3/foo.txt": {Data: []byte("foo"), Mode: 0644},
},
success: true,
},
@ -112,8 +117,9 @@ func TestMakePayload(t *testing.T) {
"bar": []byte("bar"),
},
},
payload: map[string][]byte{
"path/to/1/2/3/foo.txt": []byte("foo"),
mode: 0644,
payload: map[string]util.FileProjection{
"path/to/1/2/3/foo.txt": {Data: []byte("foo"), Mode: 0644},
},
success: true,
},
@ -135,9 +141,10 @@ func TestMakePayload(t *testing.T) {
"bar": []byte("bar"),
},
},
payload: map[string][]byte{
"path/to/1/2/3/foo.txt": []byte("foo"),
"another/path/to/the/esteemed/bar.bin": []byte("bar"),
mode: 0644,
payload: map[string]util.FileProjection{
"path/to/1/2/3/foo.txt": {Data: []byte("foo"), Mode: 0644},
"another/path/to/the/esteemed/bar.bin": {Data: []byte("bar"), Mode: 0644},
},
success: true,
},
@ -155,12 +162,65 @@ func TestMakePayload(t *testing.T) {
"bar": []byte("bar"),
},
},
mode: 0644,
success: false,
},
{
name: "mapping with Mode",
mappings: []api.KeyToPath{
{
Key: "foo",
Path: "foo.txt",
Mode: &case_mapping_mode,
},
{
Key: "bar",
Path: "bar.bin",
Mode: &case_mapping_mode,
},
},
secret: &api.Secret{
Data: map[string][]byte{
"foo": []byte("foo"),
"bar": []byte("bar"),
},
},
mode: 0644,
payload: map[string]util.FileProjection{
"foo.txt": {Data: []byte("foo"), Mode: case_mapping_mode},
"bar.bin": {Data: []byte("bar"), Mode: case_mapping_mode},
},
success: true,
},
{
name: "mapping with defaultMode",
mappings: []api.KeyToPath{
{
Key: "foo",
Path: "foo.txt",
},
{
Key: "bar",
Path: "bar.bin",
},
},
secret: &api.Secret{
Data: map[string][]byte{
"foo": []byte("foo"),
"bar": []byte("bar"),
},
},
mode: 0644,
payload: map[string]util.FileProjection{
"foo.txt": {Data: []byte("foo"), Mode: 0644},
"bar.bin": {Data: []byte("bar"), Mode: 0644},
},
success: true,
},
}
for _, tc := range cases {
actualPayload, err := makePayload(tc.mappings, tc.secret)
actualPayload, err := makePayload(tc.mappings, tc.secret, &tc.mode)
if err != nil && tc.success {
t.Errorf("%v: unexpected failure making payload: %v", tc.name, err)
continue
@ -217,7 +277,7 @@ func TestPlugin(t *testing.T) {
testNamespace = "test_secret_namespace"
testName = "test_secret_name"
volumeSpec = volumeSpec(testVolumeName, testName)
volumeSpec = volumeSpec(testVolumeName, testName, 0644)
secret = secret(testNamespace, testName)
client = fake.NewSimpleClientset(&secret)
pluginMgr = volume.VolumePluginMgr{}
@ -290,7 +350,7 @@ func TestPluginReboot(t *testing.T) {
testNamespace = "test_secret_namespace"
testName = "test_secret_name"
volumeSpec = volumeSpec(testVolumeName, testName)
volumeSpec = volumeSpec(testVolumeName, testName, 0644)
secret = secret(testNamespace, testName)
client = fake.NewSimpleClientset(&secret)
pluginMgr = volume.VolumePluginMgr{}
@ -336,12 +396,13 @@ func TestPluginReboot(t *testing.T) {
doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t)
}
func volumeSpec(volumeName, secretName string) *api.Volume {
func volumeSpec(volumeName, secretName string, defaultMode int32) *api.Volume {
return &api.Volume{
Name: volumeName,
VolumeSource: api.VolumeSource{
Secret: &api.SecretVolumeSource{
SecretName: secretName,
DefaultMode: &defaultMode,
},
},
}

View File

@ -60,9 +60,14 @@ type AtomicWriter struct {
logContext string
}
type FileProjection struct {
Data []byte
Mode int32
}
// NewAtomicWriter creates a new AtomicWriter configured to write to the given
// target directory, or returns an error if the target directory does not exist.
func NewAtomicWriter(targetDir, logContext string) (*AtomicWriter, error) {
func NewAtomicWriter(targetDir string, logContext string) (*AtomicWriter, error) {
_, err := os.Stat(targetDir)
if os.IsNotExist(err) {
return nil, err
@ -113,7 +118,7 @@ const (
// 9.  The new data directory symlink is renamed to the data directory; rename is atomic
// 10. Old paths are removed from the user-visible portion of the target directory
// 11.  The previous timestamped directory is removed, if it exists
func (w *AtomicWriter) Write(payload map[string][]byte) error {
func (w *AtomicWriter) Write(payload map[string]FileProjection) error {
// (1)
cleanPayload, err := validatePayload(payload)
if err != nil {
@ -203,8 +208,8 @@ func (w *AtomicWriter) Write(payload map[string][]byte) error {
}
// validatePayload returns an error if any path in the payload returns a copy of the payload with the paths cleaned.
func validatePayload(payload map[string][]byte) (map[string][]byte, error) {
cleanPayload := make(map[string][]byte)
func validatePayload(payload map[string]FileProjection) (map[string]FileProjection, error) {
cleanPayload := make(map[string]FileProjection)
for k, content := range payload {
if err := validatePath(k); err != nil {
return nil, err
@ -257,9 +262,9 @@ func validatePath(targetPath string) error {
}
// shouldWritePayload returns whether the payload should be written to disk.
func (w *AtomicWriter) shouldWritePayload(payload map[string][]byte) (bool, error) {
for userVisiblePath, content := range payload {
shouldWrite, err := w.shouldWriteFile(path.Join(w.targetDir, userVisiblePath), content)
func (w *AtomicWriter) shouldWritePayload(payload map[string]FileProjection) (bool, error) {
for userVisiblePath, fileProjection := range payload {
shouldWrite, err := w.shouldWriteFile(path.Join(w.targetDir, userVisiblePath), fileProjection.Data)
if err != nil {
return false, err
}
@ -290,7 +295,7 @@ func (w *AtomicWriter) shouldWriteFile(path string, content []byte) (bool, error
// pathsToRemove walks the user-visible portion of the target directory and
// determines which paths should be removed (if any) after the payload is
// written to the target directory.
func (w *AtomicWriter) pathsToRemove(payload map[string][]byte) (sets.String, error) {
func (w *AtomicWriter) pathsToRemove(payload map[string]FileProjection) (sets.String, error) {
paths := sets.NewString()
visitor := func(path string, info os.FileInfo, err error) error {
if path == w.targetDir {
@ -355,8 +360,10 @@ func (w *AtomicWriter) newTimestampDir() (string, error) {
// writePayloadToDir writes the given payload to the given directory. The
// directory must exist.
func (w *AtomicWriter) writePayloadToDir(payload map[string][]byte, dir string) error {
for userVisiblePath, content := range payload {
func (w *AtomicWriter) writePayloadToDir(payload map[string]FileProjection, dir string) error {
for userVisiblePath, fileProjection := range payload {
content := fileProjection.Data
mode := os.FileMode(fileProjection.Mode)
fullPath := path.Join(dir, userVisiblePath)
baseDir, _ := filepath.Split(fullPath)
@ -366,11 +373,19 @@ func (w *AtomicWriter) writePayloadToDir(payload map[string][]byte, dir string)
return err
}
err = ioutil.WriteFile(fullPath, content, 0644)
err = ioutil.WriteFile(fullPath, content, mode)
if err != nil {
glog.Errorf("%s: unable to write file %s: %v", w.logContext, fullPath, err)
glog.Errorf("%s: unable to write file %s with mode %v: %v", w.logContext, fullPath, mode, err)
return err
}
// Chmod is needed because ioutil.WriteFile() ends up calling
// open(2) to create the file, so the final mode used is "mode &
// ~umask". But we want to make sure the specified mode is used
// in the file no matter what the umask is.
err = os.Chmod(fullPath, mode)
if err != nil {
glog.Errorf("%s: unable to write file %s with mode %v: %v", w.logContext, fullPath, mode, err)
}
}
return nil
@ -387,7 +402,7 @@ func (w *AtomicWriter) writePayloadToDir(payload map[string][]byte, dir string)
// foo/bar -> ../..data/foo/bar
// baz/bar -> ../..data/baz/bar
// foo/baz/blah -> ../../..data/foo/baz/blah
func (w *AtomicWriter) createUserVisibleFiles(payload map[string][]byte) error {
func (w *AtomicWriter) createUserVisibleFiles(payload map[string]FileProjection) error {
for userVisiblePath := range payload {
dir, _ := filepath.Split(userVisiblePath)
subDirs := 0

View File

@ -130,90 +130,90 @@ func TestValidatePath(t *testing.T) {
func TestPathsToRemove(t *testing.T) {
cases := []struct {
name string
payload1 map[string][]byte
payload2 map[string][]byte
payload1 map[string]FileProjection
payload2 map[string]FileProjection
expected sets.String
}{
{
name: "simple",
payload1: map[string][]byte{
"foo.txt": []byte("foo"),
"bar.txt": []byte("bar"),
payload1: map[string]FileProjection{
"foo.txt": {Mode: 0644, Data: []byte("foo")},
"bar.txt": {Mode: 0644, Data: []byte("bar")},
},
payload2: map[string][]byte{
"foo.txt": []byte("foo"),
payload2: map[string]FileProjection{
"foo.txt": {Mode: 0644, Data: []byte("foo")},
},
expected: sets.NewString("bar.txt"),
},
{
name: "simple 2",
payload1: map[string][]byte{
"foo.txt": []byte("foo"),
"zip/bar.txt": []byte("zip/bar"),
payload1: map[string]FileProjection{
"foo.txt": {Mode: 0644, Data: []byte("foo")},
"zip/bar.txt": {Mode: 0644, Data: []byte("zip/b}ar")},
},
payload2: map[string][]byte{
"foo.txt": []byte("foo"),
payload2: map[string]FileProjection{
"foo.txt": {Mode: 0644, Data: []byte("foo")},
},
expected: sets.NewString("zip/bar.txt", "zip"),
},
{
name: "subdirs 1",
payload1: map[string][]byte{
"foo.txt": []byte("foo"),
"zip/zap/bar.txt": []byte("zip/bar"),
payload1: map[string]FileProjection{
"foo.txt": {Mode: 0644, Data: []byte("foo")},
"zip/zap/bar.txt": {Mode: 0644, Data: []byte("zip/bar")},
},
payload2: map[string][]byte{
"foo.txt": []byte("foo"),
payload2: map[string]FileProjection{
"foo.txt": {Mode: 0644, Data: []byte("foo")},
},
expected: sets.NewString("zip/zap/bar.txt", "zip", "zip/zap"),
},
{
name: "subdirs 2",
payload1: map[string][]byte{
"foo.txt": []byte("foo"),
"zip/1/2/3/4/bar.txt": []byte("zip/bar"),
payload1: map[string]FileProjection{
"foo.txt": {Mode: 0644, Data: []byte("foo")},
"zip/1/2/3/4/bar.txt": {Mode: 0644, Data: []byte("zip/b}ar")},
},
payload2: map[string][]byte{
"foo.txt": []byte("foo"),
payload2: map[string]FileProjection{
"foo.txt": {Mode: 0644, Data: []byte("foo")},
},
expected: sets.NewString("zip/1/2/3/4/bar.txt", "zip", "zip/1", "zip/1/2", "zip/1/2/3", "zip/1/2/3/4"),
},
{
name: "subdirs 3",
payload1: map[string][]byte{
"foo.txt": []byte("foo"),
"zip/1/2/3/4/bar.txt": []byte("zip/bar"),
"zap/a/b/c/bar.txt": []byte("zap/bar"),
payload1: map[string]FileProjection{
"foo.txt": {Mode: 0644, Data: []byte("foo")},
"zip/1/2/3/4/bar.txt": {Mode: 0644, Data: []byte("zip/b}ar")},
"zap/a/b/c/bar.txt": {Mode: 0644, Data: []byte("zap/bar")},
},
payload2: map[string][]byte{
"foo.txt": []byte("foo"),
payload2: map[string]FileProjection{
"foo.txt": {Mode: 0644, Data: []byte("foo")},
},
expected: sets.NewString("zip/1/2/3/4/bar.txt", "zip", "zip/1", "zip/1/2", "zip/1/2/3", "zip/1/2/3/4", "zap", "zap/a", "zap/a/b", "zap/a/b/c", "zap/a/b/c/bar.txt"),
},
{
name: "subdirs 4",
payload1: map[string][]byte{
"foo.txt": []byte("foo"),
"zap/1/2/3/4/bar.txt": []byte("zip/bar"),
"zap/1/2/c/bar.txt": []byte("zap/bar"),
"zap/1/2/magic.txt": []byte("indigo"),
payload1: map[string]FileProjection{
"foo.txt": {Mode: 0644, Data: []byte("foo")},
"zap/1/2/3/4/bar.txt": {Mode: 0644, Data: []byte("zip/bar")},
"zap/1/2/c/bar.txt": {Mode: 0644, Data: []byte("zap/bar")},
"zap/1/2/magic.txt": {Mode: 0644, Data: []byte("indigo")},
},
payload2: map[string][]byte{
"foo.txt": []byte("foo"),
"zap/1/2/magic.txt": []byte("indigo"),
payload2: map[string]FileProjection{
"foo.txt": {Mode: 0644, Data: []byte("foo")},
"zap/1/2/magic.txt": {Mode: 0644, Data: []byte("indigo")},
},
expected: sets.NewString("zap/1/2/3/4/bar.txt", "zap/1/2/3", "zap/1/2/3/4", "zap/1/2/3/4/bar.txt", "zap/1/2/c", "zap/1/2/c/bar.txt"),
},
{
name: "subdirs 5",
payload1: map[string][]byte{
"foo.txt": []byte("foo"),
"zap/1/2/3/4/bar.txt": []byte("zip/bar"),
"zap/1/2/c/bar.txt": []byte("zap/bar"),
payload1: map[string]FileProjection{
"foo.txt": {Mode: 0644, Data: []byte("foo")},
"zap/1/2/3/4/bar.txt": {Mode: 0644, Data: []byte("zip/bar")},
"zap/1/2/c/bar.txt": {Mode: 0644, Data: []byte("zap/bar")},
},
payload2: map[string][]byte{
"foo.txt": []byte("foo"),
"zap/1/2/magic.txt": []byte("indigo"),
payload2: map[string]FileProjection{
"foo.txt": {Mode: 0644, Data: []byte("foo")},
"zap/1/2/magic.txt": {Mode: 0644, Data: []byte("indigo")},
},
expected: sets.NewString("zap/1/2/3/4/bar.txt", "zap/1/2/3", "zap/1/2/3/4", "zap/1/2/3/4/bar.txt", "zap/1/2/c", "zap/1/2/c/bar.txt"),
},
@ -263,88 +263,114 @@ IAAAAAAAsDyZDwU=`
cases := []struct {
name string
payload map[string][]byte
payload map[string]FileProjection
success bool
}{
{
name: "invalid payload 1",
payload: map[string][]byte{
"foo": []byte("foo"),
"..bar": []byte("bar"),
"binary.bin": mysteryBinaryBytes,
payload: map[string]FileProjection{
"foo": {Mode: 0644, Data: []byte("foo")},
"..bar": {Mode: 0644, Data: []byte("bar")},
"binary.bin": {Mode: 0644, Data: mysteryBinaryBytes},
},
success: false,
},
{
name: "invalid payload 2",
payload: map[string][]byte{
"foo/../bar": []byte("foo"),
payload: map[string]FileProjection{
"foo/../bar": {Mode: 0644, Data: []byte("foo")},
},
success: false,
},
{
name: "basic 1",
payload: map[string][]byte{
"foo": []byte("foo"),
"bar": []byte("bar"),
payload: map[string]FileProjection{
"foo": {Mode: 0644, Data: []byte("foo")},
"bar": {Mode: 0644, Data: []byte("bar")},
},
success: true,
},
{
name: "basic 2",
payload: map[string][]byte{
"binary.bin": mysteryBinaryBytes,
".binary.bin": mysteryBinaryBytes,
payload: map[string]FileProjection{
"binary.bin": {Mode: 0644, Data: mysteryBinaryBytes},
".binary.bin": {Mode: 0644, Data: mysteryBinaryBytes},
},
success: true,
},
{
name: "basic mode 1",
payload: map[string]FileProjection{
"foo": {Mode: 0777, Data: []byte("foo")},
"bar": {Mode: 0400, Data: []byte("bar")},
},
success: true,
},
{
name: "dotfiles",
payload: map[string][]byte{
"foo": []byte("foo"),
"bar": []byte("bar"),
".dotfile": []byte("dotfile"),
".dotfile.file": []byte("dotfile.file"),
payload: map[string]FileProjection{
"foo": {Mode: 0644, Data: []byte("foo")},
"bar": {Mode: 0644, Data: []byte("bar")},
".dotfile": {Mode: 0644, Data: []byte("dotfile")},
".dotfile.file": {Mode: 0644, Data: []byte("dotfile.file")},
},
success: true,
},
{
name: "dotfiles mode",
payload: map[string]FileProjection{
"foo": {Mode: 0407, Data: []byte("foo")},
"bar": {Mode: 0440, Data: []byte("bar")},
".dotfile": {Mode: 0777, Data: []byte("dotfile")},
".dotfile.file": {Mode: 0666, Data: []byte("dotfile.file")},
},
success: true,
},
{
name: "subdirectories 1",
payload: map[string][]byte{
"foo/bar.txt": []byte("foo/bar"),
"bar/zab.txt": []byte("bar/zab.txt"),
payload: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
},
success: true,
},
{
name: "subdirectories mode 1",
payload: map[string]FileProjection{
"foo/bar.txt": {Mode: 0400, Data: []byte("foo/bar")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
},
success: true,
},
{
name: "subdirectories 2",
payload: map[string][]byte{
"foo//bar.txt": []byte("foo//bar"),
"bar///bar/zab.txt": []byte("bar/../bar/zab.txt"),
payload: map[string]FileProjection{
"foo//bar.txt": {Mode: 0644, Data: []byte("foo//bar")},
"bar///bar/zab.txt": {Mode: 0644, Data: []byte("bar/../bar/zab.txt")},
},
success: true,
},
{
name: "subdirectories 3",
payload: map[string][]byte{
"foo/bar.txt": []byte("foo/bar"),
"bar/zab.txt": []byte("bar/zab.txt"),
"foo/blaz/bar.txt": []byte("foo/blaz/bar"),
"bar/zib/zab.txt": []byte("bar/zib/zab.txt"),
payload: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar")},
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt")},
},
success: true,
},
{
name: "kitchen sink",
payload: map[string][]byte{
"foo.log": []byte("foo"),
"bar.zap": []byte("bar"),
".dotfile": []byte("dotfile"),
"foo/bar.txt": []byte("foo/bar"),
"bar/zab.txt": []byte("bar/zab.txt"),
"foo/blaz/bar.txt": []byte("foo/blaz/bar"),
"bar/zib/zab.txt": []byte("bar/zib/zab.txt"),
"1/2/3/4/5/6/7/8/9/10/.dotfile.lib": []byte("1-2-3-dotfile"),
payload: map[string]FileProjection{
"foo.log": {Mode: 0644, Data: []byte("foo")},
"bar.zap": {Mode: 0644, Data: []byte("bar")},
".dotfile": {Mode: 0644, Data: []byte("dotfile")},
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar")},
"bar/zib/zab.txt": {Mode: 0400, Data: []byte("bar/zib/zab.txt")},
"1/2/3/4/5/6/7/8/9/10/.dotfile.lib": {Mode: 0777, Data: []byte("1-2-3-dotfile")},
},
success: true,
},
@ -376,150 +402,150 @@ IAAAAAAAsDyZDwU=`
func TestUpdate(t *testing.T) {
cases := []struct {
name string
first map[string][]byte
next map[string][]byte
first map[string]FileProjection
next map[string]FileProjection
shouldWrite bool
}{
{
name: "update",
first: map[string][]byte{
"foo": []byte("foo"),
"bar": []byte("bar"),
first: map[string]FileProjection{
"foo": {Mode: 0644, Data: []byte("foo")},
"bar": {Mode: 0644, Data: []byte("bar")},
},
next: map[string][]byte{
"foo": []byte("foo2"),
"bar": []byte("bar2"),
next: map[string]FileProjection{
"foo": {Mode: 0644, Data: []byte("foo2")},
"bar": {Mode: 0640, Data: []byte("bar2")},
},
shouldWrite: true,
},
{
name: "no update",
first: map[string][]byte{
"foo": []byte("foo"),
"bar": []byte("bar"),
first: map[string]FileProjection{
"foo": {Mode: 0644, Data: []byte("foo")},
"bar": {Mode: 0644, Data: []byte("bar")},
},
next: map[string][]byte{
"foo": []byte("foo"),
"bar": []byte("bar"),
next: map[string]FileProjection{
"foo": {Mode: 0644, Data: []byte("foo")},
"bar": {Mode: 0644, Data: []byte("bar")},
},
shouldWrite: false,
},
{
name: "no update 2",
first: map[string][]byte{
"foo/bar.txt": []byte("foo"),
"bar/zab.txt": []byte("bar"),
first: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
},
next: map[string][]byte{
"foo/bar.txt": []byte("foo"),
"bar/zab.txt": []byte("bar"),
next: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
},
shouldWrite: false,
},
{
name: "add 1",
first: map[string][]byte{
"foo/bar.txt": []byte("foo"),
"bar/zab.txt": []byte("bar"),
first: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
},
next: map[string][]byte{
"foo/bar.txt": []byte("foo"),
"bar/zab.txt": []byte("bar"),
"blu/zip.txt": []byte("zip"),
next: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
"blu/zip.txt": {Mode: 0644, Data: []byte("zip")},
},
shouldWrite: true,
},
{
name: "add 2",
first: map[string][]byte{
"foo/bar.txt": []byte("foo"),
"bar/zab.txt": []byte("bar"),
first: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
},
next: map[string][]byte{
"foo/bar.txt": []byte("foo"),
"bar/zab.txt": []byte("bar"),
"blu/two/2/3/4/5/zip.txt": []byte("zip"),
next: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
"blu/two/2/3/4/5/zip.txt": {Mode: 0644, Data: []byte("zip")},
},
shouldWrite: true,
},
{
name: "add 3",
first: map[string][]byte{
"foo/bar.txt": []byte("foo"),
"bar/zab.txt": []byte("bar"),
first: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
},
next: map[string][]byte{
"foo/bar.txt": []byte("foo"),
"bar/zab.txt": []byte("bar"),
"bar/2/3/4/5/zip.txt": []byte("zip"),
next: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
"bar/2/3/4/5/zip.txt": {Mode: 0644, Data: []byte("zip")},
},
shouldWrite: true,
},
{
name: "delete 1",
first: map[string][]byte{
"foo/bar.txt": []byte("foo"),
"bar/zab.txt": []byte("bar"),
first: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
},
next: map[string][]byte{
"foo/bar.txt": []byte("foo"),
next: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
},
shouldWrite: true,
},
{
name: "delete 2",
first: map[string][]byte{
"foo/bar.txt": []byte("foo"),
"bar/1/2/3/zab.txt": []byte("bar"),
first: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
"bar/1/2/3/zab.txt": {Mode: 0644, Data: []byte("bar")},
},
next: map[string][]byte{
"foo/bar.txt": []byte("foo"),
next: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
},
shouldWrite: true,
},
{
name: "delete 3",
first: map[string][]byte{
"foo/bar.txt": []byte("foo"),
"bar/1/2/sip.txt": []byte("sip"),
"bar/1/2/3/zab.txt": []byte("bar"),
first: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
"bar/1/2/sip.txt": {Mode: 0644, Data: []byte("sip")},
"bar/1/2/3/zab.txt": {Mode: 0644, Data: []byte("bar")},
},
next: map[string][]byte{
"foo/bar.txt": []byte("foo"),
"bar/1/2/sip.txt": []byte("sip"),
next: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
"bar/1/2/sip.txt": {Mode: 0644, Data: []byte("sip")},
},
shouldWrite: true,
},
{
name: "delete 4",
first: map[string][]byte{
"foo/bar.txt": []byte("foo"),
"bar/1/2/sip.txt": []byte("sip"),
"bar/1/2/3/4/5/6zab.txt": []byte("bar"),
first: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
"bar/1/2/sip.txt": {Mode: 0644, Data: []byte("sip")},
"bar/1/2/3/4/5/6zab.txt": {Mode: 0644, Data: []byte("bar")},
},
next: map[string][]byte{
"foo/bar.txt": []byte("foo"),
"bar/1/2/sip.txt": []byte("sip"),
next: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
"bar/1/2/sip.txt": {Mode: 0644, Data: []byte("sip")},
},
shouldWrite: true,
},
{
name: "delete all",
first: map[string][]byte{
"foo/bar.txt": []byte("foo"),
"bar/1/2/sip.txt": []byte("sip"),
"bar/1/2/3/4/5/6zab.txt": []byte("bar"),
first: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
"bar/1/2/sip.txt": {Mode: 0644, Data: []byte("sip")},
"bar/1/2/3/4/5/6zab.txt": {Mode: 0644, Data: []byte("bar")},
},
next: map[string][]byte{},
next: map[string]FileProjection{},
shouldWrite: true,
},
{
name: "add and delete 1",
first: map[string][]byte{
"foo/bar.txt": []byte("foo"),
first: map[string]FileProjection{
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
},
next: map[string][]byte{
"bar/baz.txt": []byte("baz"),
next: map[string]FileProjection{
"bar/baz.txt": {Mode: 0644, Data: []byte("baz")},
},
shouldWrite: true,
},
@ -563,130 +589,130 @@ func TestUpdate(t *testing.T) {
func TestMultipleUpdates(t *testing.T) {
cases := []struct {
name string
payloads []map[string][]byte
payloads []map[string]FileProjection
}{
{
name: "update 1",
payloads: []map[string][]byte{
payloads: []map[string]FileProjection{
{
"foo": []byte("foo"),
"bar": []byte("bar"),
"foo": {Mode: 0644, Data: []byte("foo")},
"bar": {Mode: 0644, Data: []byte("bar")},
},
{
"foo": []byte("foo2"),
"bar": []byte("bar2"),
"foo": {Mode: 0400, Data: []byte("foo2")},
"bar": {Mode: 0400, Data: []byte("bar2")},
},
{
"foo": []byte("foo3"),
"bar": []byte("bar3"),
"foo": {Mode: 0600, Data: []byte("foo3")},
"bar": {Mode: 0600, Data: []byte("bar3")},
},
},
},
{
name: "update 2",
payloads: []map[string][]byte{
payloads: []map[string]FileProjection{
{
"foo/bar.txt": []byte("foo/bar"),
"bar/zab.txt": []byte("bar/zab.txt"),
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
},
{
"foo/bar.txt": []byte("foo/bar2"),
"bar/zab.txt": []byte("bar/zab.txt2"),
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
"bar/zab.txt": {Mode: 0400, Data: []byte("bar/zab.txt2")},
},
},
},
{
name: "clear sentinel",
payloads: []map[string][]byte{
payloads: []map[string]FileProjection{
{
"foo": []byte("foo"),
"bar": []byte("bar"),
"foo": {Mode: 0644, Data: []byte("foo")},
"bar": {Mode: 0644, Data: []byte("bar")},
},
{
"foo": []byte("foo2"),
"bar": []byte("bar2"),
"foo": {Mode: 0644, Data: []byte("foo2")},
"bar": {Mode: 0644, Data: []byte("bar2")},
},
{
"foo": []byte("foo3"),
"bar": []byte("bar3"),
"foo": {Mode: 0644, Data: []byte("foo3")},
"bar": {Mode: 0644, Data: []byte("bar3")},
},
{
"foo": []byte("foo4"),
"bar": []byte("bar4"),
"foo": {Mode: 0644, Data: []byte("foo4")},
"bar": {Mode: 0644, Data: []byte("bar4")},
},
},
},
{
name: "subdirectories 2",
payloads: []map[string][]byte{
payloads: []map[string]FileProjection{
{
"foo/bar.txt": []byte("foo/bar"),
"bar/zab.txt": []byte("bar/zab.txt"),
"foo/blaz/bar.txt": []byte("foo/blaz/bar"),
"bar/zib/zab.txt": []byte("bar/zib/zab.txt"),
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar")},
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt")},
},
{
"foo/bar.txt": []byte("foo/bar2"),
"bar/zab.txt": []byte("bar/zab.txt2"),
"foo/blaz/bar.txt": []byte("foo/blaz/bar2"),
"bar/zib/zab.txt": []byte("bar/zib/zab.txt2"),
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt2")},
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar2")},
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt2")},
},
},
},
{
name: "add 1",
payloads: []map[string][]byte{
payloads: []map[string]FileProjection{
{
"foo/bar.txt": []byte("foo/bar"),
"bar//zab.txt": []byte("bar/zab.txt"),
"foo/blaz/bar.txt": []byte("foo/blaz/bar"),
"bar/zib////zib/zab.txt": []byte("bar/zib/zab.txt"),
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
"bar//zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar")},
"bar/zib////zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt")},
},
{
"foo/bar.txt": []byte("foo/bar2"),
"bar/zab.txt": []byte("bar/zab.txt2"),
"foo/blaz/bar.txt": []byte("foo/blaz/bar2"),
"bar/zib/zab.txt": []byte("bar/zib/zab.txt2"),
"add/new/keys.txt": []byte("addNewKeys"),
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt2")},
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar2")},
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt2")},
"add/new/keys.txt": {Mode: 0644, Data: []byte("addNewKeys")},
},
},
},
{
name: "add 2",
payloads: []map[string][]byte{
payloads: []map[string]FileProjection{
{
"foo/bar.txt": []byte("foo/bar2"),
"bar/zab.txt": []byte("bar/zab.txt2"),
"foo/blaz/bar.txt": []byte("foo/blaz/bar2"),
"bar/zib/zab.txt": []byte("bar/zib/zab.txt2"),
"add/new/keys.txt": []byte("addNewKeys"),
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt2")},
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar2")},
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt2")},
"add/new/keys.txt": {Mode: 0644, Data: []byte("addNewKeys")},
},
{
"foo/bar.txt": []byte("foo/bar2"),
"bar/zab.txt": []byte("bar/zab.txt2"),
"foo/blaz/bar.txt": []byte("foo/blaz/bar2"),
"bar/zib/zab.txt": []byte("bar/zib/zab.txt2"),
"add/new/keys.txt": []byte("addNewKeys"),
"add/new/keys2.txt": []byte("addNewKeys2"),
"add/new/keys3.txt": []byte("addNewKeys3"),
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt2")},
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar2")},
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt2")},
"add/new/keys.txt": {Mode: 0644, Data: []byte("addNewKeys")},
"add/new/keys2.txt": {Mode: 0644, Data: []byte("addNewKeys2")},
"add/new/keys3.txt": {Mode: 0644, Data: []byte("addNewKeys3")},
},
},
},
{
name: "remove 1",
payloads: []map[string][]byte{
payloads: []map[string]FileProjection{
{
"foo/bar.txt": []byte("foo/bar"),
"bar//zab.txt": []byte("bar/zab.txt"),
"foo/blaz/bar.txt": []byte("foo/blaz/bar"),
"zip/zap/zup/fop.txt": []byte("zip/zap/zup/fop.txt"),
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
"bar//zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar")},
"zip/zap/zup/fop.txt": {Mode: 0644, Data: []byte("zip/zap/zup/fop.txt")},
},
{
"foo/bar.txt": []byte("foo/bar2"),
"bar/zab.txt": []byte("bar/zab.txt2"),
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt2")},
},
{
"foo/bar.txt": []byte("foo/bar"),
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
},
},
},
@ -709,9 +735,9 @@ func TestMultipleUpdates(t *testing.T) {
}
}
func checkVolumeContents(targetDir, tcName string, payload map[string][]byte, t *testing.T) {
func checkVolumeContents(targetDir, tcName string, payload map[string]FileProjection, t *testing.T) {
// use filepath.Walk to reconstruct the payload, then deep equal
observedPayload := map[string][]byte{}
observedPayload := make(map[string]FileProjection)
visitor := func(path string, info os.FileInfo, err error) error {
if info.Mode().IsRegular() || info.IsDir() {
return nil
@ -727,7 +753,13 @@ func checkVolumeContents(targetDir, tcName string, payload map[string][]byte, t
if err != nil {
return err
}
observedPayload[relativePath] = content
fileInfo, err := os.Stat(path)
if err != nil {
return err
}
mode := int32(fileInfo.Mode())
observedPayload[relativePath] = FileProjection{Data: content, Mode: mode}
return nil
}
@ -737,7 +769,7 @@ func checkVolumeContents(targetDir, tcName string, payload map[string][]byte, t
t.Errorf("%v: unexpected error walking directory: %v", tcName, err)
}
cleanPathPayload := make(map[string][]byte, len(payload))
cleanPathPayload := make(map[string]FileProjection, len(payload))
for k, v := range payload {
cleanPathPayload[path.Clean(k)] = v
}

View File

@ -18,6 +18,7 @@ package common
import (
"fmt"
"os"
"time"
"k8s.io/kubernetes/pkg/api"
@ -32,27 +33,37 @@ var _ = framework.KubeDescribe("ConfigMap", func() {
f := framework.NewDefaultFramework("configmap")
It("should be consumable from pods in volume [Conformance]", func() {
doConfigMapE2EWithoutMappings(f, 0, 0)
doConfigMapE2EWithoutMappings(f, 0, 0, nil)
})
It("should be consumable from pods in volume with defaultMode set [Conformance]", func() {
defaultMode := int32(0400)
doConfigMapE2EWithoutMappings(f, 0, 0, &defaultMode)
})
It("should be consumable from pods in volume as non-root [Conformance]", func() {
doConfigMapE2EWithoutMappings(f, 1000, 0)
doConfigMapE2EWithoutMappings(f, 1000, 0, nil)
})
It("should be consumable from pods in volume as non-root with FSGroup [Feature:FSGroup]", func() {
doConfigMapE2EWithoutMappings(f, 1000, 1001)
doConfigMapE2EWithoutMappings(f, 1000, 1001, nil)
})
It("should be consumable from pods in volume with mappings [Conformance]", func() {
doConfigMapE2EWithMappings(f, 0, 0)
doConfigMapE2EWithMappings(f, 0, 0, nil)
})
It("should be consumable from pods in volume with mappings and Item mode set[Conformance]", func() {
mode := int32(0400)
doConfigMapE2EWithMappings(f, 0, 0, &mode)
})
It("should be consumable from pods in volume with mappings as non-root [Conformance]", func() {
doConfigMapE2EWithMappings(f, 1000, 0)
doConfigMapE2EWithMappings(f, 1000, 0, nil)
})
It("should be consumable from pods in volume with mappings as non-root with FSGroup [Feature:FSGroup]", func() {
doConfigMapE2EWithMappings(f, 1000, 1001)
doConfigMapE2EWithMappings(f, 1000, 1001, nil)
})
It("updates should be reflected in volume [Conformance]", func() {
@ -290,7 +301,7 @@ func newConfigMap(f *framework.Framework, name string) *api.ConfigMap {
}
}
func doConfigMapE2EWithoutMappings(f *framework.Framework, uid, fsGroup int64) {
func doConfigMapE2EWithoutMappings(f *framework.Framework, uid, fsGroup int64, defaultMode *int32) {
var (
name = "configmap-test-volume-" + string(uuid.NewUUID())
volumeName = "configmap-volume"
@ -331,13 +342,14 @@ func doConfigMapE2EWithoutMappings(f *framework.Framework, uid, fsGroup int64) {
Containers: []api.Container{
{
Name: "configmap-volume-test",
Image: "gcr.io/google_containers/mounttest:0.6",
Args: []string{"--file_content=/etc/configmap-volume/data-1"},
Image: "gcr.io/google_containers/mounttest:0.7",
Args: []string{
"--file_content=/etc/configmap-volume/data-1",
"--file_mode=/etc/configmap-volume/data-1"},
VolumeMounts: []api.VolumeMount{
{
Name: volumeName,
MountPath: volumeMountPath,
ReadOnly: true,
},
},
},
@ -353,14 +365,27 @@ func doConfigMapE2EWithoutMappings(f *framework.Framework, uid, fsGroup int64) {
if fsGroup != 0 {
pod.Spec.SecurityContext.FSGroup = &fsGroup
}
if defaultMode != nil {
pod.Spec.Volumes[0].VolumeSource.ConfigMap.DefaultMode = defaultMode
} else {
mode := int32(0644)
defaultMode = &mode
}
f.TestContainerOutput("consume configMaps", pod, 0, []string{
// Just check file mode if fsGroup is not set. If fsGroup is set, the
// final mode is adjusted and we are not testing that case.
output := []string{
"content of file \"/etc/configmap-volume/data-1\": value-1",
})
}
if fsGroup == 0 {
modeString := fmt.Sprintf("%v", os.FileMode(*defaultMode))
output = append(output, "mode of file \"/etc/configmap-volume/data-1\": "+modeString)
}
f.TestContainerOutput("consume configMaps", pod, 0, output)
}
func doConfigMapE2EWithMappings(f *framework.Framework, uid, fsGroup int64) {
func doConfigMapE2EWithMappings(f *framework.Framework, uid, fsGroup int64, itemMode *int32) {
var (
name = "configmap-test-volume-map-" + string(uuid.NewUUID())
volumeName = "configmap-volume"
@ -407,8 +432,9 @@ func doConfigMapE2EWithMappings(f *framework.Framework, uid, fsGroup int64) {
Containers: []api.Container{
{
Name: "configmap-volume-test",
Image: "gcr.io/google_containers/mounttest:0.6",
Args: []string{"--file_content=/etc/configmap-volume/path/to/data-2"},
Image: "gcr.io/google_containers/mounttest:0.7",
Args: []string{"--file_content=/etc/configmap-volume/path/to/data-2",
"--file_mode=/etc/configmap-volume/path/to/data-2"},
VolumeMounts: []api.VolumeMount{
{
Name: volumeName,
@ -429,8 +455,21 @@ func doConfigMapE2EWithMappings(f *framework.Framework, uid, fsGroup int64) {
if fsGroup != 0 {
pod.Spec.SecurityContext.FSGroup = &fsGroup
}
f.TestContainerOutput("consume configMaps", pod, 0, []string{
"content of file \"/etc/configmap-volume/path/to/data-2\": value-2",
})
if itemMode != nil {
pod.Spec.Volumes[0].VolumeSource.ConfigMap.Items[0].Mode = itemMode
} else {
mode := int32(0644)
itemMode = &mode
}
// Just check file mode if fsGroup is not set. If fsGroup is set, the
// final mode is adjusted and we are not testing that case.
output := []string{
"content of file \"/etc/configmap-volume/path/to/data-2\": value-2",
}
if fsGroup == 0 {
modeString := fmt.Sprintf("%v", os.FileMode(*itemMode))
output = append(output, "mode of file \"/etc/configmap-volume/path/to/data-2\": "+modeString)
}
f.TestContainerOutput("consume configMaps", pod, 0, output)
}

View File

@ -47,6 +47,26 @@ var _ = framework.KubeDescribe("Downward API volume", func() {
})
})
It("should set DefaultMode on files [Conformance]", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
defaultMode := int32(0400)
pod := downwardAPIVolumePodForModeTest(podName, "/etc/podname", nil, &defaultMode)
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
"mode of file \"/etc/podname\": -r--------",
})
})
It("should set mode on item file [Conformance]", func() {
podName := "downwardapi-volume-" + string(uuid.NewUUID())
mode := int32(0400)
pod := downwardAPIVolumePodForModeTest(podName, "/etc/podname", &mode, nil)
f.TestContainerOutput("downward API volume plugin", pod, 0, []string{
"mode of file \"/etc/podname\": -r--------",
})
})
It("should provide podname as non-root with fsgroup [Feature:FSGroup]", func() {
podName := "metadata-volume-" + string(uuid.NewUUID())
uid := int64(1001)
@ -179,6 +199,32 @@ var _ = framework.KubeDescribe("Downward API volume", func() {
})
func downwardAPIVolumePodForModeTest(name, filePath string, itemMode, defaultMode *int32) *api.Pod {
pod := downwardAPIVolumeBasePod(name, nil, nil)
pod.Spec.Containers = []api.Container{
{
Name: "client-container",
Image: "gcr.io/google_containers/mounttest:0.7",
Command: []string{"/mt", "--file_mode=" + filePath},
VolumeMounts: []api.VolumeMount{
{
Name: "podinfo",
MountPath: "/etc",
},
},
},
}
if itemMode != nil {
pod.Spec.Volumes[0].VolumeSource.DownwardAPI.Items[0].Mode = itemMode
}
if defaultMode != nil {
pod.Spec.Volumes[0].VolumeSource.DownwardAPI.DefaultMode = defaultMode
}
return pod
}
func downwardAPIVolumePodForSimpleTest(name string, filePath string) *api.Pod {
pod := downwardAPIVolumeBasePod(name, nil, nil)

View File

@ -73,7 +73,6 @@ var _ = framework.KubeDescribe("Secrets", func() {
{
Name: volumeName,
MountPath: volumeMountPath,
ReadOnly: true,
},
},
},
@ -88,6 +87,133 @@ var _ = framework.KubeDescribe("Secrets", func() {
})
})
It("should be consumable from pods in volume with defaultMode set [Conformance]", func() {
name := "secret-test-defaultmode-" + string(uuid.NewUUID())
volumeName := "secret-volume"
volumeMountPath := "/etc/secret-volume"
secret := secretForTest(f.Namespace.Name, name)
By(fmt.Sprintf("Creating secret with name %s", secret.Name))
defer func() {
By("Cleaning up the secret")
if err := f.Client.Secrets(f.Namespace.Name).Delete(secret.Name); err != nil {
framework.Failf("unable to delete secret %v: %v", secret.Name, err)
}
}()
var err error
if secret, err = f.Client.Secrets(f.Namespace.Name).Create(secret); err != nil {
framework.Failf("unable to create test secret %s: %v", secret.Name, err)
}
defaultMode := int32(0400)
pod := &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "pod-secrets-" + string(uuid.NewUUID()),
},
Spec: api.PodSpec{
Volumes: []api.Volume{
{
Name: volumeName,
VolumeSource: api.VolumeSource{
Secret: &api.SecretVolumeSource{
SecretName: name,
DefaultMode: &defaultMode,
},
},
},
},
Containers: []api.Container{
{
Name: "secret-volume-test",
Image: "gcr.io/google_containers/mounttest:0.7",
Args: []string{
"--file_content=/etc/secret-volume/data-1",
"--file_mode=/etc/secret-volume/data-1"},
VolumeMounts: []api.VolumeMount{
{
Name: volumeName,
MountPath: volumeMountPath,
ReadOnly: true,
},
},
},
},
RestartPolicy: api.RestartPolicyNever,
},
}
f.TestContainerOutput("consume secrets", pod, 0, []string{
"content of file \"/etc/secret-volume/data-1\": value-1",
"mode of file \"/etc/secret-volume/data-1\": -r--------",
})
})
It("should be consumable from pods in volume with Mode set in the item [Conformance]", func() {
name := "secret-test-itemmode-" + string(uuid.NewUUID())
volumeName := "secret-volume"
volumeMountPath := "/etc/secret-volume"
secret := secretForTest(f.Namespace.Name, name)
By(fmt.Sprintf("Creating secret with name %s", secret.Name))
defer func() {
By("Cleaning up the secret")
if err := f.Client.Secrets(f.Namespace.Name).Delete(secret.Name); err != nil {
framework.Failf("unable to delete secret %v: %v", secret.Name, err)
}
}()
var err error
if secret, err = f.Client.Secrets(f.Namespace.Name).Create(secret); err != nil {
framework.Failf("unable to create test secret %s: %v", secret.Name, err)
}
mode := int32(0400)
pod := &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: "pod-secrets-" + string(uuid.NewUUID()),
},
Spec: api.PodSpec{
Volumes: []api.Volume{
{
Name: volumeName,
VolumeSource: api.VolumeSource{
Secret: &api.SecretVolumeSource{
SecretName: name,
Items: []api.KeyToPath{
{
Key: "data-1",
Path: "data-1",
Mode: &mode,
},
},
},
},
},
},
Containers: []api.Container{
{
Name: "secret-volume-test",
Image: "gcr.io/google_containers/mounttest:0.7",
Args: []string{
"--file_content=/etc/secret-volume/data-1",
"--file_mode=/etc/secret-volume/data-1"},
VolumeMounts: []api.VolumeMount{
{
Name: volumeName,
MountPath: volumeMountPath,
},
},
},
},
RestartPolicy: api.RestartPolicyNever,
},
}
f.TestContainerOutput("consume secrets", pod, 0, []string{
"content of file \"/etc/secret-volume/data-1\": value-1",
"mode of file \"/etc/secret-volume/data-1\": -r--------",
})
})
It("should be consumable in multiple volumes in a pod [Conformance]", func() {
// This test ensures that the same secret can be mounted in multiple
// volumes in the same pod. This test case exists to prevent

View File

@ -237,12 +237,14 @@ func TestServiceAccountTokenAutoMount(t *testing.T) {
}
// Pod we expect to get created
defaultMode := int32(0644)
expectedServiceAccount := serviceaccountadmission.DefaultServiceAccountName
expectedVolumes := append(protoPod.Spec.Volumes, api.Volume{
Name: defaultTokenName,
VolumeSource: api.VolumeSource{
Secret: &api.SecretVolumeSource{
SecretName: defaultTokenName,
DefaultMode: &defaultMode,
},
},
})