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:
parent
2bc5414de6
commit
568f4c2e63
@ -251,15 +251,59 @@ func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source)
|
|||||||
policies := []api.RestartPolicy{api.RestartPolicyAlways, api.RestartPolicyNever, api.RestartPolicyOnFailure}
|
policies := []api.RestartPolicy{api.RestartPolicyAlways, api.RestartPolicyNever, api.RestartPolicyOnFailure}
|
||||||
*rp = policies[c.Rand.Intn(len(policies))]
|
*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
|
// 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) {
|
func(m *api.DownwardAPIVolumeFile, c fuzz.Continue) {
|
||||||
m.Path = c.RandString()
|
m.Path = c.RandString()
|
||||||
versions := []string{"v1"}
|
versions := []string{"v1"}
|
||||||
m.FieldRef = &api.ObjectFieldSelector{}
|
m.FieldRef = &api.ObjectFieldSelector{}
|
||||||
m.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))]
|
m.FieldRef.APIVersion = versions[c.Rand.Intn(len(versions))]
|
||||||
m.FieldRef.FieldPath = c.RandString()
|
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) {
|
func(vs *api.VolumeSource, c fuzz.Continue) {
|
||||||
// Exactly one of the fields must be set.
|
// Exactly one of the fields must be set.
|
||||||
|
@ -607,6 +607,12 @@ type SecretVolumeSource struct {
|
|||||||
// the volume setup will error. Paths must be relative and may not contain
|
// the volume setup will error. Paths must be relative and may not contain
|
||||||
// the '..' path or start with '..'.
|
// the '..' path or start with '..'.
|
||||||
Items []KeyToPath `json:"items,omitempty"`
|
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.
|
// Represents an NFS mount that lasts the lifetime of a pod.
|
||||||
@ -708,6 +714,12 @@ type FlockerVolumeSource struct {
|
|||||||
type DownwardAPIVolumeSource struct {
|
type DownwardAPIVolumeSource struct {
|
||||||
// Items is a list of DownwardAPIVolume file
|
// Items is a list of DownwardAPIVolume file
|
||||||
Items []DownwardAPIVolumeFile `json:"items,omitempty"`
|
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
|
// 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
|
// Selects a resource of the container: only resources limits and requests
|
||||||
// (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.
|
// (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.
|
||||||
ResourceFieldRef *ResourceFieldSelector `json:"resourceFieldRef,omitempty"`
|
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.
|
// 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 volume setup will error. Paths must be relative and may not contain
|
||||||
// the '..' path or start with '..'.
|
// the '..' path or start with '..'.
|
||||||
Items []KeyToPath `json:"items,omitempty"`
|
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.
|
// 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 contain the path element '..'.
|
||||||
// May not start with the string '..'.
|
// May not start with the string '..'.
|
||||||
Path string `json:"path"`
|
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
|
// ContainerPort represents a network port in a single container
|
||||||
|
@ -35,6 +35,9 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
|||||||
SetDefaults_Pod,
|
SetDefaults_Pod,
|
||||||
SetDefaults_PodSpec,
|
SetDefaults_PodSpec,
|
||||||
SetDefaults_Probe,
|
SetDefaults_Probe,
|
||||||
|
SetDefaults_SecretVolumeSource,
|
||||||
|
SetDefaults_ConfigMapVolumeSource,
|
||||||
|
SetDefaults_DownwardAPIVolumeSource,
|
||||||
SetDefaults_Secret,
|
SetDefaults_Secret,
|
||||||
SetDefaults_PersistentVolume,
|
SetDefaults_PersistentVolume,
|
||||||
SetDefaults_PersistentVolumeClaim,
|
SetDefaults_PersistentVolumeClaim,
|
||||||
@ -174,6 +177,24 @@ func SetDefaults_Probe(obj *Probe) {
|
|||||||
obj.FailureThreshold = 3
|
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) {
|
func SetDefaults_Secret(obj *Secret) {
|
||||||
if obj.Type == "" {
|
if obj.Type == "" {
|
||||||
obj.Type = SecretTypeOpaque
|
obj.Type = SecretTypeOpaque
|
||||||
|
@ -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) {
|
func TestSetDefaultSecret(t *testing.T) {
|
||||||
s := &versioned.Secret{}
|
s := &versioned.Secret{}
|
||||||
obj2 := roundTrip(t, runtime.Object(s))
|
obj2 := roundTrip(t, runtime.Object(s))
|
||||||
|
@ -770,8 +770,18 @@ type SecretVolumeSource struct {
|
|||||||
// the volume setup will error. Paths must be relative and may not contain
|
// the volume setup will error. Paths must be relative and may not contain
|
||||||
// the '..' path or start with '..'.
|
// the '..' path or start with '..'.
|
||||||
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
|
||||||
|
// 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.
|
// Represents an NFS mount that lasts the lifetime of a pod.
|
||||||
// NFS volumes do not support ownership management or SELinux relabeling.
|
// NFS volumes do not support ownership management or SELinux relabeling.
|
||||||
type NFSVolumeSource struct {
|
type NFSVolumeSource struct {
|
||||||
@ -869,8 +879,18 @@ type ConfigMapVolumeSource struct {
|
|||||||
// the volume setup will error. Paths must be relative and may not contain
|
// the volume setup will error. Paths must be relative and may not contain
|
||||||
// the '..' path or start with '..'.
|
// the '..' path or start with '..'.
|
||||||
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
|
||||||
|
// 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.
|
// Maps a string key to a path within a volume.
|
||||||
type KeyToPath struct {
|
type KeyToPath struct {
|
||||||
// The key to project.
|
// The key to project.
|
||||||
@ -881,6 +901,11 @@ type KeyToPath struct {
|
|||||||
// May not contain the path element '..'.
|
// May not contain the path element '..'.
|
||||||
// May not start with the string '..'.
|
// May not start with the string '..'.
|
||||||
Path string `json:"path" protobuf:"bytes,2,opt,name=path"`
|
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.
|
// ContainerPort represents a network port in a single container.
|
||||||
@ -3287,8 +3312,18 @@ type ComponentStatusList struct {
|
|||||||
type DownwardAPIVolumeSource struct {
|
type DownwardAPIVolumeSource struct {
|
||||||
// Items is a list of downward API volume file
|
// Items is a list of downward API volume file
|
||||||
Items []DownwardAPIVolumeFile `json:"items,omitempty" protobuf:"bytes,1,rep,name=items"`
|
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
|
// DownwardAPIVolumeFile represents information to create the file containing the pod field
|
||||||
type DownwardAPIVolumeFile struct {
|
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 '..'
|
// 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
|
// Selects a resource of the container: only resources limits and requests
|
||||||
// (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.
|
// (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported.
|
||||||
ResourceFieldRef *ResourceFieldSelector `json:"resourceFieldRef,omitempty" protobuf:"bytes,3,opt,name=resourceFieldRef"`
|
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.
|
// SecurityContext holds security configuration that will be applied to a container.
|
||||||
|
@ -52,6 +52,7 @@ const fieldImmutableErrorMsg string = `field is immutable`
|
|||||||
const isNotIntegerErrorMsg string = `must be an integer`
|
const isNotIntegerErrorMsg string = `must be an integer`
|
||||||
|
|
||||||
var pdPartitionErrorMsg string = validation.InclusiveRangeError(1, 255)
|
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
|
const totalAnnotationSizeLimitB int = 256 * (1 << 10) // 256 kB
|
||||||
|
|
||||||
@ -660,6 +661,12 @@ func validateSecretVolumeSource(secretSource *api.SecretVolumeSource, fldPath *f
|
|||||||
if len(secretSource.SecretName) == 0 {
|
if len(secretSource.SecretName) == 0 {
|
||||||
allErrs = append(allErrs, field.Required(fldPath.Child("secretName"), ""))
|
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")
|
itemsPath := fldPath.Child("items")
|
||||||
for i, kp := range secretSource.Items {
|
for i, kp := range secretSource.Items {
|
||||||
itemPath := itemsPath.Index(i)
|
itemPath := itemsPath.Index(i)
|
||||||
@ -673,6 +680,12 @@ func validateConfigMapVolumeSource(configMapSource *api.ConfigMapVolumeSource, f
|
|||||||
if len(configMapSource.Name) == 0 {
|
if len(configMapSource.Name) == 0 {
|
||||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
|
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")
|
itemsPath := fldPath.Child("items")
|
||||||
for i, kp := range configMapSource.Items {
|
for i, kp := range configMapSource.Items {
|
||||||
itemPath := itemsPath.Index(i)
|
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, field.Required(fldPath.Child("path"), ""))
|
||||||
}
|
}
|
||||||
allErrs = append(allErrs, validateLocalNonReservedPath(kp.Path, 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
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -745,6 +762,12 @@ var validDownwardAPIFieldPathExpressions = sets.NewString(
|
|||||||
|
|
||||||
func validateDownwardAPIVolumeSource(downwardAPIVolume *api.DownwardAPIVolumeSource, fldPath *field.Path) field.ErrorList {
|
func validateDownwardAPIVolumeSource(downwardAPIVolume *api.DownwardAPIVolumeSource, fldPath *field.Path) field.ErrorList {
|
||||||
allErrs := 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 {
|
for _, file := range downwardAPIVolume.Items {
|
||||||
if len(file.Path) == 0 {
|
if len(file.Path) == 0 {
|
||||||
allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
|
allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
|
||||||
@ -760,6 +783,9 @@ func validateDownwardAPIVolumeSource(downwardAPIVolume *api.DownwardAPIVolumeSou
|
|||||||
} else {
|
} else {
|
||||||
allErrs = append(allErrs, field.Required(fldPath, "one of fieldRef and resourceFieldRef is required"))
|
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
|
return allErrs
|
||||||
}
|
}
|
||||||
|
@ -794,6 +794,10 @@ func TestValidateKeyToPath(t *testing.T) {
|
|||||||
kp: api.KeyToPath{Key: "k", Path: "p/..p/p../p..p"},
|
kp: api.KeyToPath{Key: "k", Path: "p/..p/p../p..p"},
|
||||||
ok: true,
|
ok: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
kp: api.KeyToPath{Key: "k", Path: "p", Mode: newInt32(0644)},
|
||||||
|
ok: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
kp: api.KeyToPath{Key: "", Path: "p"},
|
kp: api.KeyToPath{Key: "", Path: "p"},
|
||||||
ok: false,
|
ok: false,
|
||||||
@ -824,6 +828,16 @@ func TestValidateKeyToPath(t *testing.T) {
|
|||||||
ok: false,
|
ok: false,
|
||||||
errtype: field.ErrorTypeInvalid,
|
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 {
|
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{
|
vol: api.Volume{
|
||||||
Name: "secret",
|
Name: "secret",
|
||||||
VolumeSource: api.VolumeSource{
|
VolumeSource: api.VolumeSource{
|
||||||
@ -1138,6 +1164,7 @@ func TestValidateVolumes(t *testing.T) {
|
|||||||
Items: []api.KeyToPath{{
|
Items: []api.KeyToPath{{
|
||||||
Key: "key",
|
Key: "key",
|
||||||
Path: "filename",
|
Path: "filename",
|
||||||
|
Mode: newInt32(0644),
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1200,6 +1227,34 @@ func TestValidateVolumes(t *testing.T) {
|
|||||||
errtype: field.ErrorTypeInvalid,
|
errtype: field.ErrorTypeInvalid,
|
||||||
errfield: "secret.items[0].path",
|
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
|
// ConfigMap
|
||||||
{
|
{
|
||||||
name: "valid 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{
|
vol: api.Volume{
|
||||||
Name: "cfgmap",
|
Name: "cfgmap",
|
||||||
VolumeSource: api.VolumeSource{
|
VolumeSource: api.VolumeSource{
|
||||||
@ -1225,6 +1294,7 @@ func TestValidateVolumes(t *testing.T) {
|
|||||||
Items: []api.KeyToPath{{
|
Items: []api.KeyToPath{{
|
||||||
Key: "key",
|
Key: "key",
|
||||||
Path: "filename",
|
Path: "filename",
|
||||||
|
Mode: newInt32(0644),
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1288,6 +1358,34 @@ func TestValidateVolumes(t *testing.T) {
|
|||||||
errtype: field.ErrorTypeInvalid,
|
errtype: field.ErrorTypeInvalid,
|
||||||
errfield: "configMap.items[0].path",
|
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
|
// Glusterfs
|
||||||
{
|
{
|
||||||
name: "valid 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",
|
name: "downapi empty metatada path",
|
||||||
vol: api.Volume{
|
vol: api.Volume{
|
||||||
@ -1673,6 +1840,32 @@ func TestValidateVolumes(t *testing.T) {
|
|||||||
errfield: "downwardAPI",
|
errfield: "downwardAPI",
|
||||||
errdetail: "fieldRef and resourceFieldRef can not be specified simultaneously",
|
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
|
// FC
|
||||||
{
|
{
|
||||||
name: "valid FC",
|
name: "valid FC",
|
||||||
|
@ -175,7 +175,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)
|
payload, err := makePayload(b.source.Items, configMap, b.source.DefaultMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -202,22 +202,36 @@ func (b *configMapVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePayload(mappings []api.KeyToPath, configMap *api.ConfigMap) (map[string][]byte, error) {
|
func makePayload(mappings []api.KeyToPath, configMap *api.ConfigMap, defaultMode *int32) (map[string]volumeutil.FileProjection, error) {
|
||||||
payload := make(map[string][]byte, len(configMap.Data))
|
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 {
|
if len(mappings) == 0 {
|
||||||
for name, data := range configMap.Data {
|
for name, data := range configMap.Data {
|
||||||
payload[name] = []byte(data)
|
fileProjection.Data = []byte(data)
|
||||||
|
fileProjection.Mode = *defaultMode
|
||||||
|
payload[name] = fileProjection
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, ktp := range mappings {
|
for _, ktp := range mappings {
|
||||||
content, ok := configMap.Data[ktp.Key]
|
content, ok := configMap.Data[ktp.Key]
|
||||||
if !ok {
|
if !ok {
|
||||||
glog.Errorf("references non-existent config key")
|
err_msg := "references non-existent config key"
|
||||||
return nil, fmt.Errorf("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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,11 +36,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestMakePayload(t *testing.T) {
|
func TestMakePayload(t *testing.T) {
|
||||||
|
case_mapping_mode := int32(0400)
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
mappings []api.KeyToPath
|
mappings []api.KeyToPath
|
||||||
configMap *api.ConfigMap
|
configMap *api.ConfigMap
|
||||||
payload map[string][]byte
|
mode int32
|
||||||
|
payload map[string]util.FileProjection
|
||||||
success bool
|
success bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@ -51,9 +53,10 @@ func TestMakePayload(t *testing.T) {
|
|||||||
"bar": "bar",
|
"bar": "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
payload: map[string][]byte{
|
mode: 0644,
|
||||||
"foo": []byte("foo"),
|
payload: map[string]util.FileProjection{
|
||||||
"bar": []byte("bar"),
|
"foo": {Data: []byte("foo"), Mode: 0644},
|
||||||
|
"bar": {Data: []byte("bar"), Mode: 0644},
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
},
|
},
|
||||||
@ -71,8 +74,9 @@ func TestMakePayload(t *testing.T) {
|
|||||||
"bar": "bar",
|
"bar": "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
payload: map[string][]byte{
|
mode: 0644,
|
||||||
"path/to/foo.txt": []byte("foo"),
|
payload: map[string]util.FileProjection{
|
||||||
|
"path/to/foo.txt": {Data: []byte("foo"), Mode: 0644},
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
},
|
},
|
||||||
@ -90,8 +94,9 @@ func TestMakePayload(t *testing.T) {
|
|||||||
"bar": "bar",
|
"bar": "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
payload: map[string][]byte{
|
mode: 0644,
|
||||||
"path/to/1/2/3/foo.txt": []byte("foo"),
|
payload: map[string]util.FileProjection{
|
||||||
|
"path/to/1/2/3/foo.txt": {Data: []byte("foo"), Mode: 0644},
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
},
|
},
|
||||||
@ -109,8 +114,9 @@ func TestMakePayload(t *testing.T) {
|
|||||||
"bar": "bar",
|
"bar": "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
payload: map[string][]byte{
|
mode: 0644,
|
||||||
"path/to/1/2/3/foo.txt": []byte("foo"),
|
payload: map[string]util.FileProjection{
|
||||||
|
"path/to/1/2/3/foo.txt": {Data: []byte("foo"), Mode: 0644},
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
},
|
},
|
||||||
@ -132,9 +138,10 @@ func TestMakePayload(t *testing.T) {
|
|||||||
"bar": "bar",
|
"bar": "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
payload: map[string][]byte{
|
mode: 0644,
|
||||||
"path/to/1/2/3/foo.txt": []byte("foo"),
|
payload: map[string]util.FileProjection{
|
||||||
"another/path/to/the/esteemed/bar.bin": []byte("bar"),
|
"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,
|
success: true,
|
||||||
},
|
},
|
||||||
@ -152,12 +159,65 @@ func TestMakePayload(t *testing.T) {
|
|||||||
"bar": "bar",
|
"bar": "bar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mode: 0644,
|
||||||
success: false,
|
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 {
|
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 {
|
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
|
||||||
@ -214,7 +274,7 @@ func TestPlugin(t *testing.T) {
|
|||||||
testNamespace = "test_configmap_namespace"
|
testNamespace = "test_configmap_namespace"
|
||||||
testName = "test_configmap_name"
|
testName = "test_configmap_name"
|
||||||
|
|
||||||
volumeSpec = volumeSpec(testVolumeName, testName)
|
volumeSpec = volumeSpec(testVolumeName, testName, 0644)
|
||||||
configMap = configMap(testNamespace, testName)
|
configMap = configMap(testNamespace, testName)
|
||||||
client = fake.NewSimpleClientset(&configMap)
|
client = fake.NewSimpleClientset(&configMap)
|
||||||
pluginMgr = volume.VolumePluginMgr{}
|
pluginMgr = volume.VolumePluginMgr{}
|
||||||
@ -277,7 +337,7 @@ func TestPluginReboot(t *testing.T) {
|
|||||||
testNamespace = "test_configmap_namespace"
|
testNamespace = "test_configmap_namespace"
|
||||||
testName = "test_configmap_name"
|
testName = "test_configmap_name"
|
||||||
|
|
||||||
volumeSpec = volumeSpec(testVolumeName, testName)
|
volumeSpec = volumeSpec(testVolumeName, testName, 0644)
|
||||||
configMap = configMap(testNamespace, testName)
|
configMap = configMap(testNamespace, testName)
|
||||||
client = fake.NewSimpleClientset(&configMap)
|
client = fake.NewSimpleClientset(&configMap)
|
||||||
pluginMgr = volume.VolumePluginMgr{}
|
pluginMgr = volume.VolumePluginMgr{}
|
||||||
@ -324,7 +384,7 @@ func TestPluginReboot(t *testing.T) {
|
|||||||
doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, 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{
|
return &api.Volume{
|
||||||
Name: volumeName,
|
Name: volumeName,
|
||||||
VolumeSource: api.VolumeSource{
|
VolumeSource: api.VolumeSource{
|
||||||
@ -332,6 +392,7 @@ func volumeSpec(volumeName, configMapName string) *api.Volume {
|
|||||||
LocalObjectReference: api.LocalObjectReference{
|
LocalObjectReference: api.LocalObjectReference{
|
||||||
Name: configMapName,
|
Name: configMapName,
|
||||||
},
|
},
|
||||||
|
DefaultMode: &defaultMode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,7 @@ func (plugin *downwardAPIPlugin) NewMounter(spec *volume.Spec, pod *api.Pod, opt
|
|||||||
}
|
}
|
||||||
return &downwardAPIVolumeMounter{
|
return &downwardAPIVolumeMounter{
|
||||||
downwardAPIVolume: v,
|
downwardAPIVolume: v,
|
||||||
|
source: *spec.Volume.DownwardAPI,
|
||||||
opts: &opts,
|
opts: &opts,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -130,7 +131,8 @@ type downwardAPIVolume struct {
|
|||||||
// and dumps it in files
|
// and dumps it in files
|
||||||
type downwardAPIVolumeMounter struct {
|
type downwardAPIVolumeMounter struct {
|
||||||
*downwardAPIVolume
|
*downwardAPIVolume
|
||||||
opts *volume.VolumeOptions
|
source api.DownwardAPIVolumeSource
|
||||||
|
opts *volume.VolumeOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// downwardAPIVolumeMounter implements volume.Mounter interface
|
// downwardAPIVolumeMounter implements volume.Mounter interface
|
||||||
@ -166,7 +168,7 @@ func (b *downwardAPIVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := b.collectData()
|
data, err := b.collectData(b.source.DefaultMode)
|
||||||
if err != nil {
|
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())
|
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
|
return err
|
||||||
@ -193,16 +195,27 @@ func (b *downwardAPIVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||||||
// collectData collects requested downwardAPI in data map.
|
// collectData collects requested downwardAPI in data map.
|
||||||
// Map's key is the requested name of file to dump
|
// 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.
|
// 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{}
|
errlist := []error{}
|
||||||
data := make(map[string][]byte)
|
data := make(map[string]volumeutil.FileProjection)
|
||||||
for _, fileInfo := range d.items {
|
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 fileInfo.FieldRef != nil {
|
||||||
if values, err := fieldpath.ExtractFieldPathAsString(d.pod, fileInfo.FieldRef.FieldPath); err != 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())
|
glog.Errorf("Unable to extract field %s: %s", fileInfo.FieldRef.FieldPath, err.Error())
|
||||||
errlist = append(errlist, err)
|
errlist = append(errlist, err)
|
||||||
} else {
|
} else {
|
||||||
data[path.Clean(fileInfo.Path)] = []byte(sortLines(values))
|
fileProjection.Data = []byte(sortLines(values))
|
||||||
}
|
}
|
||||||
} else if fileInfo.ResourceFieldRef != nil {
|
} else if fileInfo.ResourceFieldRef != nil {
|
||||||
containerName := fileInfo.ResourceFieldRef.ContainerName
|
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())
|
glog.Errorf("Unable to extract field %s: %s", fileInfo.ResourceFieldRef.Resource, err.Error())
|
||||||
errlist = append(errlist, err)
|
errlist = append(errlist, err)
|
||||||
} else {
|
} else {
|
||||||
data[path.Clean(fileInfo.Path)] = []byte(sortLines(values))
|
fileProjection.Data = []byte(sortLines(values))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data[fPath] = fileProjection
|
||||||
}
|
}
|
||||||
return data, utilerrors.NewAggregate(errlist)
|
return data, utilerrors.NewAggregate(errlist)
|
||||||
}
|
}
|
||||||
|
@ -103,10 +103,12 @@ func TestLabels(t *testing.T) {
|
|||||||
defer os.RemoveAll(rootDir)
|
defer os.RemoveAll(rootDir)
|
||||||
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
|
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
|
||||||
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
|
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
|
||||||
|
defaultMode := int32(0644)
|
||||||
volumeSpec := &api.Volume{
|
volumeSpec := &api.Volume{
|
||||||
Name: testVolumeName,
|
Name: testVolumeName,
|
||||||
VolumeSource: api.VolumeSource{
|
VolumeSource: api.VolumeSource{
|
||||||
DownwardAPI: &api.DownwardAPIVolumeSource{
|
DownwardAPI: &api.DownwardAPIVolumeSource{
|
||||||
|
DefaultMode: &defaultMode,
|
||||||
Items: []api.DownwardAPIVolumeFile{
|
Items: []api.DownwardAPIVolumeFile{
|
||||||
{Path: "labels", FieldRef: &api.ObjectFieldSelector{
|
{Path: "labels", FieldRef: &api.ObjectFieldSelector{
|
||||||
FieldPath: "metadata.labels"}}}},
|
FieldPath: "metadata.labels"}}}},
|
||||||
@ -167,10 +169,12 @@ func TestAnnotations(t *testing.T) {
|
|||||||
"a1": "value1",
|
"a1": "value1",
|
||||||
"a2": "value2"}
|
"a2": "value2"}
|
||||||
|
|
||||||
|
defaultMode := int32(0644)
|
||||||
volumeSpec := &api.Volume{
|
volumeSpec := &api.Volume{
|
||||||
Name: testVolumeName,
|
Name: testVolumeName,
|
||||||
VolumeSource: api.VolumeSource{
|
VolumeSource: api.VolumeSource{
|
||||||
DownwardAPI: &api.DownwardAPIVolumeSource{
|
DownwardAPI: &api.DownwardAPIVolumeSource{
|
||||||
|
DefaultMode: &defaultMode,
|
||||||
Items: []api.DownwardAPIVolumeFile{
|
Items: []api.DownwardAPIVolumeFile{
|
||||||
{Path: "annotations", FieldRef: &api.ObjectFieldSelector{
|
{Path: "annotations", FieldRef: &api.ObjectFieldSelector{
|
||||||
FieldPath: "metadata.annotations"}}}},
|
FieldPath: "metadata.annotations"}}}},
|
||||||
@ -230,10 +234,12 @@ func TestName(t *testing.T) {
|
|||||||
testName = "test_metadata_name"
|
testName = "test_metadata_name"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
defaultMode := int32(0644)
|
||||||
volumeSpec := &api.Volume{
|
volumeSpec := &api.Volume{
|
||||||
Name: testVolumeName,
|
Name: testVolumeName,
|
||||||
VolumeSource: api.VolumeSource{
|
VolumeSource: api.VolumeSource{
|
||||||
DownwardAPI: &api.DownwardAPIVolumeSource{
|
DownwardAPI: &api.DownwardAPIVolumeSource{
|
||||||
|
DefaultMode: &defaultMode,
|
||||||
Items: []api.DownwardAPIVolumeFile{
|
Items: []api.DownwardAPIVolumeFile{
|
||||||
{Path: "name_file_name", FieldRef: &api.ObjectFieldSelector{
|
{Path: "name_file_name", FieldRef: &api.ObjectFieldSelector{
|
||||||
FieldPath: "metadata.name"}}}},
|
FieldPath: "metadata.name"}}}},
|
||||||
@ -293,10 +299,12 @@ func TestNamespace(t *testing.T) {
|
|||||||
testName = "test_metadata_name"
|
testName = "test_metadata_name"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
defaultMode := int32(0644)
|
||||||
volumeSpec := &api.Volume{
|
volumeSpec := &api.Volume{
|
||||||
Name: testVolumeName,
|
Name: testVolumeName,
|
||||||
VolumeSource: api.VolumeSource{
|
VolumeSource: api.VolumeSource{
|
||||||
DownwardAPI: &api.DownwardAPIVolumeSource{
|
DownwardAPI: &api.DownwardAPIVolumeSource{
|
||||||
|
DefaultMode: &defaultMode,
|
||||||
Items: []api.DownwardAPIVolumeFile{
|
Items: []api.DownwardAPIVolumeFile{
|
||||||
{Path: "namespace_file_name", FieldRef: &api.ObjectFieldSelector{
|
{Path: "namespace_file_name", FieldRef: &api.ObjectFieldSelector{
|
||||||
FieldPath: "metadata.namespace"}}}},
|
FieldPath: "metadata.namespace"}}}},
|
||||||
@ -371,10 +379,12 @@ func TestWriteTwiceNoUpdate(t *testing.T) {
|
|||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
|
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
|
||||||
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
|
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
|
||||||
|
defaultMode := int32(0644)
|
||||||
volumeSpec := &api.Volume{
|
volumeSpec := &api.Volume{
|
||||||
Name: testVolumeName,
|
Name: testVolumeName,
|
||||||
VolumeSource: api.VolumeSource{
|
VolumeSource: api.VolumeSource{
|
||||||
DownwardAPI: &api.DownwardAPIVolumeSource{
|
DownwardAPI: &api.DownwardAPIVolumeSource{
|
||||||
|
DefaultMode: &defaultMode,
|
||||||
Items: []api.DownwardAPIVolumeFile{
|
Items: []api.DownwardAPIVolumeFile{
|
||||||
{Path: "labels", FieldRef: &api.ObjectFieldSelector{
|
{Path: "labels", FieldRef: &api.ObjectFieldSelector{
|
||||||
FieldPath: "metadata.labels"}}}},
|
FieldPath: "metadata.labels"}}}},
|
||||||
@ -457,10 +467,12 @@ func TestWriteTwiceWithUpdate(t *testing.T) {
|
|||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
|
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
|
||||||
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
|
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
|
||||||
|
defaultMode := int32(0644)
|
||||||
volumeSpec := &api.Volume{
|
volumeSpec := &api.Volume{
|
||||||
Name: testVolumeName,
|
Name: testVolumeName,
|
||||||
VolumeSource: api.VolumeSource{
|
VolumeSource: api.VolumeSource{
|
||||||
DownwardAPI: &api.DownwardAPIVolumeSource{
|
DownwardAPI: &api.DownwardAPIVolumeSource{
|
||||||
|
DefaultMode: &defaultMode,
|
||||||
Items: []api.DownwardAPIVolumeFile{
|
Items: []api.DownwardAPIVolumeFile{
|
||||||
{Path: "labels", FieldRef: &api.ObjectFieldSelector{
|
{Path: "labels", FieldRef: &api.ObjectFieldSelector{
|
||||||
FieldPath: "metadata.labels"}}}},
|
FieldPath: "metadata.labels"}}}},
|
||||||
@ -563,10 +575,12 @@ func TestWriteWithUnixPath(t *testing.T) {
|
|||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
|
pluginMgr.InitPlugins(ProbeVolumePlugins(), host)
|
||||||
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
|
plugin, err := pluginMgr.FindPluginByName(downwardAPIPluginName)
|
||||||
|
defaultMode := int32(0644)
|
||||||
volumeSpec := &api.Volume{
|
volumeSpec := &api.Volume{
|
||||||
Name: testVolumeName,
|
Name: testVolumeName,
|
||||||
VolumeSource: api.VolumeSource{
|
VolumeSource: api.VolumeSource{
|
||||||
DownwardAPI: &api.DownwardAPIVolumeSource{
|
DownwardAPI: &api.DownwardAPIVolumeSource{
|
||||||
|
DefaultMode: &defaultMode,
|
||||||
Items: []api.DownwardAPIVolumeFile{
|
Items: []api.DownwardAPIVolumeFile{
|
||||||
{Path: "this/is/mine/labels", FieldRef: &api.ObjectFieldSelector{
|
{Path: "this/is/mine/labels", FieldRef: &api.ObjectFieldSelector{
|
||||||
FieldPath: "metadata.labels"}},
|
FieldPath: "metadata.labels"}},
|
||||||
@ -643,10 +657,12 @@ func TestWriteWithUnixPathBadPath(t *testing.T) {
|
|||||||
t.Errorf("Can't find the plugin by name")
|
t.Errorf("Can't find the plugin by name")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defaultMode := int32(0644)
|
||||||
volumeSpec := &api.Volume{
|
volumeSpec := &api.Volume{
|
||||||
Name: testVolumeName,
|
Name: testVolumeName,
|
||||||
VolumeSource: api.VolumeSource{
|
VolumeSource: api.VolumeSource{
|
||||||
DownwardAPI: &api.DownwardAPIVolumeSource{
|
DownwardAPI: &api.DownwardAPIVolumeSource{
|
||||||
|
DefaultMode: &defaultMode,
|
||||||
Items: []api.DownwardAPIVolumeFile{
|
Items: []api.DownwardAPIVolumeFile{
|
||||||
{
|
{
|
||||||
Path: "this//labels",
|
Path: "this//labels",
|
||||||
@ -684,3 +700,138 @@ func TestWriteWithUnixPathBadPath(t *testing.T) {
|
|||||||
t.Errorf("Found `%s` expected %s", data, fieldpath.FormatMap(labels))
|
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)
|
||||||
|
}
|
||||||
|
@ -190,7 +190,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)
|
payload, err := makePayload(b.source.Items, secret, b.source.DefaultMode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -217,25 +217,38 @@ func (b *secretVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePayload(mappings []api.KeyToPath, secret *api.Secret) (map[string][]byte, error) {
|
func makePayload(mappings []api.KeyToPath, secret *api.Secret, defaultMode *int32) (map[string]volumeutil.FileProjection, error) {
|
||||||
payload := make(map[string][]byte, len(secret.Data))
|
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 {
|
if len(mappings) == 0 {
|
||||||
for name, data := range secret.Data {
|
for name, data := range secret.Data {
|
||||||
payload[name] = []byte(data)
|
fileProjection.Data = []byte(data)
|
||||||
|
fileProjection.Mode = *defaultMode
|
||||||
|
payload[name] = fileProjection
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for _, ktp := range mappings {
|
for _, ktp := range mappings {
|
||||||
content, ok := secret.Data[ktp.Key]
|
content, ok := secret.Data[ktp.Key]
|
||||||
if !ok {
|
if !ok {
|
||||||
glog.Errorf("references non-existent secret key")
|
err_msg := "references non-existent secret key"
|
||||||
return nil, fmt.Errorf("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
|
return payload, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,11 +39,13 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestMakePayload(t *testing.T) {
|
func TestMakePayload(t *testing.T) {
|
||||||
|
case_mapping_mode := int32(0400)
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
mappings []api.KeyToPath
|
mappings []api.KeyToPath
|
||||||
secret *api.Secret
|
secret *api.Secret
|
||||||
payload map[string][]byte
|
mode int32
|
||||||
|
payload map[string]util.FileProjection
|
||||||
success bool
|
success bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@ -54,9 +56,10 @@ func TestMakePayload(t *testing.T) {
|
|||||||
"bar": []byte("bar"),
|
"bar": []byte("bar"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
payload: map[string][]byte{
|
mode: 0644,
|
||||||
"foo": []byte("foo"),
|
payload: map[string]util.FileProjection{
|
||||||
"bar": []byte("bar"),
|
"foo": {Data: []byte("foo"), Mode: 0644},
|
||||||
|
"bar": {Data: []byte("bar"), Mode: 0644},
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
},
|
},
|
||||||
@ -74,8 +77,9 @@ func TestMakePayload(t *testing.T) {
|
|||||||
"bar": []byte("bar"),
|
"bar": []byte("bar"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
payload: map[string][]byte{
|
mode: 0644,
|
||||||
"path/to/foo.txt": []byte("foo"),
|
payload: map[string]util.FileProjection{
|
||||||
|
"path/to/foo.txt": {Data: []byte("foo"), Mode: 0644},
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
},
|
},
|
||||||
@ -93,8 +97,9 @@ func TestMakePayload(t *testing.T) {
|
|||||||
"bar": []byte("bar"),
|
"bar": []byte("bar"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
payload: map[string][]byte{
|
mode: 0644,
|
||||||
"path/to/1/2/3/foo.txt": []byte("foo"),
|
payload: map[string]util.FileProjection{
|
||||||
|
"path/to/1/2/3/foo.txt": {Data: []byte("foo"), Mode: 0644},
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
},
|
},
|
||||||
@ -112,8 +117,9 @@ func TestMakePayload(t *testing.T) {
|
|||||||
"bar": []byte("bar"),
|
"bar": []byte("bar"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
payload: map[string][]byte{
|
mode: 0644,
|
||||||
"path/to/1/2/3/foo.txt": []byte("foo"),
|
payload: map[string]util.FileProjection{
|
||||||
|
"path/to/1/2/3/foo.txt": {Data: []byte("foo"), Mode: 0644},
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
},
|
},
|
||||||
@ -135,9 +141,10 @@ func TestMakePayload(t *testing.T) {
|
|||||||
"bar": []byte("bar"),
|
"bar": []byte("bar"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
payload: map[string][]byte{
|
mode: 0644,
|
||||||
"path/to/1/2/3/foo.txt": []byte("foo"),
|
payload: map[string]util.FileProjection{
|
||||||
"another/path/to/the/esteemed/bar.bin": []byte("bar"),
|
"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,
|
success: true,
|
||||||
},
|
},
|
||||||
@ -155,12 +162,65 @@ func TestMakePayload(t *testing.T) {
|
|||||||
"bar": []byte("bar"),
|
"bar": []byte("bar"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mode: 0644,
|
||||||
success: false,
|
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 {
|
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 {
|
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
|
||||||
@ -217,7 +277,7 @@ func TestPlugin(t *testing.T) {
|
|||||||
testNamespace = "test_secret_namespace"
|
testNamespace = "test_secret_namespace"
|
||||||
testName = "test_secret_name"
|
testName = "test_secret_name"
|
||||||
|
|
||||||
volumeSpec = volumeSpec(testVolumeName, testName)
|
volumeSpec = volumeSpec(testVolumeName, testName, 0644)
|
||||||
secret = secret(testNamespace, testName)
|
secret = secret(testNamespace, testName)
|
||||||
client = fake.NewSimpleClientset(&secret)
|
client = fake.NewSimpleClientset(&secret)
|
||||||
pluginMgr = volume.VolumePluginMgr{}
|
pluginMgr = volume.VolumePluginMgr{}
|
||||||
@ -290,7 +350,7 @@ func TestPluginReboot(t *testing.T) {
|
|||||||
testNamespace = "test_secret_namespace"
|
testNamespace = "test_secret_namespace"
|
||||||
testName = "test_secret_name"
|
testName = "test_secret_name"
|
||||||
|
|
||||||
volumeSpec = volumeSpec(testVolumeName, testName)
|
volumeSpec = volumeSpec(testVolumeName, testName, 0644)
|
||||||
secret = secret(testNamespace, testName)
|
secret = secret(testNamespace, testName)
|
||||||
client = fake.NewSimpleClientset(&secret)
|
client = fake.NewSimpleClientset(&secret)
|
||||||
pluginMgr = volume.VolumePluginMgr{}
|
pluginMgr = volume.VolumePluginMgr{}
|
||||||
@ -336,12 +396,13 @@ func TestPluginReboot(t *testing.T) {
|
|||||||
doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, 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{
|
return &api.Volume{
|
||||||
Name: volumeName,
|
Name: volumeName,
|
||||||
VolumeSource: api.VolumeSource{
|
VolumeSource: api.VolumeSource{
|
||||||
Secret: &api.SecretVolumeSource{
|
Secret: &api.SecretVolumeSource{
|
||||||
SecretName: secretName,
|
SecretName: secretName,
|
||||||
|
DefaultMode: &defaultMode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -60,9 +60,14 @@ type AtomicWriter struct {
|
|||||||
logContext string
|
logContext string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type FileProjection struct {
|
||||||
|
Data []byte
|
||||||
|
Mode int32
|
||||||
|
}
|
||||||
|
|
||||||
// NewAtomicWriter creates a new AtomicWriter configured to write to the given
|
// NewAtomicWriter creates a new AtomicWriter configured to write to the given
|
||||||
// target directory, or returns an error if the target directory does not exist.
|
// 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)
|
_, err := os.Stat(targetDir)
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -113,7 +118,7 @@ const (
|
|||||||
// 9. The new data directory symlink is renamed to the data directory; rename is atomic
|
// 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
|
// 10. Old paths are removed from the user-visible portion of the target directory
|
||||||
// 11. The previous timestamped directory is removed, if it exists
|
// 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)
|
// (1)
|
||||||
cleanPayload, err := validatePayload(payload)
|
cleanPayload, err := validatePayload(payload)
|
||||||
if err != nil {
|
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.
|
// 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) {
|
func validatePayload(payload map[string]FileProjection) (map[string]FileProjection, error) {
|
||||||
cleanPayload := make(map[string][]byte)
|
cleanPayload := make(map[string]FileProjection)
|
||||||
for k, content := range payload {
|
for k, content := range payload {
|
||||||
if err := validatePath(k); err != nil {
|
if err := validatePath(k); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -257,9 +262,9 @@ func validatePath(targetPath string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// shouldWritePayload returns whether the payload should be written to disk.
|
// shouldWritePayload returns whether the payload should be written to disk.
|
||||||
func (w *AtomicWriter) shouldWritePayload(payload map[string][]byte) (bool, error) {
|
func (w *AtomicWriter) shouldWritePayload(payload map[string]FileProjection) (bool, error) {
|
||||||
for userVisiblePath, content := range payload {
|
for userVisiblePath, fileProjection := range payload {
|
||||||
shouldWrite, err := w.shouldWriteFile(path.Join(w.targetDir, userVisiblePath), content)
|
shouldWrite, err := w.shouldWriteFile(path.Join(w.targetDir, userVisiblePath), fileProjection.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
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
|
// pathsToRemove walks the user-visible portion of the target directory and
|
||||||
// determines which paths should be removed (if any) after the payload is
|
// determines which paths should be removed (if any) after the payload is
|
||||||
// written to the target directory.
|
// 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()
|
paths := sets.NewString()
|
||||||
visitor := func(path string, info os.FileInfo, err error) error {
|
visitor := func(path string, info os.FileInfo, err error) error {
|
||||||
if path == w.targetDir {
|
if path == w.targetDir {
|
||||||
@ -355,8 +360,10 @@ func (w *AtomicWriter) newTimestampDir() (string, error) {
|
|||||||
|
|
||||||
// writePayloadToDir writes the given payload to the given directory. The
|
// writePayloadToDir writes the given payload to the given directory. The
|
||||||
// directory must exist.
|
// directory must exist.
|
||||||
func (w *AtomicWriter) writePayloadToDir(payload map[string][]byte, dir string) error {
|
func (w *AtomicWriter) writePayloadToDir(payload map[string]FileProjection, dir string) error {
|
||||||
for userVisiblePath, content := range payload {
|
for userVisiblePath, fileProjection := range payload {
|
||||||
|
content := fileProjection.Data
|
||||||
|
mode := os.FileMode(fileProjection.Mode)
|
||||||
fullPath := path.Join(dir, userVisiblePath)
|
fullPath := path.Join(dir, userVisiblePath)
|
||||||
baseDir, _ := filepath.Split(fullPath)
|
baseDir, _ := filepath.Split(fullPath)
|
||||||
|
|
||||||
@ -366,11 +373,19 @@ func (w *AtomicWriter) writePayloadToDir(payload map[string][]byte, dir string)
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ioutil.WriteFile(fullPath, content, 0644)
|
err = ioutil.WriteFile(fullPath, content, mode)
|
||||||
if err != nil {
|
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
|
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
|
return nil
|
||||||
@ -387,7 +402,7 @@ func (w *AtomicWriter) writePayloadToDir(payload map[string][]byte, dir string)
|
|||||||
// foo/bar -> ../..data/foo/bar
|
// foo/bar -> ../..data/foo/bar
|
||||||
// baz/bar -> ../..data/baz/bar
|
// baz/bar -> ../..data/baz/bar
|
||||||
// foo/baz/blah -> ../../..data/foo/baz/blah
|
// 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 {
|
for userVisiblePath := range payload {
|
||||||
dir, _ := filepath.Split(userVisiblePath)
|
dir, _ := filepath.Split(userVisiblePath)
|
||||||
subDirs := 0
|
subDirs := 0
|
||||||
|
@ -130,90 +130,90 @@ func TestValidatePath(t *testing.T) {
|
|||||||
func TestPathsToRemove(t *testing.T) {
|
func TestPathsToRemove(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
payload1 map[string][]byte
|
payload1 map[string]FileProjection
|
||||||
payload2 map[string][]byte
|
payload2 map[string]FileProjection
|
||||||
expected sets.String
|
expected sets.String
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "simple",
|
name: "simple",
|
||||||
payload1: map[string][]byte{
|
payload1: map[string]FileProjection{
|
||||||
"foo.txt": []byte("foo"),
|
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar.txt": []byte("bar"),
|
"bar.txt": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
payload2: map[string][]byte{
|
payload2: map[string]FileProjection{
|
||||||
"foo.txt": []byte("foo"),
|
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
},
|
},
|
||||||
expected: sets.NewString("bar.txt"),
|
expected: sets.NewString("bar.txt"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "simple 2",
|
name: "simple 2",
|
||||||
payload1: map[string][]byte{
|
payload1: map[string]FileProjection{
|
||||||
"foo.txt": []byte("foo"),
|
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"zip/bar.txt": []byte("zip/bar"),
|
"zip/bar.txt": {Mode: 0644, Data: []byte("zip/b}ar")},
|
||||||
},
|
},
|
||||||
payload2: map[string][]byte{
|
payload2: map[string]FileProjection{
|
||||||
"foo.txt": []byte("foo"),
|
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
},
|
},
|
||||||
expected: sets.NewString("zip/bar.txt", "zip"),
|
expected: sets.NewString("zip/bar.txt", "zip"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "subdirs 1",
|
name: "subdirs 1",
|
||||||
payload1: map[string][]byte{
|
payload1: map[string]FileProjection{
|
||||||
"foo.txt": []byte("foo"),
|
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"zip/zap/bar.txt": []byte("zip/bar"),
|
"zip/zap/bar.txt": {Mode: 0644, Data: []byte("zip/bar")},
|
||||||
},
|
},
|
||||||
payload2: map[string][]byte{
|
payload2: map[string]FileProjection{
|
||||||
"foo.txt": []byte("foo"),
|
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
},
|
},
|
||||||
expected: sets.NewString("zip/zap/bar.txt", "zip", "zip/zap"),
|
expected: sets.NewString("zip/zap/bar.txt", "zip", "zip/zap"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "subdirs 2",
|
name: "subdirs 2",
|
||||||
payload1: map[string][]byte{
|
payload1: map[string]FileProjection{
|
||||||
"foo.txt": []byte("foo"),
|
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"zip/1/2/3/4/bar.txt": []byte("zip/bar"),
|
"zip/1/2/3/4/bar.txt": {Mode: 0644, Data: []byte("zip/b}ar")},
|
||||||
},
|
},
|
||||||
payload2: map[string][]byte{
|
payload2: map[string]FileProjection{
|
||||||
"foo.txt": []byte("foo"),
|
"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"),
|
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",
|
name: "subdirs 3",
|
||||||
payload1: map[string][]byte{
|
payload1: map[string]FileProjection{
|
||||||
"foo.txt": []byte("foo"),
|
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"zip/1/2/3/4/bar.txt": []byte("zip/bar"),
|
"zip/1/2/3/4/bar.txt": {Mode: 0644, Data: []byte("zip/b}ar")},
|
||||||
"zap/a/b/c/bar.txt": []byte("zap/bar"),
|
"zap/a/b/c/bar.txt": {Mode: 0644, Data: []byte("zap/bar")},
|
||||||
},
|
},
|
||||||
payload2: map[string][]byte{
|
payload2: map[string]FileProjection{
|
||||||
"foo.txt": []byte("foo"),
|
"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"),
|
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",
|
name: "subdirs 4",
|
||||||
payload1: map[string][]byte{
|
payload1: map[string]FileProjection{
|
||||||
"foo.txt": []byte("foo"),
|
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"zap/1/2/3/4/bar.txt": []byte("zip/bar"),
|
"zap/1/2/3/4/bar.txt": {Mode: 0644, Data: []byte("zip/bar")},
|
||||||
"zap/1/2/c/bar.txt": []byte("zap/bar"),
|
"zap/1/2/c/bar.txt": {Mode: 0644, Data: []byte("zap/bar")},
|
||||||
"zap/1/2/magic.txt": []byte("indigo"),
|
"zap/1/2/magic.txt": {Mode: 0644, Data: []byte("indigo")},
|
||||||
},
|
},
|
||||||
payload2: map[string][]byte{
|
payload2: map[string]FileProjection{
|
||||||
"foo.txt": []byte("foo"),
|
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"zap/1/2/magic.txt": []byte("indigo"),
|
"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"),
|
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",
|
name: "subdirs 5",
|
||||||
payload1: map[string][]byte{
|
payload1: map[string]FileProjection{
|
||||||
"foo.txt": []byte("foo"),
|
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"zap/1/2/3/4/bar.txt": []byte("zip/bar"),
|
"zap/1/2/3/4/bar.txt": {Mode: 0644, Data: []byte("zip/bar")},
|
||||||
"zap/1/2/c/bar.txt": []byte("zap/bar"),
|
"zap/1/2/c/bar.txt": {Mode: 0644, Data: []byte("zap/bar")},
|
||||||
},
|
},
|
||||||
payload2: map[string][]byte{
|
payload2: map[string]FileProjection{
|
||||||
"foo.txt": []byte("foo"),
|
"foo.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"zap/1/2/magic.txt": []byte("indigo"),
|
"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"),
|
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 {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
payload map[string][]byte
|
payload map[string]FileProjection
|
||||||
success bool
|
success bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "invalid payload 1",
|
name: "invalid payload 1",
|
||||||
payload: map[string][]byte{
|
payload: map[string]FileProjection{
|
||||||
"foo": []byte("foo"),
|
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||||
"..bar": []byte("bar"),
|
"..bar": {Mode: 0644, Data: []byte("bar")},
|
||||||
"binary.bin": mysteryBinaryBytes,
|
"binary.bin": {Mode: 0644, Data: mysteryBinaryBytes},
|
||||||
},
|
},
|
||||||
success: false,
|
success: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid payload 2",
|
name: "invalid payload 2",
|
||||||
payload: map[string][]byte{
|
payload: map[string]FileProjection{
|
||||||
"foo/../bar": []byte("foo"),
|
"foo/../bar": {Mode: 0644, Data: []byte("foo")},
|
||||||
},
|
},
|
||||||
success: false,
|
success: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "basic 1",
|
name: "basic 1",
|
||||||
payload: map[string][]byte{
|
payload: map[string]FileProjection{
|
||||||
"foo": []byte("foo"),
|
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar": []byte("bar"),
|
"bar": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "basic 2",
|
name: "basic 2",
|
||||||
payload: map[string][]byte{
|
payload: map[string]FileProjection{
|
||||||
"binary.bin": mysteryBinaryBytes,
|
"binary.bin": {Mode: 0644, Data: mysteryBinaryBytes},
|
||||||
".binary.bin": 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,
|
success: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "dotfiles",
|
name: "dotfiles",
|
||||||
payload: map[string][]byte{
|
payload: map[string]FileProjection{
|
||||||
"foo": []byte("foo"),
|
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar": []byte("bar"),
|
"bar": {Mode: 0644, Data: []byte("bar")},
|
||||||
".dotfile": []byte("dotfile"),
|
".dotfile": {Mode: 0644, Data: []byte("dotfile")},
|
||||||
".dotfile.file": []byte("dotfile.file"),
|
".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,
|
success: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "subdirectories 1",
|
name: "subdirectories 1",
|
||||||
payload: map[string][]byte{
|
payload: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo/bar"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
|
||||||
"bar/zab.txt": []byte("bar/zab.txt"),
|
"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,
|
success: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "subdirectories 2",
|
name: "subdirectories 2",
|
||||||
payload: map[string][]byte{
|
payload: map[string]FileProjection{
|
||||||
"foo//bar.txt": []byte("foo//bar"),
|
"foo//bar.txt": {Mode: 0644, Data: []byte("foo//bar")},
|
||||||
"bar///bar/zab.txt": []byte("bar/../bar/zab.txt"),
|
"bar///bar/zab.txt": {Mode: 0644, Data: []byte("bar/../bar/zab.txt")},
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "subdirectories 3",
|
name: "subdirectories 3",
|
||||||
payload: map[string][]byte{
|
payload: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo/bar"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
|
||||||
"bar/zab.txt": []byte("bar/zab.txt"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
|
||||||
"foo/blaz/bar.txt": []byte("foo/blaz/bar"),
|
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar")},
|
||||||
"bar/zib/zab.txt": []byte("bar/zib/zab.txt"),
|
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt")},
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "kitchen sink",
|
name: "kitchen sink",
|
||||||
payload: map[string][]byte{
|
payload: map[string]FileProjection{
|
||||||
"foo.log": []byte("foo"),
|
"foo.log": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar.zap": []byte("bar"),
|
"bar.zap": {Mode: 0644, Data: []byte("bar")},
|
||||||
".dotfile": []byte("dotfile"),
|
".dotfile": {Mode: 0644, Data: []byte("dotfile")},
|
||||||
"foo/bar.txt": []byte("foo/bar"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
|
||||||
"bar/zab.txt": []byte("bar/zab.txt"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
|
||||||
"foo/blaz/bar.txt": []byte("foo/blaz/bar"),
|
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar")},
|
||||||
"bar/zib/zab.txt": []byte("bar/zib/zab.txt"),
|
"bar/zib/zab.txt": {Mode: 0400, Data: []byte("bar/zib/zab.txt")},
|
||||||
"1/2/3/4/5/6/7/8/9/10/.dotfile.lib": []byte("1-2-3-dotfile"),
|
"1/2/3/4/5/6/7/8/9/10/.dotfile.lib": {Mode: 0777, Data: []byte("1-2-3-dotfile")},
|
||||||
},
|
},
|
||||||
success: true,
|
success: true,
|
||||||
},
|
},
|
||||||
@ -376,150 +402,150 @@ IAAAAAAAsDyZDwU=`
|
|||||||
func TestUpdate(t *testing.T) {
|
func TestUpdate(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
first map[string][]byte
|
first map[string]FileProjection
|
||||||
next map[string][]byte
|
next map[string]FileProjection
|
||||||
shouldWrite bool
|
shouldWrite bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "update",
|
name: "update",
|
||||||
first: map[string][]byte{
|
first: map[string]FileProjection{
|
||||||
"foo": []byte("foo"),
|
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar": []byte("bar"),
|
"bar": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
next: map[string][]byte{
|
next: map[string]FileProjection{
|
||||||
"foo": []byte("foo2"),
|
"foo": {Mode: 0644, Data: []byte("foo2")},
|
||||||
"bar": []byte("bar2"),
|
"bar": {Mode: 0640, Data: []byte("bar2")},
|
||||||
},
|
},
|
||||||
shouldWrite: true,
|
shouldWrite: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no update",
|
name: "no update",
|
||||||
first: map[string][]byte{
|
first: map[string]FileProjection{
|
||||||
"foo": []byte("foo"),
|
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar": []byte("bar"),
|
"bar": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
next: map[string][]byte{
|
next: map[string]FileProjection{
|
||||||
"foo": []byte("foo"),
|
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar": []byte("bar"),
|
"bar": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
shouldWrite: false,
|
shouldWrite: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no update 2",
|
name: "no update 2",
|
||||||
first: map[string][]byte{
|
first: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar/zab.txt": []byte("bar"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
next: map[string][]byte{
|
next: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar/zab.txt": []byte("bar"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
shouldWrite: false,
|
shouldWrite: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "add 1",
|
name: "add 1",
|
||||||
first: map[string][]byte{
|
first: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar/zab.txt": []byte("bar"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
next: map[string][]byte{
|
next: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar/zab.txt": []byte("bar"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||||
"blu/zip.txt": []byte("zip"),
|
"blu/zip.txt": {Mode: 0644, Data: []byte("zip")},
|
||||||
},
|
},
|
||||||
shouldWrite: true,
|
shouldWrite: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "add 2",
|
name: "add 2",
|
||||||
first: map[string][]byte{
|
first: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar/zab.txt": []byte("bar"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
next: map[string][]byte{
|
next: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar/zab.txt": []byte("bar"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||||
"blu/two/2/3/4/5/zip.txt": []byte("zip"),
|
"blu/two/2/3/4/5/zip.txt": {Mode: 0644, Data: []byte("zip")},
|
||||||
},
|
},
|
||||||
shouldWrite: true,
|
shouldWrite: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "add 3",
|
name: "add 3",
|
||||||
first: map[string][]byte{
|
first: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar/zab.txt": []byte("bar"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
next: map[string][]byte{
|
next: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar/zab.txt": []byte("bar"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||||
"bar/2/3/4/5/zip.txt": []byte("zip"),
|
"bar/2/3/4/5/zip.txt": {Mode: 0644, Data: []byte("zip")},
|
||||||
},
|
},
|
||||||
shouldWrite: true,
|
shouldWrite: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "delete 1",
|
name: "delete 1",
|
||||||
first: map[string][]byte{
|
first: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar/zab.txt": []byte("bar"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
next: map[string][]byte{
|
next: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
},
|
},
|
||||||
shouldWrite: true,
|
shouldWrite: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "delete 2",
|
name: "delete 2",
|
||||||
first: map[string][]byte{
|
first: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar/1/2/3/zab.txt": []byte("bar"),
|
"bar/1/2/3/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
next: map[string][]byte{
|
next: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
},
|
},
|
||||||
shouldWrite: true,
|
shouldWrite: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "delete 3",
|
name: "delete 3",
|
||||||
first: map[string][]byte{
|
first: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar/1/2/sip.txt": []byte("sip"),
|
"bar/1/2/sip.txt": {Mode: 0644, Data: []byte("sip")},
|
||||||
"bar/1/2/3/zab.txt": []byte("bar"),
|
"bar/1/2/3/zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
next: map[string][]byte{
|
next: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar/1/2/sip.txt": []byte("sip"),
|
"bar/1/2/sip.txt": {Mode: 0644, Data: []byte("sip")},
|
||||||
},
|
},
|
||||||
shouldWrite: true,
|
shouldWrite: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "delete 4",
|
name: "delete 4",
|
||||||
first: map[string][]byte{
|
first: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar/1/2/sip.txt": []byte("sip"),
|
"bar/1/2/sip.txt": {Mode: 0644, Data: []byte("sip")},
|
||||||
"bar/1/2/3/4/5/6zab.txt": []byte("bar"),
|
"bar/1/2/3/4/5/6zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
next: map[string][]byte{
|
next: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar/1/2/sip.txt": []byte("sip"),
|
"bar/1/2/sip.txt": {Mode: 0644, Data: []byte("sip")},
|
||||||
},
|
},
|
||||||
shouldWrite: true,
|
shouldWrite: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "delete all",
|
name: "delete all",
|
||||||
first: map[string][]byte{
|
first: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar/1/2/sip.txt": []byte("sip"),
|
"bar/1/2/sip.txt": {Mode: 0644, Data: []byte("sip")},
|
||||||
"bar/1/2/3/4/5/6zab.txt": []byte("bar"),
|
"bar/1/2/3/4/5/6zab.txt": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
next: map[string][]byte{},
|
next: map[string]FileProjection{},
|
||||||
shouldWrite: true,
|
shouldWrite: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "add and delete 1",
|
name: "add and delete 1",
|
||||||
first: map[string][]byte{
|
first: map[string]FileProjection{
|
||||||
"foo/bar.txt": []byte("foo"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo")},
|
||||||
},
|
},
|
||||||
next: map[string][]byte{
|
next: map[string]FileProjection{
|
||||||
"bar/baz.txt": []byte("baz"),
|
"bar/baz.txt": {Mode: 0644, Data: []byte("baz")},
|
||||||
},
|
},
|
||||||
shouldWrite: true,
|
shouldWrite: true,
|
||||||
},
|
},
|
||||||
@ -563,130 +589,130 @@ func TestUpdate(t *testing.T) {
|
|||||||
func TestMultipleUpdates(t *testing.T) {
|
func TestMultipleUpdates(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
payloads []map[string][]byte
|
payloads []map[string]FileProjection
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "update 1",
|
name: "update 1",
|
||||||
payloads: []map[string][]byte{
|
payloads: []map[string]FileProjection{
|
||||||
{
|
{
|
||||||
"foo": []byte("foo"),
|
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar": []byte("bar"),
|
"bar": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"foo": []byte("foo2"),
|
"foo": {Mode: 0400, Data: []byte("foo2")},
|
||||||
"bar": []byte("bar2"),
|
"bar": {Mode: 0400, Data: []byte("bar2")},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"foo": []byte("foo3"),
|
"foo": {Mode: 0600, Data: []byte("foo3")},
|
||||||
"bar": []byte("bar3"),
|
"bar": {Mode: 0600, Data: []byte("bar3")},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "update 2",
|
name: "update 2",
|
||||||
payloads: []map[string][]byte{
|
payloads: []map[string]FileProjection{
|
||||||
{
|
{
|
||||||
"foo/bar.txt": []byte("foo/bar"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
|
||||||
"bar/zab.txt": []byte("bar/zab.txt"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"foo/bar.txt": []byte("foo/bar2"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
|
||||||
"bar/zab.txt": []byte("bar/zab.txt2"),
|
"bar/zab.txt": {Mode: 0400, Data: []byte("bar/zab.txt2")},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "clear sentinel",
|
name: "clear sentinel",
|
||||||
payloads: []map[string][]byte{
|
payloads: []map[string]FileProjection{
|
||||||
{
|
{
|
||||||
"foo": []byte("foo"),
|
"foo": {Mode: 0644, Data: []byte("foo")},
|
||||||
"bar": []byte("bar"),
|
"bar": {Mode: 0644, Data: []byte("bar")},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"foo": []byte("foo2"),
|
"foo": {Mode: 0644, Data: []byte("foo2")},
|
||||||
"bar": []byte("bar2"),
|
"bar": {Mode: 0644, Data: []byte("bar2")},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"foo": []byte("foo3"),
|
"foo": {Mode: 0644, Data: []byte("foo3")},
|
||||||
"bar": []byte("bar3"),
|
"bar": {Mode: 0644, Data: []byte("bar3")},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"foo": []byte("foo4"),
|
"foo": {Mode: 0644, Data: []byte("foo4")},
|
||||||
"bar": []byte("bar4"),
|
"bar": {Mode: 0644, Data: []byte("bar4")},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "subdirectories 2",
|
name: "subdirectories 2",
|
||||||
payloads: []map[string][]byte{
|
payloads: []map[string]FileProjection{
|
||||||
{
|
{
|
||||||
"foo/bar.txt": []byte("foo/bar"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
|
||||||
"bar/zab.txt": []byte("bar/zab.txt"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
|
||||||
"foo/blaz/bar.txt": []byte("foo/blaz/bar"),
|
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar")},
|
||||||
"bar/zib/zab.txt": []byte("bar/zib/zab.txt"),
|
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt")},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"foo/bar.txt": []byte("foo/bar2"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
|
||||||
"bar/zab.txt": []byte("bar/zab.txt2"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt2")},
|
||||||
"foo/blaz/bar.txt": []byte("foo/blaz/bar2"),
|
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar2")},
|
||||||
"bar/zib/zab.txt": []byte("bar/zib/zab.txt2"),
|
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt2")},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "add 1",
|
name: "add 1",
|
||||||
payloads: []map[string][]byte{
|
payloads: []map[string]FileProjection{
|
||||||
{
|
{
|
||||||
"foo/bar.txt": []byte("foo/bar"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
|
||||||
"bar//zab.txt": []byte("bar/zab.txt"),
|
"bar//zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
|
||||||
"foo/blaz/bar.txt": []byte("foo/blaz/bar"),
|
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar")},
|
||||||
"bar/zib////zib/zab.txt": []byte("bar/zib/zab.txt"),
|
"bar/zib////zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt")},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"foo/bar.txt": []byte("foo/bar2"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
|
||||||
"bar/zab.txt": []byte("bar/zab.txt2"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt2")},
|
||||||
"foo/blaz/bar.txt": []byte("foo/blaz/bar2"),
|
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar2")},
|
||||||
"bar/zib/zab.txt": []byte("bar/zib/zab.txt2"),
|
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt2")},
|
||||||
"add/new/keys.txt": []byte("addNewKeys"),
|
"add/new/keys.txt": {Mode: 0644, Data: []byte("addNewKeys")},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "add 2",
|
name: "add 2",
|
||||||
payloads: []map[string][]byte{
|
payloads: []map[string]FileProjection{
|
||||||
{
|
{
|
||||||
"foo/bar.txt": []byte("foo/bar2"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
|
||||||
"bar/zab.txt": []byte("bar/zab.txt2"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt2")},
|
||||||
"foo/blaz/bar.txt": []byte("foo/blaz/bar2"),
|
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar2")},
|
||||||
"bar/zib/zab.txt": []byte("bar/zib/zab.txt2"),
|
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt2")},
|
||||||
"add/new/keys.txt": []byte("addNewKeys"),
|
"add/new/keys.txt": {Mode: 0644, Data: []byte("addNewKeys")},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"foo/bar.txt": []byte("foo/bar2"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
|
||||||
"bar/zab.txt": []byte("bar/zab.txt2"),
|
"bar/zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt2")},
|
||||||
"foo/blaz/bar.txt": []byte("foo/blaz/bar2"),
|
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar2")},
|
||||||
"bar/zib/zab.txt": []byte("bar/zib/zab.txt2"),
|
"bar/zib/zab.txt": {Mode: 0644, Data: []byte("bar/zib/zab.txt2")},
|
||||||
"add/new/keys.txt": []byte("addNewKeys"),
|
"add/new/keys.txt": {Mode: 0644, Data: []byte("addNewKeys")},
|
||||||
"add/new/keys2.txt": []byte("addNewKeys2"),
|
"add/new/keys2.txt": {Mode: 0644, Data: []byte("addNewKeys2")},
|
||||||
"add/new/keys3.txt": []byte("addNewKeys3"),
|
"add/new/keys3.txt": {Mode: 0644, Data: []byte("addNewKeys3")},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "remove 1",
|
name: "remove 1",
|
||||||
payloads: []map[string][]byte{
|
payloads: []map[string]FileProjection{
|
||||||
{
|
{
|
||||||
"foo/bar.txt": []byte("foo/bar"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar")},
|
||||||
"bar//zab.txt": []byte("bar/zab.txt"),
|
"bar//zab.txt": {Mode: 0644, Data: []byte("bar/zab.txt")},
|
||||||
"foo/blaz/bar.txt": []byte("foo/blaz/bar"),
|
"foo/blaz/bar.txt": {Mode: 0644, Data: []byte("foo/blaz/bar")},
|
||||||
"zip/zap/zup/fop.txt": []byte("zip/zap/zup/fop.txt"),
|
"zip/zap/zup/fop.txt": {Mode: 0644, Data: []byte("zip/zap/zup/fop.txt")},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"foo/bar.txt": []byte("foo/bar2"),
|
"foo/bar.txt": {Mode: 0644, Data: []byte("foo/bar2")},
|
||||||
"bar/zab.txt": []byte("bar/zab.txt2"),
|
"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
|
// 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 {
|
visitor := func(path string, info os.FileInfo, err error) error {
|
||||||
if info.Mode().IsRegular() || info.IsDir() {
|
if info.Mode().IsRegular() || info.IsDir() {
|
||||||
return nil
|
return nil
|
||||||
@ -727,7 +753,13 @@ func checkVolumeContents(targetDir, tcName string, payload map[string][]byte, t
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
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)
|
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 {
|
for k, v := range payload {
|
||||||
cleanPathPayload[path.Clean(k)] = v
|
cleanPathPayload[path.Clean(k)] = v
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package common
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
@ -32,27 +33,37 @@ var _ = framework.KubeDescribe("ConfigMap", func() {
|
|||||||
f := framework.NewDefaultFramework("configmap")
|
f := framework.NewDefaultFramework("configmap")
|
||||||
|
|
||||||
It("should be consumable from pods in volume [Conformance]", func() {
|
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() {
|
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() {
|
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() {
|
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() {
|
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() {
|
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() {
|
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 (
|
var (
|
||||||
name = "configmap-test-volume-" + string(uuid.NewUUID())
|
name = "configmap-test-volume-" + string(uuid.NewUUID())
|
||||||
volumeName = "configmap-volume"
|
volumeName = "configmap-volume"
|
||||||
@ -331,13 +342,14 @@ func doConfigMapE2EWithoutMappings(f *framework.Framework, uid, fsGroup int64) {
|
|||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Name: "configmap-volume-test",
|
Name: "configmap-volume-test",
|
||||||
Image: "gcr.io/google_containers/mounttest:0.6",
|
Image: "gcr.io/google_containers/mounttest:0.7",
|
||||||
Args: []string{"--file_content=/etc/configmap-volume/data-1"},
|
Args: []string{
|
||||||
|
"--file_content=/etc/configmap-volume/data-1",
|
||||||
|
"--file_mode=/etc/configmap-volume/data-1"},
|
||||||
VolumeMounts: []api.VolumeMount{
|
VolumeMounts: []api.VolumeMount{
|
||||||
{
|
{
|
||||||
Name: volumeName,
|
Name: volumeName,
|
||||||
MountPath: volumeMountPath,
|
MountPath: volumeMountPath,
|
||||||
ReadOnly: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -353,14 +365,27 @@ func doConfigMapE2EWithoutMappings(f *framework.Framework, uid, fsGroup int64) {
|
|||||||
if fsGroup != 0 {
|
if fsGroup != 0 {
|
||||||
pod.Spec.SecurityContext.FSGroup = &fsGroup
|
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",
|
"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 (
|
var (
|
||||||
name = "configmap-test-volume-map-" + string(uuid.NewUUID())
|
name = "configmap-test-volume-map-" + string(uuid.NewUUID())
|
||||||
volumeName = "configmap-volume"
|
volumeName = "configmap-volume"
|
||||||
@ -407,8 +432,9 @@ func doConfigMapE2EWithMappings(f *framework.Framework, uid, fsGroup int64) {
|
|||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Name: "configmap-volume-test",
|
Name: "configmap-volume-test",
|
||||||
Image: "gcr.io/google_containers/mounttest:0.6",
|
Image: "gcr.io/google_containers/mounttest:0.7",
|
||||||
Args: []string{"--file_content=/etc/configmap-volume/path/to/data-2"},
|
Args: []string{"--file_content=/etc/configmap-volume/path/to/data-2",
|
||||||
|
"--file_mode=/etc/configmap-volume/path/to/data-2"},
|
||||||
VolumeMounts: []api.VolumeMount{
|
VolumeMounts: []api.VolumeMount{
|
||||||
{
|
{
|
||||||
Name: volumeName,
|
Name: volumeName,
|
||||||
@ -429,8 +455,21 @@ func doConfigMapE2EWithMappings(f *framework.Framework, uid, fsGroup int64) {
|
|||||||
if fsGroup != 0 {
|
if fsGroup != 0 {
|
||||||
pod.Spec.SecurityContext.FSGroup = &fsGroup
|
pod.Spec.SecurityContext.FSGroup = &fsGroup
|
||||||
}
|
}
|
||||||
|
if itemMode != nil {
|
||||||
|
pod.Spec.Volumes[0].VolumeSource.ConfigMap.Items[0].Mode = itemMode
|
||||||
|
} else {
|
||||||
|
mode := int32(0644)
|
||||||
|
itemMode = &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/path/to/data-2\": value-2",
|
"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)
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
It("should provide podname as non-root with fsgroup [Feature:FSGroup]", func() {
|
||||||
podName := "metadata-volume-" + string(uuid.NewUUID())
|
podName := "metadata-volume-" + string(uuid.NewUUID())
|
||||||
uid := int64(1001)
|
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 {
|
func downwardAPIVolumePodForSimpleTest(name string, filePath string) *api.Pod {
|
||||||
pod := downwardAPIVolumeBasePod(name, nil, nil)
|
pod := downwardAPIVolumeBasePod(name, nil, nil)
|
||||||
|
|
||||||
|
@ -73,7 +73,6 @@ var _ = framework.KubeDescribe("Secrets", func() {
|
|||||||
{
|
{
|
||||||
Name: volumeName,
|
Name: volumeName,
|
||||||
MountPath: volumeMountPath,
|
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() {
|
It("should be consumable in multiple volumes in a pod [Conformance]", func() {
|
||||||
// This test ensures that the same secret can be mounted in multiple
|
// This test ensures that the same secret can be mounted in multiple
|
||||||
// volumes in the same pod. This test case exists to prevent
|
// volumes in the same pod. This test case exists to prevent
|
||||||
|
@ -237,12 +237,14 @@ func TestServiceAccountTokenAutoMount(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pod we expect to get created
|
// Pod we expect to get created
|
||||||
|
defaultMode := int32(0644)
|
||||||
expectedServiceAccount := serviceaccountadmission.DefaultServiceAccountName
|
expectedServiceAccount := serviceaccountadmission.DefaultServiceAccountName
|
||||||
expectedVolumes := append(protoPod.Spec.Volumes, api.Volume{
|
expectedVolumes := append(protoPod.Spec.Volumes, api.Volume{
|
||||||
Name: defaultTokenName,
|
Name: defaultTokenName,
|
||||||
VolumeSource: api.VolumeSource{
|
VolumeSource: api.VolumeSource{
|
||||||
Secret: &api.SecretVolumeSource{
|
Secret: &api.SecretVolumeSource{
|
||||||
SecretName: defaultTokenName,
|
SecretName: defaultTokenName,
|
||||||
|
DefaultMode: &defaultMode,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user