mirror of https://github.com/k3s-io/k3s
parent
b385a94fed
commit
8fb1b71c66
|
@ -46,6 +46,7 @@ import (
|
|||
"k8s.io/kubernetes/pkg/volume/iscsi"
|
||||
"k8s.io/kubernetes/pkg/volume/nfs"
|
||||
"k8s.io/kubernetes/pkg/volume/photon_pd"
|
||||
"k8s.io/kubernetes/pkg/volume/projected"
|
||||
"k8s.io/kubernetes/pkg/volume/quobyte"
|
||||
"k8s.io/kubernetes/pkg/volume/rbd"
|
||||
"k8s.io/kubernetes/pkg/volume/secret"
|
||||
|
@ -88,6 +89,7 @@ func ProbeVolumePlugins(pluginDir string) []volume.VolumePlugin {
|
|||
allPlugins = append(allPlugins, vsphere_volume.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, photon_pd.ProbeVolumePlugins()...)
|
||||
allPlugins = append(allPlugins, projected.ProbeVolumePlugins()...)
|
||||
return allPlugins
|
||||
}
|
||||
|
||||
|
|
|
@ -243,6 +243,7 @@ pkg/util/yaml
|
|||
pkg/version/prometheus
|
||||
pkg/volume
|
||||
pkg/volume/downwardapi
|
||||
pkg/volume/projected
|
||||
pkg/volume/quobyte
|
||||
pkg/volume/util/nestedpendingoperations
|
||||
pkg/volume/util/operationexecutor
|
||||
|
|
|
@ -147,6 +147,14 @@ test/e2e/common/host_path.go: fmt.Sprintf("--file_content_in_loop=%v", filePat
|
|||
test/e2e/common/host_path.go: fmt.Sprintf("--file_content_in_loop=%v", filePathInReader),
|
||||
test/e2e/common/host_path.go: fmt.Sprintf("--retry_time=%d", retryDuration),
|
||||
test/e2e/common/host_path.go: fmt.Sprintf("--retry_time=%d", retryDuration),
|
||||
test/e2e/common/projected.go: Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/projected-configmap-volume/data-1"},
|
||||
test/e2e/common/projected.go: Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/projected-configmap-volumes/create/data-1"},
|
||||
test/e2e/common/projected.go: Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/projected-configmap-volumes/delete/data-1"},
|
||||
test/e2e/common/projected.go: Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/projected-configmap-volumes/update/data-3"},
|
||||
test/e2e/common/projected.go: Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/projected-secret-volumes/create/data-1"},
|
||||
test/e2e/common/projected.go: Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/projected-secret-volumes/delete/data-1"},
|
||||
test/e2e/common/projected.go: Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/projected-secret-volumes/update/data-3"},
|
||||
test/e2e/common/projected.go: Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=" + filePath},
|
||||
test/e2e/common/secrets.go: Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/secret-volumes/create/data-1"},
|
||||
test/e2e/common/secrets.go: Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/secret-volumes/delete/data-1"},
|
||||
test/e2e/common/secrets.go: Command: []string{"/mt", "--break_on_expected_content=false", "--retry_time=120", "--file_content_in_loop=/etc/secret-volumes/update/data-3"},
|
||||
|
|
|
@ -267,6 +267,16 @@ func coreFuncs(t apitesting.TestingCommon) []interface{} {
|
|||
mode &= 0777
|
||||
d.DefaultMode = &mode
|
||||
},
|
||||
func(s *api.ProjectedVolumeSource, 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(k *api.KeyToPath, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(k) // fuzz self without calling this function again
|
||||
k.Key = c.RandString()
|
||||
|
|
|
@ -294,6 +294,8 @@ type VolumeSource struct {
|
|||
AzureDisk *AzureDiskVolumeSource
|
||||
// PhotonPersistentDisk represents a Photon Controller persistent disk attached and mounted on kubelets host machine
|
||||
PhotonPersistentDisk *PhotonPersistentDiskVolumeSource
|
||||
// Items for all in one resources secrets, configmaps, and downward API
|
||||
Projected *ProjectedVolumeSource
|
||||
}
|
||||
|
||||
// Similar to VolumeSource but meant for the administrator who creates PVs.
|
||||
|
@ -746,7 +748,29 @@ type SecretVolumeSource struct {
|
|||
// mode, like fsGroup, and the result can be other mode bits set.
|
||||
// +optional
|
||||
DefaultMode *int32
|
||||
// Specify whether the Secret or it's key must be defined
|
||||
// Specify whether the Secret or its key must be defined
|
||||
// +optional
|
||||
Optional *bool
|
||||
}
|
||||
|
||||
// Adapts a secret into a projected volume.
|
||||
//
|
||||
// The contents of the target Secret's Data field will be presented in a
|
||||
// projected volume as files using the keys in the Data field as the file names.
|
||||
// Note that this is identical to a secret volume source without the default
|
||||
// mode.
|
||||
type SecretProjection struct {
|
||||
LocalObjectReference
|
||||
// If unspecified, each key-value pair in the Data field of the referenced
|
||||
// Secret will be projected into the volume as a file whose name is the
|
||||
// key and content is the value. If specified, the listed keys will be
|
||||
// projected into the specified paths, and unlisted keys will not be
|
||||
// present. If a key is specified which is not present in the Secret,
|
||||
// the volume setup will error unless it is marked optional. Paths must be
|
||||
// relative and may not contain the '..' path or start with '..'.
|
||||
// +optional
|
||||
Items []KeyToPath
|
||||
// Specify whether the Secret or its key must be defined
|
||||
// +optional
|
||||
Optional *bool
|
||||
}
|
||||
|
@ -927,6 +951,15 @@ type DownwardAPIVolumeFile struct {
|
|||
Mode *int32
|
||||
}
|
||||
|
||||
// Represents downward API info for projecting into a projected volume.
|
||||
// Note that this is identical to a downwardAPI volume source without the default
|
||||
// mode.
|
||||
type DownwardAPIProjection struct {
|
||||
// Items is a list of DownwardAPIVolume file
|
||||
// +optional
|
||||
Items []DownwardAPIVolumeFile
|
||||
}
|
||||
|
||||
// AzureFile represents an Azure File Service mount on the host and bind mount to the pod.
|
||||
type AzureFileVolumeSource struct {
|
||||
// the name of secret that contains Azure Storage Account Name and Key
|
||||
|
@ -1017,6 +1050,54 @@ type ConfigMapVolumeSource struct {
|
|||
Optional *bool
|
||||
}
|
||||
|
||||
// Adapts a ConfigMap into a projected volume.
|
||||
//
|
||||
// The contents of the target ConfigMap's Data field will be presented in a
|
||||
// projected volume as files using the keys in the Data field as the file names,
|
||||
// unless the items element is populated with specific mappings of keys to paths.
|
||||
// Note that this is identical to a configmap volume source without the default
|
||||
// mode.
|
||||
type ConfigMapProjection struct {
|
||||
LocalObjectReference
|
||||
// If unspecified, each key-value pair in the Data field of the referenced
|
||||
// ConfigMap will be projected into the volume as a file whose name is the
|
||||
// key and content is the value. If specified, the listed keys will be
|
||||
// projected into the specified paths, and unlisted keys will not be
|
||||
// present. If a key is specified which is not present in the ConfigMap,
|
||||
// the volume setup will error unless it is marked optional. Paths must be
|
||||
// relative and may not contain the '..' path or start with '..'.
|
||||
// +optional
|
||||
Items []KeyToPath
|
||||
// Specify whether the ConfigMap or it's keys must be defined
|
||||
// +optional
|
||||
Optional *bool
|
||||
}
|
||||
|
||||
// Represents a projected volume source
|
||||
type ProjectedVolumeSource struct {
|
||||
// list of volume projections
|
||||
Sources []VolumeProjection
|
||||
// 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.
|
||||
// +optional
|
||||
DefaultMode *int32
|
||||
}
|
||||
|
||||
// Projection that may be projected along with other supported volume types
|
||||
type VolumeProjection struct {
|
||||
// all types below are the supported types for projection into the same volume
|
||||
|
||||
// information about the secret data to project
|
||||
Secret *SecretProjection
|
||||
// information about the downwardAPI data to project
|
||||
DownwardAPI *DownwardAPIProjection
|
||||
// information about the configMap data to project
|
||||
ConfigMap *ConfigMapProjection
|
||||
}
|
||||
|
||||
// Maps a string key to a path within a volume.
|
||||
type KeyToPath struct {
|
||||
// The key to project.
|
||||
|
|
|
@ -39,6 +39,7 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
|||
SetDefaults_SecretVolumeSource,
|
||||
SetDefaults_ConfigMapVolumeSource,
|
||||
SetDefaults_DownwardAPIVolumeSource,
|
||||
SetDefaults_ProjectedVolumeSource,
|
||||
SetDefaults_Secret,
|
||||
SetDefaults_PersistentVolume,
|
||||
SetDefaults_PersistentVolumeClaim,
|
||||
|
@ -218,6 +219,12 @@ func SetDefaults_Secret(obj *Secret) {
|
|||
obj.Type = SecretTypeOpaque
|
||||
}
|
||||
}
|
||||
func SetDefaults_ProjectedVolumeSource(obj *ProjectedVolumeSource) {
|
||||
if obj.DefaultMode == nil {
|
||||
perm := int32(ProjectedVolumeSourceDefaultMode)
|
||||
obj.DefaultMode = &perm
|
||||
}
|
||||
}
|
||||
func SetDefaults_PersistentVolume(obj *PersistentVolume) {
|
||||
if obj.Status.Phase == "" {
|
||||
obj.Status.Phase = VolumePending
|
||||
|
|
|
@ -376,6 +376,28 @@ func TestSetDefaultDownwardAPIVolumeSource(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSetDefaultProjectedVolumeSource(t *testing.T) {
|
||||
s := v1.PodSpec{}
|
||||
s.Volumes = []v1.Volume{
|
||||
{
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Projected: &v1.ProjectedVolumeSource{},
|
||||
},
|
||||
},
|
||||
}
|
||||
pod := &v1.Pod{
|
||||
Spec: s,
|
||||
}
|
||||
output := roundTrip(t, runtime.Object(pod))
|
||||
pod2 := output.(*v1.Pod)
|
||||
defaultMode := pod2.Spec.Volumes[0].VolumeSource.Projected.DefaultMode
|
||||
expectedMode := v1.ProjectedVolumeSourceDefaultMode
|
||||
|
||||
if defaultMode == nil || *defaultMode != expectedMode {
|
||||
t.Errorf("Expected ProjectedVolumeSource DefaultMode %v, got %v", expectedMode, defaultMode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDefaultSecret(t *testing.T) {
|
||||
s := &v1.Secret{}
|
||||
obj2 := roundTrip(t, runtime.Object(s))
|
||||
|
|
|
@ -326,6 +326,8 @@ type VolumeSource struct {
|
|||
AzureDisk *AzureDiskVolumeSource `json:"azureDisk,omitempty" protobuf:"bytes,22,opt,name=azureDisk"`
|
||||
// PhotonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine
|
||||
PhotonPersistentDisk *PhotonPersistentDiskVolumeSource `json:"photonPersistentDisk,omitempty" protobuf:"bytes,23,opt,name=photonPersistentDisk"`
|
||||
// Items for all in one resources secrets, configmaps, and downward API
|
||||
Projected *ProjectedVolumeSource `json:"projected,omitempty"`
|
||||
}
|
||||
|
||||
// PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace.
|
||||
|
@ -944,6 +946,28 @@ const (
|
|||
SecretVolumeSourceDefaultMode int32 = 0644
|
||||
)
|
||||
|
||||
// Adapts a secret into a projected volume.
|
||||
//
|
||||
// The contents of the target Secret's Data field will be presented in a
|
||||
// projected volume as files using the keys in the Data field as the file names.
|
||||
// Note that this is identical to a secret volume source without the default
|
||||
// mode.
|
||||
type SecretProjection struct {
|
||||
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
|
||||
// If unspecified, each key-value pair in the Data field of the referenced
|
||||
// Secret will be projected into the volume as a file whose name is the
|
||||
// key and content is the value. If specified, the listed keys will be
|
||||
// projected into the specified paths, and unlisted keys will not be
|
||||
// present. If a key is specified which is not present in the Secret,
|
||||
// the volume setup will error unless it is marked optional. Paths must be
|
||||
// relative and may not contain the '..' path or start with '..'.
|
||||
// +optional
|
||||
Items []KeyToPath `json:"items,omitempty" protobuf:"bytes,2,rep,name=items"`
|
||||
// Specify whether the Secret or its key must be defined
|
||||
// +optional
|
||||
Optional *bool `json:"optional,omitempty" protobuf:"varint,4,opt,name=optional"`
|
||||
}
|
||||
|
||||
// Represents an NFS mount that lasts the lifetime of a pod.
|
||||
// NFS volumes do not support ownership management or SELinux relabeling.
|
||||
type NFSVolumeSource struct {
|
||||
|
@ -1108,6 +1132,58 @@ const (
|
|||
ConfigMapVolumeSourceDefaultMode int32 = 0644
|
||||
)
|
||||
|
||||
// Adapts a ConfigMap into a projected volume.
|
||||
//
|
||||
// The contents of the target ConfigMap's Data field will be presented in a
|
||||
// projected volume as files using the keys in the Data field as the file names,
|
||||
// unless the items element is populated with specific mappings of keys to paths.
|
||||
// Note that this is identical to a configmap volume source without the default
|
||||
// mode.
|
||||
type ConfigMapProjection struct {
|
||||
LocalObjectReference `json:",inline" protobuf:"bytes,1,opt,name=localObjectReference"`
|
||||
// If unspecified, each key-value pair in the Data field of the referenced
|
||||
// ConfigMap will be projected into the volume as a file whose name is the
|
||||
// key and content is the value. If specified, the listed keys will be
|
||||
// projected into the specified paths, and unlisted keys will not be
|
||||
// present. If a key is specified which is not present in the ConfigMap,
|
||||
// the volume setup will error unless it is marked optional. Paths must be
|
||||
// relative and may not contain the '..' path or start with '..'.
|
||||
// +optional
|
||||
Items []KeyToPath `json:"items,omitempty" protobuf:"bytes,2,rep,name=items"`
|
||||
// Specify whether the ConfigMap or it's keys must be defined
|
||||
// +optional
|
||||
Optional *bool `json:"optional,omitempty" protobuf:"varint,4,opt,name=optional"`
|
||||
}
|
||||
|
||||
// Represents a projected volume source
|
||||
type ProjectedVolumeSource struct {
|
||||
// list of volume projections
|
||||
Sources []VolumeProjection `json:"sources"`
|
||||
// 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.
|
||||
// +optional
|
||||
DefaultMode *int32 `json:"defaultMode,omitempty"`
|
||||
}
|
||||
|
||||
// Projection that may be projected along with other supported volume types
|
||||
type VolumeProjection struct {
|
||||
// all types below are the supported types for projection into the same volume
|
||||
|
||||
// information about the secret data to project
|
||||
Secret *SecretProjection `json:"secret,omitempty"`
|
||||
// information about the downwardAPI data to project
|
||||
DownwardAPI *DownwardAPIProjection `json:"downwardAPI,omitempty"`
|
||||
// information about the configMap data to project
|
||||
ConfigMap *ConfigMapProjection `json:"configMap,omitempty"`
|
||||
}
|
||||
|
||||
const (
|
||||
ProjectedVolumeSourceDefaultMode int32 = 0644
|
||||
)
|
||||
|
||||
// Maps a string key to a path within a volume.
|
||||
type KeyToPath struct {
|
||||
// The key to project.
|
||||
|
@ -4095,6 +4171,15 @@ type DownwardAPIVolumeFile struct {
|
|||
Mode *int32 `json:"mode,omitempty" protobuf:"varint,4,opt,name=mode"`
|
||||
}
|
||||
|
||||
// Represents downward API info for projecting into a projected volume.
|
||||
// Note that this is identical to a downwardAPI volume source without the default
|
||||
// mode.
|
||||
type DownwardAPIProjection struct {
|
||||
// Items is a list of DownwardAPIVolume file
|
||||
// +optional
|
||||
Items []DownwardAPIVolumeFile `json:"items,omitempty" protobuf:"bytes,1,rep,name=items"`
|
||||
}
|
||||
|
||||
// SecurityContext holds security configuration that will be applied to a container.
|
||||
// Some fields are present in both SecurityContext and PodSecurityContext. When both
|
||||
// are set, the values in SecurityContext take precedence.
|
||||
|
|
|
@ -518,6 +518,14 @@ func validateVolumeSource(source *api.VolumeSource, fldPath *field.Path) field.E
|
|||
numVolumes++
|
||||
allErrs = append(allErrs, validateAzureDisk(source.AzureDisk, fldPath.Child("azureDisk"))...)
|
||||
}
|
||||
if source.Projected != nil {
|
||||
if numVolumes > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("projected"), "may not specify more than 1 volume type"))
|
||||
} else {
|
||||
numVolumes++
|
||||
allErrs = append(allErrs, validateProjectedVolumeSource(source.Projected, fldPath.Child("projected"))...)
|
||||
}
|
||||
}
|
||||
|
||||
if numVolumes == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath, "must specify a volume type"))
|
||||
|
@ -723,6 +731,30 @@ var validDownwardAPIFieldPathExpressions = sets.NewString(
|
|||
"metadata.labels",
|
||||
"metadata.annotations")
|
||||
|
||||
func validateDownwardAPIVolumeFile(file *api.DownwardAPIVolumeFile, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if len(file.Path) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
|
||||
}
|
||||
allErrs = append(allErrs, validateLocalNonReservedPath(file.Path, fldPath.Child("path"))...)
|
||||
if file.FieldRef != nil {
|
||||
allErrs = append(allErrs, validateObjectFieldSelector(file.FieldRef, &validDownwardAPIFieldPathExpressions, fldPath.Child("fieldRef"))...)
|
||||
if file.ResourceFieldRef != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, "resource", "fieldRef and resourceFieldRef can not be specified simultaneously"))
|
||||
}
|
||||
} else if file.ResourceFieldRef != nil {
|
||||
allErrs = append(allErrs, validateContainerResourceFieldSelector(file.ResourceFieldRef, &validContainerResourceFieldPathExpressions, fldPath.Child("resourceFieldRef"), true)...)
|
||||
} else {
|
||||
allErrs = append(allErrs, field.Required(fldPath, "one of fieldRef and resourceFieldRef is required"))
|
||||
}
|
||||
if file.Mode != nil && (*file.Mode > 0777 || *file.Mode < 0) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("mode"), *file.Mode, volumeModeErrorMsg))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateDownwardAPIVolumeSource(downwardAPIVolume *api.DownwardAPIVolumeSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
|
@ -732,27 +764,99 @@ func validateDownwardAPIVolumeSource(downwardAPIVolume *api.DownwardAPIVolumeSou
|
|||
}
|
||||
|
||||
for _, file := range downwardAPIVolume.Items {
|
||||
if len(file.Path) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
|
||||
}
|
||||
allErrs = append(allErrs, validateLocalNonReservedPath(file.Path, fldPath.Child("path"))...)
|
||||
if file.FieldRef != nil {
|
||||
allErrs = append(allErrs, validateObjectFieldSelector(file.FieldRef, &validDownwardAPIFieldPathExpressions, fldPath.Child("fieldRef"))...)
|
||||
if file.ResourceFieldRef != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, "resource", "fieldRef and resourceFieldRef can not be specified simultaneously"))
|
||||
allErrs = append(allErrs, validateDownwardAPIVolumeFile(&file, fldPath)...)
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateProjectionSources(projection *api.ProjectedVolumeSource, projectionMode *int32, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
allPaths := sets.String{}
|
||||
|
||||
for _, source := range projection.Sources {
|
||||
numSources := 0
|
||||
if source.Secret != nil {
|
||||
if numSources > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("secret"), "may not specify more than 1 volume type"))
|
||||
} else {
|
||||
numSources++
|
||||
if len(source.Secret.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
|
||||
}
|
||||
itemsPath := fldPath.Child("items")
|
||||
for i, kp := range source.Secret.Items {
|
||||
itemPath := itemsPath.Index(i)
|
||||
allErrs = append(allErrs, validateKeyToPath(&kp, itemPath)...)
|
||||
if len(kp.Path) > 0 {
|
||||
curPath := kp.Path
|
||||
if !allPaths.Has(curPath) {
|
||||
allPaths.Insert(curPath)
|
||||
} else {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, source.Secret.Name, "conflicting duplicate paths"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if file.ResourceFieldRef != nil {
|
||||
allErrs = append(allErrs, validateContainerResourceFieldSelector(file.ResourceFieldRef, &validContainerResourceFieldPathExpressions, fldPath.Child("resourceFieldRef"), true)...)
|
||||
} else {
|
||||
allErrs = append(allErrs, field.Required(fldPath, "one of fieldRef and resourceFieldRef is required"))
|
||||
}
|
||||
if file.Mode != nil && (*file.Mode > 0777 || *file.Mode < 0) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("mode"), *file.Mode, volumeModeErrorMsg))
|
||||
if source.ConfigMap != nil {
|
||||
if numSources > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("configMap"), "may not specify more than 1 volume type"))
|
||||
} else {
|
||||
numSources++
|
||||
if len(source.ConfigMap.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
|
||||
}
|
||||
itemsPath := fldPath.Child("items")
|
||||
for i, kp := range source.ConfigMap.Items {
|
||||
itemPath := itemsPath.Index(i)
|
||||
allErrs = append(allErrs, validateKeyToPath(&kp, itemPath)...)
|
||||
if len(kp.Path) > 0 {
|
||||
curPath := kp.Path
|
||||
if !allPaths.Has(curPath) {
|
||||
allPaths.Insert(curPath)
|
||||
} else {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, source.ConfigMap.Name, "conflicting duplicate paths"))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if source.DownwardAPI != nil {
|
||||
if numSources > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("downwardAPI"), "may not specify more than 1 volume type"))
|
||||
} else {
|
||||
numSources++
|
||||
for _, file := range source.DownwardAPI.Items {
|
||||
allErrs = append(allErrs, validateDownwardAPIVolumeFile(&file, fldPath.Child("downwardAPI"))...)
|
||||
if len(file.Path) > 0 {
|
||||
curPath := file.Path
|
||||
if !allPaths.Has(curPath) {
|
||||
allPaths.Insert(curPath)
|
||||
} else {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, curPath, "conflicting duplicate paths"))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateProjectedVolumeSource(projection *api.ProjectedVolumeSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
projectionMode := projection.DefaultMode
|
||||
if projectionMode != nil && (*projectionMode > 0777 || *projectionMode < 0) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("defaultMode"), *projectionMode, volumeModeErrorMsg))
|
||||
}
|
||||
|
||||
allErrs = append(allErrs, validateProjectionSources(projection, projectionMode, fldPath)...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// This validate will make sure targetPath:
|
||||
// 1. is not abs path
|
||||
// 2. does not have any element which is ".."
|
||||
|
|
|
@ -905,6 +905,7 @@ var (
|
|||
Quobyte FSType = "quobyte"
|
||||
AzureDisk FSType = "azureDisk"
|
||||
PhotonPersistentDisk FSType = "photonPersistentDisk"
|
||||
Projected FSType = "projected"
|
||||
All FSType = "*"
|
||||
)
|
||||
|
||||
|
|
|
@ -279,6 +279,12 @@ func getSecretNames(pod *v1.Pod) sets.String {
|
|||
for i := range pod.Spec.Volumes {
|
||||
if source := pod.Spec.Volumes[i].Secret; source != nil {
|
||||
result.Insert(source.SecretName)
|
||||
} else if source := pod.Spec.Volumes[i].Projected; source != nil {
|
||||
for j := range source.Sources {
|
||||
if secretVolumeSource := source.Sources[j].Secret; secretVolumeSource != nil {
|
||||
result.Insert(secretVolumeSource.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
|
|
|
@ -61,7 +61,9 @@ func GetAllFSTypesAsSet() sets.String {
|
|||
string(extensions.VsphereVolume),
|
||||
string(extensions.Quobyte),
|
||||
string(extensions.AzureDisk),
|
||||
string(extensions.PhotonPersistentDisk))
|
||||
string(extensions.PhotonPersistentDisk),
|
||||
string(extensions.Projected),
|
||||
)
|
||||
return fstypes
|
||||
}
|
||||
|
||||
|
@ -114,6 +116,8 @@ func GetVolumeFSType(v api.Volume) (extensions.FSType, error) {
|
|||
return extensions.AzureDisk, nil
|
||||
case v.PhotonPersistentDisk != nil:
|
||||
return extensions.PhotonPersistentDisk, nil
|
||||
case v.Projected != nil:
|
||||
return extensions.Projected, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("unknown volume type for volume: %#v", v)
|
||||
|
|
|
@ -193,7 +193,7 @@ func (b *configMapVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||
len(configMap.Data),
|
||||
totalBytes)
|
||||
|
||||
payload, err := makePayload(b.source.Items, configMap, b.source.DefaultMode, optional)
|
||||
payload, err := MakePayload(b.source.Items, configMap, b.source.DefaultMode, optional)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -220,7 +220,8 @@ func (b *configMapVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func makePayload(mappings []v1.KeyToPath, configMap *v1.ConfigMap, defaultMode *int32, optional bool) (map[string]volumeutil.FileProjection, error) {
|
||||
// Note: this function is exported so that it can be called from the projection volume driver
|
||||
func MakePayload(mappings []v1.KeyToPath, configMap *v1.ConfigMap, defaultMode *int32, optional bool) (map[string]volumeutil.FileProjection, error) {
|
||||
if defaultMode == nil {
|
||||
return nil, fmt.Errorf("No defaultMode used, not even the default value for it")
|
||||
}
|
||||
|
|
|
@ -238,7 +238,7 @@ func TestMakePayload(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
actualPayload, err := makePayload(tc.mappings, tc.configMap, &tc.mode, tc.optional)
|
||||
actualPayload, err := MakePayload(tc.mappings, tc.configMap, &tc.mode, tc.optional)
|
||||
if err != nil && tc.success {
|
||||
t.Errorf("%v: unexpected failure making payload: %v", tc.name, err)
|
||||
continue
|
||||
|
|
|
@ -175,7 +175,7 @@ func (b *downwardAPIVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||
return err
|
||||
}
|
||||
|
||||
data, err := b.collectData(b.source.DefaultMode)
|
||||
data, err := CollectData(b.source.Items, b.pod, b.plugin.host, b.source.DefaultMode)
|
||||
if err != nil {
|
||||
glog.Errorf("Error preparing data for downwardAPI volume %v for pod %v/%v: %s", b.volName, b.pod.Namespace, b.pod.Name, err.Error())
|
||||
return err
|
||||
|
@ -203,17 +203,19 @@ func (b *downwardAPIVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// 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 value is the (sorted) content of the field to be dumped in the file.
|
||||
func (d *downwardAPIVolume) collectData(defaultMode *int32) (map[string]volumeutil.FileProjection, error) {
|
||||
//
|
||||
// Note: this function is exported so that it can be called from the projection volume driver
|
||||
func CollectData(items []v1.DownwardAPIVolumeFile, pod *v1.Pod, host volume.VolumeHost, defaultMode *int32) (map[string]volumeutil.FileProjection, error) {
|
||||
if defaultMode == nil {
|
||||
return nil, fmt.Errorf("No defaultMode used, not even the default value for it")
|
||||
}
|
||||
|
||||
errlist := []error{}
|
||||
data := make(map[string]volumeutil.FileProjection)
|
||||
for _, fileInfo := range d.items {
|
||||
for _, fileInfo := range items {
|
||||
var fileProjection volumeutil.FileProjection
|
||||
fPath := path.Clean(fileInfo.Path)
|
||||
if fileInfo.Mode != nil {
|
||||
|
@ -223,7 +225,7 @@ func (d *downwardAPIVolume) collectData(defaultMode *int32) (map[string]volumeut
|
|||
}
|
||||
if fileInfo.FieldRef != nil {
|
||||
// TODO: unify with Kubelet.podFieldSelectorRuntimeValue
|
||||
if values, err := fieldpath.ExtractFieldPathAsString(d.pod, fileInfo.FieldRef.FieldPath); err != nil {
|
||||
if values, err := fieldpath.ExtractFieldPathAsString(pod, fileInfo.FieldRef.FieldPath); err != nil {
|
||||
glog.Errorf("Unable to extract field %s: %s", fileInfo.FieldRef.FieldPath, err.Error())
|
||||
errlist = append(errlist, err)
|
||||
} else {
|
||||
|
@ -231,10 +233,10 @@ func (d *downwardAPIVolume) collectData(defaultMode *int32) (map[string]volumeut
|
|||
}
|
||||
} else if fileInfo.ResourceFieldRef != nil {
|
||||
containerName := fileInfo.ResourceFieldRef.ContainerName
|
||||
nodeAllocatable, err := d.plugin.host.GetNodeAllocatable()
|
||||
nodeAllocatable, err := host.GetNodeAllocatable()
|
||||
if err != nil {
|
||||
errlist = append(errlist, err)
|
||||
} else if values, err := fieldpath.ExtractResourceValueByContainerNameAndNodeAllocatable(fileInfo.ResourceFieldRef, d.pod, containerName, nodeAllocatable); err != nil {
|
||||
} else if values, err := fieldpath.ExtractResourceValueByContainerNameAndNodeAllocatable(fileInfo.ResourceFieldRef, pod, containerName, nodeAllocatable); err != nil {
|
||||
glog.Errorf("Unable to extract field %s: %s", fileInfo.ResourceFieldRef.Resource, err.Error())
|
||||
errlist = append(errlist, err)
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package projected
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
utilstrings "k8s.io/kubernetes/pkg/util/strings"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
"k8s.io/kubernetes/pkg/volume/configmap"
|
||||
"k8s.io/kubernetes/pkg/volume/downwardapi"
|
||||
"k8s.io/kubernetes/pkg/volume/secret"
|
||||
volumeutil "k8s.io/kubernetes/pkg/volume/util"
|
||||
)
|
||||
|
||||
// ProbeVolumePlugins is the entry point for plugin detection in a package.
|
||||
func ProbeVolumePlugins() []volume.VolumePlugin {
|
||||
return []volume.VolumePlugin{&projectedPlugin{}}
|
||||
}
|
||||
|
||||
const (
|
||||
projectedPluginName = "kubernetes.io/projected"
|
||||
)
|
||||
|
||||
type projectedPlugin struct {
|
||||
host volume.VolumeHost
|
||||
getSecret func(namespace, name string) (*v1.Secret, error)
|
||||
}
|
||||
|
||||
var _ volume.VolumePlugin = &projectedPlugin{}
|
||||
|
||||
func wrappedVolumeSpec() volume.Spec {
|
||||
return volume.Spec{
|
||||
Volume: &v1.Volume{
|
||||
VolumeSource: v1.VolumeSource{
|
||||
EmptyDir: &v1.EmptyDirVolumeSource{Medium: v1.StorageMediumMemory},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
|
||||
return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedNameForDisk(projectedPluginName), volName)
|
||||
}
|
||||
|
||||
func (plugin *projectedPlugin) Init(host volume.VolumeHost) error {
|
||||
plugin.host = host
|
||||
plugin.getSecret = host.GetSecretFunc()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (plugin *projectedPlugin) GetPluginName() string {
|
||||
return projectedPluginName
|
||||
}
|
||||
|
||||
func (plugin *projectedPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
|
||||
_, _, err := getVolumeSource(spec)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return spec.Name(), nil
|
||||
}
|
||||
|
||||
func (plugin *projectedPlugin) CanSupport(spec *volume.Spec) bool {
|
||||
return spec.Volume != nil && spec.Volume.Projected != nil
|
||||
}
|
||||
|
||||
func (plugin *projectedPlugin) RequiresRemount() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (plugin *projectedPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, opts volume.VolumeOptions) (volume.Mounter, error) {
|
||||
return &projectedVolumeMounter{
|
||||
projectedVolume: &projectedVolume{
|
||||
volName: spec.Name(),
|
||||
sources: spec.Volume.Projected.Sources,
|
||||
podUID: pod.UID,
|
||||
plugin: plugin,
|
||||
},
|
||||
source: *spec.Volume.Projected,
|
||||
pod: pod,
|
||||
opts: &opts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (plugin *projectedPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
|
||||
return &projectedVolumeUnmounter{
|
||||
&projectedVolume{
|
||||
volName: volName,
|
||||
podUID: podUID,
|
||||
plugin: plugin,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (plugin *projectedPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
|
||||
projectedVolume := &v1.Volume{
|
||||
Name: volumeName,
|
||||
VolumeSource: v1.VolumeSource{
|
||||
Projected: &v1.ProjectedVolumeSource{},
|
||||
},
|
||||
}
|
||||
|
||||
return volume.NewSpecFromVolume(projectedVolume), nil
|
||||
}
|
||||
|
||||
type projectedVolume struct {
|
||||
volName string
|
||||
sources []v1.VolumeProjection
|
||||
podUID types.UID
|
||||
plugin *projectedPlugin
|
||||
volume.MetricsNil
|
||||
}
|
||||
|
||||
var _ volume.Volume = &projectedVolume{}
|
||||
|
||||
func (sv *projectedVolume) GetPath() string {
|
||||
return getPath(sv.podUID, sv.volName, sv.plugin.host)
|
||||
}
|
||||
|
||||
type projectedVolumeMounter struct {
|
||||
*projectedVolume
|
||||
|
||||
source v1.ProjectedVolumeSource
|
||||
pod *v1.Pod
|
||||
opts *volume.VolumeOptions
|
||||
}
|
||||
|
||||
var _ volume.Mounter = &projectedVolumeMounter{}
|
||||
|
||||
func (sv *projectedVolume) GetAttributes() volume.Attributes {
|
||||
return volume.Attributes{
|
||||
ReadOnly: true,
|
||||
Managed: true,
|
||||
SupportsSELinux: true,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Checks prior to mount operations to verify that the required components (binaries, etc.)
|
||||
// to mount the volume are available on the underlying node.
|
||||
// If not, it returns an error
|
||||
func (s *projectedVolumeMounter) CanMount() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *projectedVolumeMounter) SetUp(fsGroup *int64) error {
|
||||
return s.SetUpAt(s.GetPath(), fsGroup)
|
||||
}
|
||||
|
||||
func (s *projectedVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
||||
glog.V(3).Infof("Setting up volume %v for pod %v at %v", s.volName, s.pod.UID, dir)
|
||||
|
||||
wrapped, err := s.plugin.host.NewWrapperMounter(s.volName, wrappedVolumeSpec(), s.pod, *s.opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := wrapped.SetUpAt(dir, fsGroup); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := s.collectData()
|
||||
if err != nil {
|
||||
glog.Errorf("Error preparing data for projected volume %v for pod %v/%v: %s", s.volName, s.pod.Namespace, s.pod.Name, err.Error())
|
||||
}
|
||||
|
||||
writerContext := fmt.Sprintf("pod %v/%v volume %v", s.pod.Namespace, s.pod.Name, s.volName)
|
||||
writer, err := volumeutil.NewAtomicWriter(dir, writerContext)
|
||||
if err != nil {
|
||||
glog.Errorf("Error creating atomic writer: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = writer.Write(data)
|
||||
if err != nil {
|
||||
glog.Errorf("Error writing payload to dir: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = volume.SetVolumeOwnership(s, fsGroup)
|
||||
if err != nil {
|
||||
glog.Errorf("Error applying volume ownership settings for group: %v", fsGroup)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *projectedVolumeMounter) collectData() (map[string]volumeutil.FileProjection, error) {
|
||||
if s.source.DefaultMode == nil {
|
||||
return nil, fmt.Errorf("No defaultMode used, not even the default value for it")
|
||||
}
|
||||
|
||||
kubeClient := s.plugin.host.GetKubeClient()
|
||||
if kubeClient == nil {
|
||||
return nil, fmt.Errorf("Cannot setup projected volume %v because kube client is not configured", s.volName)
|
||||
}
|
||||
|
||||
errlist := []error{}
|
||||
payload := make(map[string]volumeutil.FileProjection)
|
||||
for _, source := range s.source.Sources {
|
||||
if source.Secret != nil {
|
||||
optional := source.Secret.Optional != nil && *source.Secret.Optional
|
||||
secretapi, err := s.plugin.getSecret(s.pod.Namespace, source.Secret.Name)
|
||||
if err != nil {
|
||||
if !(errors.IsNotFound(err) && optional) {
|
||||
glog.Errorf("Couldn't get secret %v/%v", s.pod.Namespace, source.Secret.Name)
|
||||
errlist = append(errlist, err)
|
||||
}
|
||||
|
||||
secretapi = &v1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: s.pod.Namespace,
|
||||
Name: source.Secret.Name,
|
||||
},
|
||||
}
|
||||
}
|
||||
secretPayload, err := secret.MakePayload(source.Secret.Items, secretapi, s.source.DefaultMode, optional)
|
||||
if err != nil {
|
||||
glog.Errorf("Couldn't get secret %v/%v: %v", s.pod.Namespace, source.Secret.Name, err)
|
||||
errlist = append(errlist, err)
|
||||
continue
|
||||
}
|
||||
|
||||
for k, v := range secretPayload {
|
||||
payload[k] = v
|
||||
}
|
||||
} else if source.ConfigMap != nil {
|
||||
optional := source.ConfigMap.Optional != nil && *source.ConfigMap.Optional
|
||||
configMap, err := kubeClient.Core().ConfigMaps(s.pod.Namespace).Get(source.ConfigMap.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if !(errors.IsNotFound(err) && optional) {
|
||||
glog.Errorf("Couldn't get configMap %v/%v: %v", s.pod.Namespace, source.ConfigMap.Name, err)
|
||||
errlist = append(errlist, err)
|
||||
continue
|
||||
}
|
||||
configMap = &v1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: s.pod.Namespace,
|
||||
Name: source.ConfigMap.Name,
|
||||
},
|
||||
}
|
||||
}
|
||||
configMapPayload, err := configmap.MakePayload(source.ConfigMap.Items, configMap, s.source.DefaultMode, optional)
|
||||
if err != nil {
|
||||
errlist = append(errlist, err)
|
||||
continue
|
||||
}
|
||||
for k, v := range configMapPayload {
|
||||
payload[k] = v
|
||||
}
|
||||
} else if source.DownwardAPI != nil {
|
||||
downwardAPIPayload, err := downwardapi.CollectData(source.DownwardAPI.Items, s.pod, s.plugin.host, s.source.DefaultMode)
|
||||
if err != nil {
|
||||
errlist = append(errlist, err)
|
||||
continue
|
||||
}
|
||||
for k, v := range downwardAPIPayload {
|
||||
payload[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return payload, utilerrors.NewAggregate(errlist)
|
||||
}
|
||||
|
||||
func sortLines(values string) string {
|
||||
splitted := strings.Split(values, "\n")
|
||||
sort.Strings(splitted)
|
||||
return strings.Join(splitted, "\n")
|
||||
}
|
||||
|
||||
type projectedVolumeUnmounter struct {
|
||||
*projectedVolume
|
||||
}
|
||||
|
||||
var _ volume.Unmounter = &projectedVolumeUnmounter{}
|
||||
|
||||
func (c *projectedVolumeUnmounter) TearDown() error {
|
||||
return c.TearDownAt(c.GetPath())
|
||||
}
|
||||
|
||||
func (c *projectedVolumeUnmounter) TearDownAt(dir string) error {
|
||||
glog.V(3).Infof("Tearing down volume %v for pod %v at %v", c.volName, c.podUID, dir)
|
||||
|
||||
wrapped, err := c.plugin.host.NewWrapperUnmounter(c.volName, wrappedVolumeSpec(), c.podUID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return wrapped.TearDownAt(dir)
|
||||
}
|
||||
|
||||
func getVolumeSource(spec *volume.Spec) (*v1.ProjectedVolumeSource, bool, error) {
|
||||
var readOnly bool
|
||||
var volumeSource *v1.ProjectedVolumeSource
|
||||
|
||||
if spec.Volume != nil && spec.Volume.Projected != nil {
|
||||
volumeSource = spec.Volume.Projected
|
||||
readOnly = spec.ReadOnly
|
||||
}
|
||||
|
||||
return volumeSource, readOnly, fmt.Errorf("Spec does not reference a projected volume type")
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -208,7 +208,7 @@ func (b *secretVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||
len(secret.Data),
|
||||
totalBytes)
|
||||
|
||||
payload, err := makePayload(b.source.Items, secret, b.source.DefaultMode, optional)
|
||||
payload, err := MakePayload(b.source.Items, secret, b.source.DefaultMode, optional)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -235,7 +235,8 @@ func (b *secretVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func makePayload(mappings []v1.KeyToPath, secret *v1.Secret, defaultMode *int32, optional bool) (map[string]volumeutil.FileProjection, error) {
|
||||
// Note: this function is exported so that it can be called from the projection volume driver
|
||||
func MakePayload(mappings []v1.KeyToPath, secret *v1.Secret, defaultMode *int32, optional bool) (map[string]volumeutil.FileProjection, error) {
|
||||
if defaultMode == nil {
|
||||
return nil, fmt.Errorf("No defaultMode used, not even the default value for it")
|
||||
}
|
||||
|
|
|
@ -241,7 +241,7 @@ func TestMakePayload(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
actualPayload, err := makePayload(tc.mappings, tc.secret, &tc.mode, tc.optional)
|
||||
actualPayload, err := MakePayload(tc.mappings, tc.secret, &tc.mode, tc.optional)
|
||||
if err != nil && tc.success {
|
||||
t.Errorf("%v: unexpected failure making payload: %v", tc.name, err)
|
||||
continue
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue