Unverified Commit 83ff0f6c authored by Kubernetes Prow Robot's avatar Kubernetes Prow Robot Committed by GitHub

Merge pull request #74086 from vladimirvivien/csi-inline-volume-api

CSI Inline Volume Implementation
parents 583ff363 923ad369
......@@ -6087,6 +6087,38 @@
],
"type": "object"
},
"io.k8s.api.core.v1.CSIVolumeSource": {
"description": "Represents a source location of a volume to mount, managed by an external CSI driver",
"properties": {
"driver": {
"description": "Driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster.",
"type": "string"
},
"fsType": {
"description": "Filesystem type to mount. Ex. \"ext4\", \"xfs\", \"ntfs\". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply.",
"type": "string"
},
"nodePublishSecretRef": {
"$ref": "#/definitions/io.k8s.api.core.v1.LocalObjectReference",
"description": "NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secret references are passed."
},
"readOnly": {
"description": "Specifies a read-only configuration for the volume. Defaults to false (read/write).",
"type": "boolean"
},
"volumeAttributes": {
"additionalProperties": {
"type": "string"
},
"description": "VolumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values.",
"type": "object"
}
},
"required": [
"driver"
],
"type": "object"
},
"io.k8s.api.core.v1.Capabilities": {
"description": "Adds and removes POSIX capabilities from running containers.",
"properties": {
......@@ -8793,7 +8825,7 @@
},
"csi": {
"$ref": "#/definitions/io.k8s.api.core.v1.CSIPersistentVolumeSource",
"description": "CSI represents storage that handled by an external CSI driver (Beta feature)."
"description": "CSI represents storage that is handled by an external CSI driver (Beta feature)."
},
"fc": {
"$ref": "#/definitions/io.k8s.api.core.v1.FCVolumeSource",
......@@ -10940,6 +10972,10 @@
"$ref": "#/definitions/io.k8s.api.core.v1.ConfigMapVolumeSource",
"description": "ConfigMap represents a configMap that should populate this volume"
},
"csi": {
"$ref": "#/definitions/io.k8s.api.core.v1.CSIVolumeSource",
"description": "CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature)."
},
"downwardAPI": {
"$ref": "#/definitions/io.k8s.api.core.v1.DownwardAPIVolumeSource",
"description": "DownwardAPI represents downward API about the pod that should populate this volume"
......@@ -11305,6 +11341,19 @@
],
"type": "object"
},
"io.k8s.api.extensions.v1beta1.AllowedCSIDriver": {
"description": "AllowedCSIDriver represents a single inline CSI Driver that is allowed to be used.",
"properties": {
"name": {
"description": "Name is the registered name of the CSI driver",
"type": "string"
}
},
"required": [
"name"
],
"type": "object"
},
"io.k8s.api.extensions.v1beta1.AllowedFlexVolume": {
"description": "AllowedFlexVolume represents a single Flexvolume that is allowed to be used. Deprecated: use AllowedFlexVolume from policy API Group instead.",
"properties": {
......@@ -12302,6 +12351,13 @@
"description": "allowPrivilegeEscalation determines if a pod can request to allow privilege escalation. If unspecified, defaults to true.",
"type": "boolean"
},
"allowedCSIDrivers": {
"description": "AllowedCSIDrivers is a whitelist of inline CSI drivers that must be explicitly set to be embedded within a pod spec. An empty value means no CSI drivers can run inline within a pod spec.",
"items": {
"$ref": "#/definitions/io.k8s.api.extensions.v1beta1.AllowedCSIDriver"
},
"type": "array"
},
"allowedCapabilities": {
"description": "allowedCapabilities is a list of capabilities that can be requested to add to the container. Capabilities in this field may be added at the pod author's discretion. You must not list a capability in both allowedCapabilities and requiredDropCapabilities.",
"items": {
......@@ -13288,6 +13344,19 @@
}
]
},
"io.k8s.api.policy.v1beta1.AllowedCSIDriver": {
"description": "AllowedCSIDriver represents a single inline CSI Driver that is allowed to be used.",
"properties": {
"name": {
"description": "Name is the registered name of the CSI driver",
"type": "string"
}
},
"required": [
"name"
],
"type": "object"
},
"io.k8s.api.policy.v1beta1.AllowedFlexVolume": {
"description": "AllowedFlexVolume represents a single Flexvolume that is allowed to be used.",
"properties": {
......@@ -13599,6 +13668,13 @@
"description": "allowPrivilegeEscalation determines if a pod can request to allow privilege escalation. If unspecified, defaults to true.",
"type": "boolean"
},
"allowedCSIDrivers": {
"description": "AllowedCSIDrivers is a whitelist of inline CSI drivers that must be explicitly set to be embedded within a pod spec. An empty value means no CSI drivers can run inline within a pod spec.",
"items": {
"$ref": "#/definitions/io.k8s.api.policy.v1beta1.AllowedCSIDriver"
},
"type": "array"
},
"allowedCapabilities": {
"description": "allowedCapabilities is a list of capabilities that can be requested to add to the container. Capabilities in this field may be added at the pod author's discretion. You must not list a capability in both allowedCapabilities and requiredDropCapabilities.",
"items": {
......@@ -165,6 +165,10 @@ func ProbeControllerVolumePlugins(cloud cloudprovider.Interface, config kubectrl
allPlugins = append(allPlugins, azure_dd.ProbeVolumePlugins()...)
allPlugins = append(allPlugins, photon_pd.ProbeVolumePlugins()...)
if utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
allPlugins = append(allPlugins, csi.ProbeVolumePlugins()...)
}
return allPlugins
}
......
......@@ -97,6 +97,10 @@ func VisitPodSecretNames(pod *api.Pod, visitor Visitor) bool {
if source.StorageOS.SecretRef != nil && !visitor(source.StorageOS.SecretRef.Name) {
return false
}
case source.CSI != nil:
if source.CSI.NodePublishSecretRef != nil && !visitor(source.CSI.NodePublishSecretRef.Name) {
return false
}
}
}
return true
......@@ -370,6 +374,9 @@ func dropDisabledFields(
}
dropDisabledProcMountField(podSpec, oldPodSpec)
dropDisabledCSIVolumeSourceAlphaFields(podSpec, oldPodSpec)
}
// dropDisabledRunAsGroupField removes disabled fields from PodSpec related
......@@ -423,6 +430,16 @@ func dropDisabledVolumeDevicesFields(podSpec, oldPodSpec *api.PodSpec) {
}
}
// dropDisabledCSIVolumeSourceAlphaFields removes disabled alpha fields from []CSIVolumeSource.
// This should be called from PrepareForCreate/PrepareForUpdate for all pod specs resources containing a CSIVolumeSource
func dropDisabledCSIVolumeSourceAlphaFields(podSpec, oldPodSpec *api.PodSpec) {
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) && !csiInUse(oldPodSpec) {
for i := range podSpec.Volumes {
podSpec.Volumes[i].CSI = nil
}
}
}
// subpathInUse returns true if the pod spec is non-nil and has a volume mount that makes use of the subPath feature
func subpathInUse(podSpec *api.PodSpec) bool {
if podSpec == nil {
......@@ -616,3 +633,16 @@ func subpathExprInUse(podSpec *api.PodSpec) bool {
}
return false
}
// csiInUse returns true if any pod's spec include inline CSI volumes.
func csiInUse(podSpec *api.PodSpec) bool {
if podSpec == nil {
return false
}
for i := range podSpec.Volumes {
if podSpec.Volumes[i].CSI != nil {
return true
}
}
return false
}
......@@ -104,7 +104,11 @@ func TestPodSecrets(t *testing.T) {
VolumeSource: api.VolumeSource{
StorageOS: &api.StorageOSVolumeSource{
SecretRef: &api.LocalObjectReference{
Name: "Spec.Volumes[*].VolumeSource.StorageOS.SecretRef"}}}}},
Name: "Spec.Volumes[*].VolumeSource.StorageOS.SecretRef"}}}}, {
VolumeSource: api.VolumeSource{
CSI: &api.CSIVolumeSource{
NodePublishSecretRef: &api.LocalObjectReference{
Name: "Spec.Volumes[*].VolumeSource.CSI.NodePublishSecretRef"}}}}},
},
}
extractedNames := sets.NewString()
......@@ -136,6 +140,7 @@ func TestPodSecrets(t *testing.T) {
"Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef",
"Spec.Volumes[*].VolumeSource.ISCSI.SecretRef",
"Spec.Volumes[*].VolumeSource.StorageOS.SecretRef",
"Spec.Volumes[*].VolumeSource.CSI.NodePublishSecretRef",
)
secretPaths := collectResourcePaths(t, "secret", nil, "", reflect.TypeOf(&api.Pod{}))
secretPaths = secretPaths.Difference(excludedSecretPaths)
......
......@@ -35,6 +35,9 @@ func DropDisabledFields(pspSpec, oldPSPSpec *policy.PodSecurityPolicySpec) {
pspSpec.AllowedUnsafeSysctls = nil
pspSpec.ForbiddenSysctls = nil
}
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
pspSpec.AllowedCSIDrivers = nil
}
}
func allowedProcMountTypesInUse(oldPSPSpec *policy.PodSecurityPolicySpec) bool {
......
......@@ -120,6 +120,10 @@ func VisitPodSecretNames(pod *v1.Pod, visitor Visitor) bool {
if source.StorageOS.SecretRef != nil && !visitor(source.StorageOS.SecretRef.Name) {
return false
}
case source.CSI != nil:
if source.CSI.NodePublishSecretRef != nil && !visitor(source.CSI.NodePublishSecretRef.Name) {
return false
}
}
}
return true
......
......@@ -268,7 +268,11 @@ func TestPodSecrets(t *testing.T) {
VolumeSource: v1.VolumeSource{
StorageOS: &v1.StorageOSVolumeSource{
SecretRef: &v1.LocalObjectReference{
Name: "Spec.Volumes[*].VolumeSource.StorageOS.SecretRef"}}}}},
Name: "Spec.Volumes[*].VolumeSource.StorageOS.SecretRef"}}}}, {
VolumeSource: v1.VolumeSource{
CSI: &v1.CSIVolumeSource{
NodePublishSecretRef: &v1.LocalObjectReference{
Name: "Spec.Volumes[*].VolumeSource.CSI.NodePublishSecretRef"}}}}},
},
}
extractedNames := sets.NewString()
......@@ -300,6 +304,7 @@ func TestPodSecrets(t *testing.T) {
"Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef",
"Spec.Volumes[*].VolumeSource.ISCSI.SecretRef",
"Spec.Volumes[*].VolumeSource.StorageOS.SecretRef",
"Spec.Volumes[*].VolumeSource.CSI.NodePublishSecretRef",
)
secretPaths := collectResourcePaths(t, "secret", nil, "", reflect.TypeOf(&v1.Pod{}))
secretPaths = secretPaths.Difference(excludedSecretPaths)
......
......@@ -154,6 +154,9 @@ type VolumeSource struct {
// StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod
// +optional
StorageOS *StorageOSVolumeSource
// CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature).
// +optional
CSI *CSIVolumeSource
}
// Similar to VolumeSource but meant for the administrator who creates PVs.
......@@ -229,7 +232,7 @@ type PersistentVolumeSource struct {
// More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md
// +optional
StorageOS *StorageOSPersistentVolumeSource
// CSI (Container Storage Interface) represents storage that handled by an external CSI driver.
// CSI (Container Storage Interface) represents storage that is handled by an external CSI driver.
// +optional
CSI *CSIPersistentVolumeSource
}
......@@ -1603,6 +1606,38 @@ type CSIPersistentVolumeSource struct {
NodePublishSecretRef *SecretReference
}
// Represents a source location of a volume to mount, managed by an external CSI driver
type CSIVolumeSource struct {
// Driver is the name of the CSI driver that handles this volume.
// Consult with your admin for the correct name as registered in the cluster.
// Required.
Driver string
// Specifies a read-only configuration for the volume.
// Defaults to false (read/write).
// +optional
ReadOnly *bool
// Filesystem type to mount. Ex. "ext4", "xfs", "ntfs".
// If not provided, the empty value is passed to the associated CSI driver
// which will determine the default filesystem to apply.
// +optional
FSType *string
// VolumeAttributes stores driver-specific properties that are passed to the CSI
// driver. Consult your driver's documentation for supported values.
// +optional
VolumeAttributes map[string]string
// NodePublishSecretRef is a reference to the secret object containing
// sensitive information to pass to the CSI driver to complete the CSI
// NodePublishVolume and NodeUnpublishVolume calls.
// This field is optional, and may be empty if no secret is required. If the
// secret object contains more than one secret, all secret references are passed.
// +optional
NodePublishSecretRef *LocalObjectReference
}
// ContainerPort represents a network port in a single container
type ContainerPort struct {
// Optional: If specified, this must be an IANA_SVC_NAME Each named port
......
......@@ -130,6 +130,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.CSIVolumeSource)(nil), (*core.CSIVolumeSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_CSIVolumeSource_To_core_CSIVolumeSource(a.(*v1.CSIVolumeSource), b.(*core.CSIVolumeSource), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*core.CSIVolumeSource)(nil), (*v1.CSIVolumeSource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_core_CSIVolumeSource_To_v1_CSIVolumeSource(a.(*core.CSIVolumeSource), b.(*v1.CSIVolumeSource), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.Capabilities)(nil), (*core.Capabilities)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_Capabilities_To_core_Capabilities(a.(*v1.Capabilities), b.(*core.Capabilities), scope)
}); err != nil {
......@@ -2330,6 +2340,34 @@ func Convert_core_CSIPersistentVolumeSource_To_v1_CSIPersistentVolumeSource(in *
return autoConvert_core_CSIPersistentVolumeSource_To_v1_CSIPersistentVolumeSource(in, out, s)
}
func autoConvert_v1_CSIVolumeSource_To_core_CSIVolumeSource(in *v1.CSIVolumeSource, out *core.CSIVolumeSource, s conversion.Scope) error {
out.Driver = in.Driver
out.ReadOnly = (*bool)(unsafe.Pointer(in.ReadOnly))
out.FSType = (*string)(unsafe.Pointer(in.FSType))
out.VolumeAttributes = *(*map[string]string)(unsafe.Pointer(&in.VolumeAttributes))
out.NodePublishSecretRef = (*core.LocalObjectReference)(unsafe.Pointer(in.NodePublishSecretRef))
return nil
}
// Convert_v1_CSIVolumeSource_To_core_CSIVolumeSource is an autogenerated conversion function.
func Convert_v1_CSIVolumeSource_To_core_CSIVolumeSource(in *v1.CSIVolumeSource, out *core.CSIVolumeSource, s conversion.Scope) error {
return autoConvert_v1_CSIVolumeSource_To_core_CSIVolumeSource(in, out, s)
}
func autoConvert_core_CSIVolumeSource_To_v1_CSIVolumeSource(in *core.CSIVolumeSource, out *v1.CSIVolumeSource, s conversion.Scope) error {
out.Driver = in.Driver
out.ReadOnly = (*bool)(unsafe.Pointer(in.ReadOnly))
out.FSType = (*string)(unsafe.Pointer(in.FSType))
out.VolumeAttributes = *(*map[string]string)(unsafe.Pointer(&in.VolumeAttributes))
out.NodePublishSecretRef = (*v1.LocalObjectReference)(unsafe.Pointer(in.NodePublishSecretRef))
return nil
}
// Convert_core_CSIVolumeSource_To_v1_CSIVolumeSource is an autogenerated conversion function.
func Convert_core_CSIVolumeSource_To_v1_CSIVolumeSource(in *core.CSIVolumeSource, out *v1.CSIVolumeSource, s conversion.Scope) error {
return autoConvert_core_CSIVolumeSource_To_v1_CSIVolumeSource(in, out, s)
}
func autoConvert_v1_Capabilities_To_core_Capabilities(in *v1.Capabilities, out *core.Capabilities, s conversion.Scope) error {
out.Add = *(*[]core.Capability)(unsafe.Pointer(&in.Add))
out.Drop = *(*[]core.Capability)(unsafe.Pointer(&in.Drop))
......@@ -7494,6 +7532,7 @@ func autoConvert_v1_VolumeSource_To_core_VolumeSource(in *v1.VolumeSource, out *
out.PortworxVolume = (*core.PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
out.ScaleIO = (*core.ScaleIOVolumeSource)(unsafe.Pointer(in.ScaleIO))
out.StorageOS = (*core.StorageOSVolumeSource)(unsafe.Pointer(in.StorageOS))
out.CSI = (*core.CSIVolumeSource)(unsafe.Pointer(in.CSI))
return nil
}
......@@ -7538,6 +7577,7 @@ func autoConvert_core_VolumeSource_To_v1_VolumeSource(in *core.VolumeSource, out
out.PortworxVolume = (*v1.PortworxVolumeSource)(unsafe.Pointer(in.PortworxVolume))
out.ScaleIO = (*v1.ScaleIOVolumeSource)(unsafe.Pointer(in.ScaleIO))
out.StorageOS = (*v1.StorageOSVolumeSource)(unsafe.Pointer(in.StorageOS))
out.CSI = (*v1.CSIVolumeSource)(unsafe.Pointer(in.CSI))
return nil
}
......
......@@ -626,6 +626,14 @@ func validateVolumeSource(source *core.VolumeSource, fldPath *field.Path, volNam
allErrs = append(allErrs, validateScaleIOVolumeSource(source.ScaleIO, fldPath.Child("scaleIO"))...)
}
}
if source.CSI != nil {
if numVolumes > 0 {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("csi"), "may not specify more than 1 volume type"))
} else {
numVolumes++
allErrs = append(allErrs, validateCSIVolumeSource(source.CSI, fldPath.Child("csi"))...)
}
}
if numVolumes == 0 {
allErrs = append(allErrs, field.Required(fldPath, "must specify a volume type"))
......@@ -1484,16 +1492,20 @@ func validateCSIPersistentVolumeSource(csi *core.CSIPersistentVolumeSource, fldP
}
}
if csi.NodeStageSecretRef != nil {
if len(csi.NodeStageSecretRef.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("nodeStageSecretRef", "name"), ""))
} else {
allErrs = append(allErrs, ValidateDNS1123Label(csi.NodeStageSecretRef.Name, fldPath.Child("name"))...)
}
if len(csi.NodeStageSecretRef.Namespace) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("nodeStageSecretRef", "namespace"), ""))
return allErrs
}
func validateCSIVolumeSource(csi *core.CSIVolumeSource, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateCSIDriverName(csi.Driver, fldPath.Child("driver"))...)
if csi.NodePublishSecretRef != nil {
if len(csi.NodePublishSecretRef.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("nodePublishSecretRef ", "name"), ""))
} else {
allErrs = append(allErrs, ValidateDNS1123Label(csi.NodeStageSecretRef.Namespace, fldPath.Child("namespace"))...)
for _, msg := range ValidateSecretName(csi.NodePublishSecretRef.Name, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), csi.NodePublishSecretRef.Name, msg))
}
}
}
......
......@@ -251,6 +251,44 @@ func (in *CSIPersistentVolumeSource) DeepCopy() *CSIPersistentVolumeSource {
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CSIVolumeSource) DeepCopyInto(out *CSIVolumeSource) {
*out = *in
if in.ReadOnly != nil {
in, out := &in.ReadOnly, &out.ReadOnly
*out = new(bool)
**out = **in
}
if in.FSType != nil {
in, out := &in.FSType, &out.FSType
*out = new(string)
**out = **in
}
if in.VolumeAttributes != nil {
in, out := &in.VolumeAttributes, &out.VolumeAttributes
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.NodePublishSecretRef != nil {
in, out := &in.NodePublishSecretRef, &out.NodePublishSecretRef
*out = new(LocalObjectReference)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CSIVolumeSource.
func (in *CSIVolumeSource) DeepCopy() *CSIVolumeSource {
if in == nil {
return nil
}
out := new(CSIVolumeSource)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Capabilities) DeepCopyInto(out *Capabilities) {
*out = *in
if in.Add != nil {
......@@ -5368,6 +5406,11 @@ func (in *VolumeSource) DeepCopyInto(out *VolumeSource) {
*out = new(StorageOSVolumeSource)
(*in).DeepCopyInto(*out)
}
if in.CSI != nil {
in, out := &in.CSI, &out.CSI
*out = new(CSIVolumeSource)
(*in).DeepCopyInto(*out)
}
return
}
......
......@@ -45,6 +45,16 @@ func init() {
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*v1beta1.AllowedCSIDriver)(nil), (*policy.AllowedCSIDriver)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_AllowedCSIDriver_To_policy_AllowedCSIDriver(a.(*v1beta1.AllowedCSIDriver), b.(*policy.AllowedCSIDriver), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*policy.AllowedCSIDriver)(nil), (*v1beta1.AllowedCSIDriver)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_policy_AllowedCSIDriver_To_v1beta1_AllowedCSIDriver(a.(*policy.AllowedCSIDriver), b.(*v1beta1.AllowedCSIDriver), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1beta1.AllowedFlexVolume)(nil), (*policy.AllowedFlexVolume)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_AllowedFlexVolume_To_policy_AllowedFlexVolume(a.(*v1beta1.AllowedFlexVolume), b.(*policy.AllowedFlexVolume), scope)
}); err != nil {
......@@ -738,6 +748,26 @@ func RegisterConversions(s *runtime.Scheme) error {
return nil
}
func autoConvert_v1beta1_AllowedCSIDriver_To_policy_AllowedCSIDriver(in *v1beta1.AllowedCSIDriver, out *policy.AllowedCSIDriver, s conversion.Scope) error {
out.Name = in.Name
return nil
}
// Convert_v1beta1_AllowedCSIDriver_To_policy_AllowedCSIDriver is an autogenerated conversion function.
func Convert_v1beta1_AllowedCSIDriver_To_policy_AllowedCSIDriver(in *v1beta1.AllowedCSIDriver, out *policy.AllowedCSIDriver, s conversion.Scope) error {
return autoConvert_v1beta1_AllowedCSIDriver_To_policy_AllowedCSIDriver(in, out, s)
}
func autoConvert_policy_AllowedCSIDriver_To_v1beta1_AllowedCSIDriver(in *policy.AllowedCSIDriver, out *v1beta1.AllowedCSIDriver, s conversion.Scope) error {
out.Name = in.Name
return nil
}
// Convert_policy_AllowedCSIDriver_To_v1beta1_AllowedCSIDriver is an autogenerated conversion function.
func Convert_policy_AllowedCSIDriver_To_v1beta1_AllowedCSIDriver(in *policy.AllowedCSIDriver, out *v1beta1.AllowedCSIDriver, s conversion.Scope) error {
return autoConvert_policy_AllowedCSIDriver_To_v1beta1_AllowedCSIDriver(in, out, s)
}
func autoConvert_v1beta1_AllowedFlexVolume_To_policy_AllowedFlexVolume(in *v1beta1.AllowedFlexVolume, out *policy.AllowedFlexVolume, s conversion.Scope) error {
out.Driver = in.Driver
return nil
......@@ -1894,6 +1924,7 @@ func autoConvert_v1beta1_PodSecurityPolicySpec_To_policy_PodSecurityPolicySpec(i
}
out.AllowedHostPaths = *(*[]policy.AllowedHostPath)(unsafe.Pointer(&in.AllowedHostPaths))
out.AllowedFlexVolumes = *(*[]policy.AllowedFlexVolume)(unsafe.Pointer(&in.AllowedFlexVolumes))
out.AllowedCSIDrivers = *(*[]policy.AllowedCSIDriver)(unsafe.Pointer(&in.AllowedCSIDrivers))
out.AllowedUnsafeSysctls = *(*[]string)(unsafe.Pointer(&in.AllowedUnsafeSysctls))
out.ForbiddenSysctls = *(*[]string)(unsafe.Pointer(&in.ForbiddenSysctls))
out.AllowedProcMountTypes = *(*[]core.ProcMountType)(unsafe.Pointer(&in.AllowedProcMountTypes))
......@@ -1935,6 +1966,7 @@ func autoConvert_policy_PodSecurityPolicySpec_To_v1beta1_PodSecurityPolicySpec(i
}
out.AllowedHostPaths = *(*[]v1beta1.AllowedHostPath)(unsafe.Pointer(&in.AllowedHostPaths))
out.AllowedFlexVolumes = *(*[]v1beta1.AllowedFlexVolume)(unsafe.Pointer(&in.AllowedFlexVolumes))
out.AllowedCSIDrivers = *(*[]v1beta1.AllowedCSIDriver)(unsafe.Pointer(&in.AllowedCSIDrivers))
out.AllowedUnsafeSysctls = *(*[]string)(unsafe.Pointer(&in.AllowedUnsafeSysctls))
out.ForbiddenSysctls = *(*[]string)(unsafe.Pointer(&in.ForbiddenSysctls))
out.AllowedProcMountTypes = *(*[]v1.ProcMountType)(unsafe.Pointer(&in.AllowedProcMountTypes))
......
......@@ -213,6 +213,10 @@ type PodSecurityPolicySpec struct {
// is allowed in the "Volumes" field.
// +optional
AllowedFlexVolumes []AllowedFlexVolume
// AllowedCSIDrivers is a whitelist of inline CSI drivers that must be explicitly set to be embedded within a pod spec.
// An empty value means no CSI drivers can run inline within a pod spec.
// +optional
AllowedCSIDrivers []AllowedCSIDriver
// AllowedUnsafeSysctls is a list of explicitly allowed unsafe sysctls, defaults to none.
// Each entry is either a plain sysctl name or ends in "*" in which case it is considered
// as a prefix of allowed sysctls. Single * means all unsafe sysctls are allowed.
......@@ -308,6 +312,12 @@ type AllowedFlexVolume struct {
Driver string
}
// AllowedCSIDriver represents a single inline CSI Driver that is allowed to be used.
type AllowedCSIDriver struct {
// Name is the registered name of the CSI driver
Name string
}
// SELinuxStrategyOptions defines the strategy type and any options used to create the strategy.
type SELinuxStrategyOptions struct {
// Rule is the strategy that will dictate the allowable labels that may be set.
......
......@@ -40,6 +40,16 @@ func init() {
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*v1beta1.AllowedCSIDriver)(nil), (*policy.AllowedCSIDriver)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_AllowedCSIDriver_To_policy_AllowedCSIDriver(a.(*v1beta1.AllowedCSIDriver), b.(*policy.AllowedCSIDriver), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*policy.AllowedCSIDriver)(nil), (*v1beta1.AllowedCSIDriver)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_policy_AllowedCSIDriver_To_v1beta1_AllowedCSIDriver(a.(*policy.AllowedCSIDriver), b.(*v1beta1.AllowedCSIDriver), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1beta1.AllowedFlexVolume)(nil), (*policy.AllowedFlexVolume)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_AllowedFlexVolume_To_policy_AllowedFlexVolume(a.(*v1beta1.AllowedFlexVolume), b.(*policy.AllowedFlexVolume), scope)
}); err != nil {
......@@ -213,6 +223,26 @@ func RegisterConversions(s *runtime.Scheme) error {
return nil
}
func autoConvert_v1beta1_AllowedCSIDriver_To_policy_AllowedCSIDriver(in *v1beta1.AllowedCSIDriver, out *policy.AllowedCSIDriver, s conversion.Scope) error {
out.Name = in.Name
return nil
}
// Convert_v1beta1_AllowedCSIDriver_To_policy_AllowedCSIDriver is an autogenerated conversion function.
func Convert_v1beta1_AllowedCSIDriver_To_policy_AllowedCSIDriver(in *v1beta1.AllowedCSIDriver, out *policy.AllowedCSIDriver, s conversion.Scope) error {
return autoConvert_v1beta1_AllowedCSIDriver_To_policy_AllowedCSIDriver(in, out, s)
}
func autoConvert_policy_AllowedCSIDriver_To_v1beta1_AllowedCSIDriver(in *policy.AllowedCSIDriver, out *v1beta1.AllowedCSIDriver, s conversion.Scope) error {
out.Name = in.Name
return nil
}
// Convert_policy_AllowedCSIDriver_To_v1beta1_AllowedCSIDriver is an autogenerated conversion function.
func Convert_policy_AllowedCSIDriver_To_v1beta1_AllowedCSIDriver(in *policy.AllowedCSIDriver, out *v1beta1.AllowedCSIDriver, s conversion.Scope) error {
return autoConvert_policy_AllowedCSIDriver_To_v1beta1_AllowedCSIDriver(in, out, s)
}
func autoConvert_v1beta1_AllowedFlexVolume_To_policy_AllowedFlexVolume(in *v1beta1.AllowedFlexVolume, out *policy.AllowedFlexVolume, s conversion.Scope) error {
out.Driver = in.Driver
return nil
......@@ -549,6 +579,7 @@ func autoConvert_v1beta1_PodSecurityPolicySpec_To_policy_PodSecurityPolicySpec(i
}
out.AllowedHostPaths = *(*[]policy.AllowedHostPath)(unsafe.Pointer(&in.AllowedHostPaths))
out.AllowedFlexVolumes = *(*[]policy.AllowedFlexVolume)(unsafe.Pointer(&in.AllowedFlexVolumes))
out.AllowedCSIDrivers = *(*[]policy.AllowedCSIDriver)(unsafe.Pointer(&in.AllowedCSIDrivers))
out.AllowedUnsafeSysctls = *(*[]string)(unsafe.Pointer(&in.AllowedUnsafeSysctls))
out.ForbiddenSysctls = *(*[]string)(unsafe.Pointer(&in.ForbiddenSysctls))
out.AllowedProcMountTypes = *(*[]core.ProcMountType)(unsafe.Pointer(&in.AllowedProcMountTypes))
......@@ -590,6 +621,7 @@ func autoConvert_policy_PodSecurityPolicySpec_To_v1beta1_PodSecurityPolicySpec(i
}
out.AllowedHostPaths = *(*[]v1beta1.AllowedHostPath)(unsafe.Pointer(&in.AllowedHostPaths))
out.AllowedFlexVolumes = *(*[]v1beta1.AllowedFlexVolume)(unsafe.Pointer(&in.AllowedFlexVolumes))
out.AllowedCSIDrivers = *(*[]v1beta1.AllowedCSIDriver)(unsafe.Pointer(&in.AllowedCSIDrivers))
out.AllowedUnsafeSysctls = *(*[]string)(unsafe.Pointer(&in.AllowedUnsafeSysctls))
out.ForbiddenSysctls = *(*[]string)(unsafe.Pointer(&in.ForbiddenSysctls))
out.AllowedProcMountTypes = *(*[]corev1.ProcMountType)(unsafe.Pointer(&in.AllowedProcMountTypes))
......
......@@ -121,6 +121,7 @@ func ValidatePodSecurityPolicySpec(spec *policy.PodSecurityPolicySpec, fldPath *
allErrs = append(allErrs, validatePSPAllowedProcMountTypes(fldPath.Child("allowedProcMountTypes"), spec.AllowedProcMountTypes)...)
allErrs = append(allErrs, validatePSPAllowedHostPaths(fldPath.Child("allowedHostPaths"), spec.AllowedHostPaths)...)
allErrs = append(allErrs, validatePSPAllowedFlexVolumes(fldPath.Child("allowedFlexVolumes"), spec.AllowedFlexVolumes)...)
allErrs = append(allErrs, validatePSPAllowedCSIDrivers(fldPath.Child("allowedCSIDrivers"), spec.AllowedCSIDrivers)...)
allErrs = append(allErrs, validatePodSecurityPolicySysctls(fldPath.Child("allowedUnsafeSysctls"), spec.AllowedUnsafeSysctls)...)
allErrs = append(allErrs, validatePodSecurityPolicySysctls(fldPath.Child("forbiddenSysctls"), spec.ForbiddenSysctls)...)
allErrs = append(allErrs, validatePodSecurityPolicySysctlListsDoNotOverlap(fldPath.Child("allowedUnsafeSysctls"), fldPath.Child("forbiddenSysctls"), spec.AllowedUnsafeSysctls, spec.ForbiddenSysctls)...)
......@@ -194,6 +195,17 @@ func validatePSPAllowedFlexVolumes(fldPath *field.Path, flexVolumes []policy.All
return allErrs
}
func validatePSPAllowedCSIDrivers(fldPath *field.Path, csiDrivers []policy.AllowedCSIDriver) field.ErrorList {
allErrs := field.ErrorList{}
if len(csiDrivers) > 0 {
for idx, csiDriver := range csiDrivers {
fieldPath := fldPath.Child("allowedCSIDriver").Index(idx).Child("name")
allErrs = append(allErrs, apivalidation.ValidateCSIDriverName(csiDriver.Name, fieldPath)...)
}
}
return allErrs
}
// validatePSPSELinux validates the SELinux fields of PodSecurityPolicy.
func validatePSPSELinux(fldPath *field.Path, seLinux *policy.SELinuxStrategyOptions) field.ErrorList {
allErrs := field.ErrorList{}
......
......@@ -28,6 +28,22 @@ import (
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllowedCSIDriver) DeepCopyInto(out *AllowedCSIDriver) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AllowedCSIDriver.
func (in *AllowedCSIDriver) DeepCopy() *AllowedCSIDriver {
if in == nil {
return nil
}
out := new(AllowedCSIDriver)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllowedFlexVolume) DeepCopyInto(out *AllowedFlexVolume) {
*out = *in
return
......@@ -370,6 +386,11 @@ func (in *PodSecurityPolicySpec) DeepCopyInto(out *PodSecurityPolicySpec) {
*out = make([]AllowedFlexVolume, len(*in))
copy(*out, *in)
}
if in.AllowedCSIDrivers != nil {
in, out := &in.AllowedCSIDrivers, &out.AllowedCSIDrivers
*out = make([]AllowedCSIDriver, len(*in))
copy(*out, *in)
}
if in.AllowedUnsafeSysctls != nil {
in, out := &in.AllowedUnsafeSysctls, &out.AllowedUnsafeSysctls
*out = make([]string, len(*in))
......
......@@ -348,6 +348,12 @@ const (
// Enables CSI to use raw block storage volumes
CSIBlockVolume utilfeature.Feature = "CSIBlockVolume"
// owner: @vladimirvivien
// alpha: v1.14
//
// Enables CSI Inline volumes support for pods
CSIInlineVolume utilfeature.Feature = "CSIInlineVolume"
// owner: @tallclair
// alpha: v1.12
//
......@@ -491,6 +497,7 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
KubeletPluginsWatcher: {Default: true, PreRelease: utilfeature.GA, LockToDefault: true}, // remove in 1.16
ResourceQuotaScopeSelectors: {Default: true, PreRelease: utilfeature.Beta},
CSIBlockVolume: {Default: true, PreRelease: utilfeature.Beta},
CSIInlineVolume: {Default: false, PreRelease: utilfeature.Alpha},
RuntimeClass: {Default: false, PreRelease: utilfeature.Alpha},
NodeLease: {Default: true, PreRelease: utilfeature.Beta},
SCTPSupport: {Default: false, PreRelease: utilfeature.Alpha},
......
......@@ -129,6 +129,8 @@ func GetVolumeFSType(v api.Volume) (policy.FSType, error) {
return policy.PortworxVolume, nil
case v.ScaleIO != nil:
return policy.ScaleIO, nil
case v.CSI != nil:
return policy.CSI, nil
}
return "", fmt.Errorf("unknown volume type for volume: %#v", v)
......
......@@ -63,15 +63,15 @@ func (c *csiAttacher) Attach(spec *volume.Spec, nodeName types.NodeName) (string
return "", errors.New("missing spec")
}
csiSource, err := getCSISourceFromSpec(spec)
pvSrc, err := getPVSourceFromSpec(spec)
if err != nil {
klog.Error(log("attacher.Attach failed to get CSI persistent source: %v", err))
klog.Error(log("attacher.Attach failed to get CSIPersistentVolumeSource: %v", err))
return "", err
}
node := string(nodeName)
pvName := spec.PersistentVolume.GetName()
attachID := getAttachmentName(csiSource.VolumeHandle, csiSource.Driver, node)
attachID := getAttachmentName(pvSrc.VolumeHandle, pvSrc.Driver, node)
attachment := &storage.VolumeAttachment{
ObjectMeta: meta.ObjectMeta{
......@@ -79,7 +79,7 @@ func (c *csiAttacher) Attach(spec *volume.Spec, nodeName types.NodeName) (string
},
Spec: storage.VolumeAttachmentSpec{
NodeName: node,
Attacher: csiSource.Driver,
Attacher: pvSrc.Driver,
Source: storage.VolumeAttachmentSource{
PersistentVolumeName: &pvName,
},
......@@ -97,12 +97,12 @@ func (c *csiAttacher) Attach(spec *volume.Spec, nodeName types.NodeName) (string
}
if alreadyExist {
klog.V(4).Info(log("attachment [%v] for volume [%v] already exists (will not be recreated)", attachID, csiSource.VolumeHandle))
klog.V(4).Info(log("attachment [%v] for volume [%v] already exists (will not be recreated)", attachID, pvSrc.VolumeHandle))
} else {
klog.V(4).Info(log("attachment [%v] for volume [%v] created successfully", attachID, csiSource.VolumeHandle))
klog.V(4).Info(log("attachment [%v] for volume [%v] created successfully", attachID, pvSrc.VolumeHandle))
}
if _, err := c.waitForVolumeAttachment(csiSource.VolumeHandle, attachID, csiTimeout); err != nil {
if _, err := c.waitForVolumeAttachment(pvSrc.VolumeHandle, attachID, csiTimeout); err != nil {
return "", err
}
......@@ -113,7 +113,7 @@ func (c *csiAttacher) Attach(spec *volume.Spec, nodeName types.NodeName) (string
}
func (c *csiAttacher) WaitForAttach(spec *volume.Spec, _ string, pod *v1.Pod, timeout time.Duration) (string, error) {
source, err := getCSISourceFromSpec(spec)
source, err := getPVSourceFromSpec(spec)
if err != nil {
klog.Error(log("attacher.WaitForAttach failed to extract CSI volume source: %v", err))
return "", err
......@@ -220,14 +220,18 @@ func (c *csiAttacher) VolumesAreAttached(specs []*volume.Spec, nodeName types.No
klog.Error(log("attacher.VolumesAreAttached missing volume.Spec"))
return nil, errors.New("missing spec")
}
source, err := getCSISourceFromSpec(spec)
pvSrc, err := getPVSourceFromSpec(spec)
if err != nil {
klog.Error(log("attacher.VolumesAreAttached failed: %v", err))
attached[spec] = false
klog.Error(log("attacher.VolumesAreAttached failed to get CSIPersistentVolumeSource: %v", err))
continue
}
skip, err := c.plugin.skipAttach(source.Driver)
driverName := pvSrc.Driver
volumeHandle := pvSrc.VolumeHandle
skip, err := c.plugin.skipAttach(driverName)
if err != nil {
klog.Error(log("Failed to check CSIDriver for %s: %s", source.Driver, err))
klog.Error(log("Failed to check CSIDriver for %s: %s", driverName, err))
} else {
if skip {
// This volume is not attachable, pretend it's attached
......@@ -236,7 +240,7 @@ func (c *csiAttacher) VolumesAreAttached(specs []*volume.Spec, nodeName types.No
}
}
attachID := getAttachmentName(source.VolumeHandle, source.Driver, string(nodeName))
attachID := getAttachmentName(volumeHandle, driverName, string(nodeName))
klog.V(4).Info(log("probing attachment status for VolumeAttachment %v", attachID))
attach, err := c.k8s.StorageV1().VolumeAttachments().Get(attachID, meta.GetOptions{})
if err != nil {
......@@ -285,9 +289,9 @@ func (c *csiAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMo
if spec == nil {
return fmt.Errorf("attacher.MountDevice failed, spec is nil")
}
csiSource, err := getCSISourceFromSpec(spec)
csiSource, err := getPVSourceFromSpec(spec)
if err != nil {
klog.Error(log("attacher.MountDevice failed to get CSI persistent source: %v", err))
klog.Error(log("attacher.MountDevice failed to get CSIPersistentVolumeSource: %v", err))
return err
}
......
......@@ -76,12 +76,13 @@ func (c *fakeCsiDriverClient) NodePublishVolume(
) error {
c.t.Log("calling fake.NodePublishVolume...")
req := &csipbv1.NodePublishVolumeRequest{
VolumeId: volID,
TargetPath: targetPath,
Readonly: readOnly,
PublishContext: publishContext,
VolumeContext: volumeContext,
Secrets: secrets,
VolumeId: volID,
TargetPath: targetPath,
StagingTargetPath: stagingTargetPath,
Readonly: readOnly,
PublishContext: publishContext,
VolumeContext: volumeContext,
Secrets: secrets,
VolumeCapability: &csipbv1.VolumeCapability{
AccessMode: &csipbv1.VolumeCapability_AccessMode{
Mode: asCSIAccessModeV1(accessMode),
......
......@@ -18,6 +18,7 @@ package csi
import (
"context"
"crypto/sha256"
"errors"
"fmt"
"os"
......@@ -42,13 +43,15 @@ var (
volHandle,
driverName,
nodeName,
attachmentID string
attachmentID,
driverMode string
}{
"specVolID",
"volumeHandle",
"driverName",
"nodeName",
"attachmentID",
"driverMode",
}
)
......@@ -57,6 +60,7 @@ type csiMountMgr struct {
k8s kubernetes.Interface
plugin *csiPlugin
driverName csiDriverName
driverMode driverMode
volumeID string
specVolumeID string
readOnly bool
......@@ -107,49 +111,97 @@ func (c *csiMountMgr) SetUpAt(dir string, fsGroup *int64) error {
return nil
}
csiSource, err := getCSISourceFromSpec(c.spec)
if err != nil {
klog.Error(log("mounter.SetupAt failed to get CSI persistent source: %v", err))
return err
}
csi := c.csiClient
ctx, cancel := context.WithTimeout(context.Background(), csiTimeout)
defer cancel()
// Check for STAGE_UNSTAGE_VOLUME set and populate deviceMountPath if so
deviceMountPath := ""
stageUnstageSet, err := csi.NodeSupportsStageUnstage(ctx)
volSrc, pvSrc, err := getSourceFromSpec(c.spec)
if err != nil {
klog.Error(log("mounter.SetUpAt failed to check for STAGE_UNSTAGE_VOLUME capabilty: %v", err))
klog.Error(log("mounter.SetupAt failed to get CSI persistent source: %v", err))
return err
}
if stageUnstageSet {
deviceMountPath, err = makeDeviceMountPath(c.plugin, c.spec)
if err != nil {
klog.Error(log("mounter.SetUpAt failed to make device mount path: %v", err))
return err
driverName := c.driverName
volumeHandle := c.volumeID
readOnly := c.readOnly
accessMode := api.ReadWriteOnce
var (
fsType string
volAttribs map[string]string
nodePublishSecrets map[string]string
publishContext map[string]string
mountOptions []string
deviceMountPath string
secretRef *api.SecretReference
)
switch {
case volSrc != nil:
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
return fmt.Errorf("CSIInlineVolume feature required")
}
}
// search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName
if c.publishContext == nil {
nodeName := string(c.plugin.host.GetNodeName())
c.publishContext, err = c.plugin.getPublishContext(c.k8s, c.volumeID, string(c.driverName), nodeName)
if c.driverMode != ephemeralDriverMode {
return fmt.Errorf("unexpected driver mode: %s", c.driverMode)
}
if volSrc.FSType != nil {
fsType = *volSrc.FSType
}
volAttribs = volSrc.VolumeAttributes
if volSrc.NodePublishSecretRef != nil {
secretName := volSrc.NodePublishSecretRef.Name
ns := c.pod.Namespace
secretRef = &api.SecretReference{Name: secretName, Namespace: ns}
}
case pvSrc != nil:
if c.driverMode != persistentDriverMode {
return fmt.Errorf("unexpected driver mode: %s", c.driverMode)
}
fsType = pvSrc.FSType
volAttribs = pvSrc.VolumeAttributes
if pvSrc.NodePublishSecretRef != nil {
secretRef = pvSrc.NodePublishSecretRef
}
//TODO (vladimirvivien) implement better AccessModes mapping between k8s and CSI
if c.spec.PersistentVolume.Spec.AccessModes != nil {
accessMode = c.spec.PersistentVolume.Spec.AccessModes[0]
}
mountOptions = c.spec.PersistentVolume.Spec.MountOptions
// Check for STAGE_UNSTAGE_VOLUME set and populate deviceMountPath if so
stageUnstageSet, err := csi.NodeSupportsStageUnstage(ctx)
if err != nil {
klog.Error(log("mounter.SetUpAt failed to check for STAGE_UNSTAGE_VOLUME capabilty: %v", err))
return err
}
}
attribs := csiSource.VolumeAttributes
if stageUnstageSet {
deviceMountPath, err = makeDeviceMountPath(c.plugin, c.spec)
if err != nil {
klog.Error(log("mounter.SetUpAt failed to make device mount path: %v", err))
return err
}
}
nodePublishSecrets := map[string]string{}
if csiSource.NodePublishSecretRef != nil {
nodePublishSecrets, err = getCredentialsFromSecret(c.k8s, csiSource.NodePublishSecretRef)
if err != nil {
return fmt.Errorf("fetching NodePublishSecretRef %s/%s failed: %v",
csiSource.NodePublishSecretRef.Namespace, csiSource.NodePublishSecretRef.Name, err)
// search for attachment by VolumeAttachment.Spec.Source.PersistentVolumeName
if c.publishContext == nil {
nodeName := string(c.plugin.host.GetNodeName())
c.publishContext, err = c.plugin.getPublishContext(c.k8s, volumeHandle, string(driverName), nodeName)
if err != nil {
return err
}
publishContext = c.publishContext
}
default:
return fmt.Errorf("volume source not found in volume.Spec")
}
// create target_dir before call to NodePublish
......@@ -159,10 +211,14 @@ func (c *csiMountMgr) SetUpAt(dir string, fsGroup *int64) error {
}
klog.V(4).Info(log("created target path successfully [%s]", dir))
//TODO (vladimirvivien) implement better AccessModes mapping between k8s and CSI
accessMode := api.ReadWriteOnce
if c.spec.PersistentVolume.Spec.AccessModes != nil {
accessMode = c.spec.PersistentVolume.Spec.AccessModes[0]
nodePublishSecrets = map[string]string{}
if secretRef != nil {
nodePublishSecrets, err = getCredentialsFromSecret(c.k8s, secretRef)
if err != nil {
return fmt.Errorf("fetching NodePublishSecretRef %s/%s failed: %v",
secretRef.Namespace, secretRef.Name, err)
}
}
// Inject pod information into volume_attributes
......@@ -172,28 +228,27 @@ func (c *csiMountMgr) SetUpAt(dir string, fsGroup *int64) error {
return err
}
if podAttrs != nil {
if attribs == nil {
attribs = podAttrs
if volAttribs == nil {
volAttribs = podAttrs
} else {
for k, v := range podAttrs {
attribs[k] = v
volAttribs[k] = v
}
}
}
fsType := csiSource.FSType
err = csi.NodePublishVolume(
ctx,
c.volumeID,
c.readOnly,
volumeHandle,
readOnly,
deviceMountPath,
dir,
accessMode,
c.publishContext,
attribs,
publishContext,
volAttribs,
nodePublishSecrets,
fsType,
c.spec.PersistentVolume.Spec.MountOptions,
mountOptions,
)
if err != nil {
......@@ -387,3 +442,9 @@ func removeMountDir(plug *csiPlugin, mountPath string) error {
}
return nil
}
// makeVolumeHandle returns csi-<sha256(podUID,volSourceSpecName)>
func makeVolumeHandle(podUID, volSourceSpecName string) string {
result := sha256.Sum256([]byte(fmt.Sprintf("%s%s", podUID, volSourceSpecName)))
return fmt.Sprintf("csi-%x", result)
}
......@@ -21,6 +21,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"os"
"path"
"testing"
......@@ -266,6 +267,266 @@ func TestMounterSetUp(t *testing.T) {
MounterSetUpTests(t, false)
})
}
func TestMounterSetUpSimple(t *testing.T) {
fakeClient := fakeclient.NewSimpleClientset()
plug, tmpDir := newTestPlugin(t, fakeClient)
defer os.RemoveAll(tmpDir)
testCases := []struct {
name string
podUID types.UID
mode driverMode
fsType string
options []string
spec func(string, []string) *volume.Spec
shouldFail bool
}{
{
name: "setup with vol source",
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
mode: ephemeralDriverMode,
fsType: "ext4",
shouldFail: true,
spec: func(fsType string, options []string) *volume.Spec {
volSrc := makeTestVol("pv1", testDriver)
volSrc.CSI.FSType = &fsType
return volume.NewSpecFromVolume(volSrc)
},
},
{
name: "setup with persistent source",
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
mode: persistentDriverMode,
fsType: "zfs",
spec: func(fsType string, options []string) *volume.Spec {
pvSrc := makeTestPV("pv1", 20, testDriver, "vol1")
pvSrc.Spec.CSI.FSType = fsType
pvSrc.Spec.MountOptions = options
return volume.NewSpecFromPersistentVolume(pvSrc, false)
},
},
{
name: "setup with persistent source without unspecified fstype and options",
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
mode: persistentDriverMode,
spec: func(fsType string, options []string) *volume.Spec {
return volume.NewSpecFromPersistentVolume(makeTestPV("pv1", 20, testDriver, "vol2"), false)
},
},
{
name: "setup with missing spec",
shouldFail: true,
spec: func(fsType string, options []string) *volume.Spec { return nil },
},
}
for _, tc := range testCases {
registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t)
t.Run(tc.name, func(t *testing.T) {
mounter, err := plug.NewMounter(
tc.spec(tc.fsType, tc.options),
&api.Pod{ObjectMeta: meta.ObjectMeta{UID: tc.podUID, Namespace: testns}},
volume.VolumeOptions{},
)
if tc.shouldFail && err != nil {
t.Log(err)
return
}
if !tc.shouldFail && err != nil {
t.Fatal("unexpected error:", err)
}
if mounter == nil {
t.Fatal("failed to create CSI mounter")
}
csiMounter := mounter.(*csiMountMgr)
csiMounter.csiClient = setupClient(t, true)
if csiMounter.driverMode != persistentDriverMode {
t.Fatal("unexpected driver mode: ", csiMounter.driverMode)
}
attachID := getAttachmentName(csiMounter.volumeID, string(csiMounter.driverName), string(plug.host.GetNodeName()))
attachment := makeTestAttachment(attachID, "test-node", csiMounter.spec.Name())
_, err = csiMounter.k8s.StorageV1().VolumeAttachments().Create(attachment)
if err != nil {
t.Fatalf("failed to setup VolumeAttachment: %v", err)
}
// Mounter.SetUp()
if err := csiMounter.SetUp(nil); err != nil {
t.Fatalf("mounter.Setup failed: %v", err)
}
// ensure call went all the way
pubs := csiMounter.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodePublishedVolumes()
vol, ok := pubs[csiMounter.volumeID]
if !ok {
t.Error("csi server may not have received NodePublishVolume call")
}
if vol.VolumeHandle != csiMounter.volumeID {
t.Error("volumeHandle not sent to CSI driver properly")
}
devicePath, err := makeDeviceMountPath(plug, csiMounter.spec)
if err != nil {
t.Fatal(err)
}
if vol.DeviceMountPath != devicePath {
t.Errorf("DeviceMountPath not sent properly to CSI driver: %s, %s", vol.DeviceMountPath, devicePath)
}
if !reflect.DeepEqual(vol.MountFlags, csiMounter.spec.PersistentVolume.Spec.MountOptions) {
t.Errorf("unexpected mount flags passed to driver: %+v", vol.MountFlags)
}
if vol.FSType != tc.fsType {
t.Error("unexpected FSType sent to driver:", vol.FSType)
}
if vol.Path != csiMounter.GetPath() {
t.Error("csi server may not have received NodePublishVolume call")
}
})
}
}
func TestMounterSetUpWithInline(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CSIInlineVolume, true)()
fakeClient := fakeclient.NewSimpleClientset()
plug, tmpDir := newTestPlugin(t, fakeClient)
defer os.RemoveAll(tmpDir)
testCases := []struct {
name string
podUID types.UID
mode driverMode
fsType string
options []string
spec func(string, []string) *volume.Spec
shouldFail bool
}{
{
name: "setup with vol source",
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
mode: ephemeralDriverMode,
fsType: "ext4",
spec: func(fsType string, options []string) *volume.Spec {
volSrc := makeTestVol("pv1", testDriver)
volSrc.CSI.FSType = &fsType
return volume.NewSpecFromVolume(volSrc)
},
},
{
name: "setup with persistent source",
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
mode: persistentDriverMode,
fsType: "zfs",
spec: func(fsType string, options []string) *volume.Spec {
pvSrc := makeTestPV("pv1", 20, testDriver, "vol1")
pvSrc.Spec.CSI.FSType = fsType
pvSrc.Spec.MountOptions = options
return volume.NewSpecFromPersistentVolume(pvSrc, false)
},
},
{
name: "setup with persistent source without unspecified fstype and options",
podUID: types.UID(fmt.Sprintf("%08X", rand.Uint64())),
mode: persistentDriverMode,
spec: func(fsType string, options []string) *volume.Spec {
return volume.NewSpecFromPersistentVolume(makeTestPV("pv1", 20, testDriver, "vol2"), false)
},
},
{
name: "setup with missing spec",
shouldFail: true,
spec: func(fsType string, options []string) *volume.Spec { return nil },
},
}
for _, tc := range testCases {
registerFakePlugin(testDriver, "endpoint", []string{"1.0.0"}, t)
t.Run(tc.name, func(t *testing.T) {
mounter, err := plug.NewMounter(
tc.spec(tc.fsType, tc.options),
&api.Pod{ObjectMeta: meta.ObjectMeta{UID: tc.podUID, Namespace: testns}},
volume.VolumeOptions{},
)
if tc.shouldFail && err != nil {
t.Log(err)
return
}
if !tc.shouldFail && err != nil {
t.Fatal("unexpected error:", err)
}
if mounter == nil {
t.Fatal("failed to create CSI mounter")
}
csiMounter := mounter.(*csiMountMgr)
csiMounter.csiClient = setupClient(t, true)
if csiMounter.driverMode != tc.mode {
t.Fatal("unexpected driver mode: ", csiMounter.driverMode)
}
if csiMounter.driverMode == ephemeralDriverMode && csiMounter.volumeID != makeVolumeHandle(string(tc.podUID), csiMounter.specVolumeID) {
t.Fatal("unexpected generated volumeHandle:", csiMounter.volumeID)
}
if csiMounter.driverMode == persistentDriverMode {
attachID := getAttachmentName(csiMounter.volumeID, string(csiMounter.driverName), string(plug.host.GetNodeName()))
attachment := makeTestAttachment(attachID, "test-node", csiMounter.spec.Name())
_, err = csiMounter.k8s.StorageV1().VolumeAttachments().Create(attachment)
if err != nil {
t.Fatalf("failed to setup VolumeAttachment: %v", err)
}
}
// Mounter.SetUp()
if err := csiMounter.SetUp(nil); err != nil {
t.Fatalf("mounter.Setup failed: %v", err)
}
// ensure call went all the way
pubs := csiMounter.csiClient.(*fakeCsiDriverClient).nodeClient.GetNodePublishedVolumes()
vol, ok := pubs[csiMounter.volumeID]
if !ok {
t.Error("csi server may not have received NodePublishVolume call")
}
if vol.VolumeHandle != csiMounter.volumeID {
t.Error("volumeHandle not sent to CSI driver properly")
}
// validate stagingTargetPath
if tc.mode == ephemeralDriverMode && vol.DeviceMountPath != "" {
t.Errorf("unexpected devicePathTarget sent to driver: %s", vol.DeviceMountPath)
}
if tc.mode == persistentDriverMode {
devicePath, err := makeDeviceMountPath(plug, csiMounter.spec)
if err != nil {
t.Fatal(err)
}
if vol.DeviceMountPath != devicePath {
t.Errorf("DeviceMountPath not sent properly to CSI driver: %s, %s", vol.DeviceMountPath, devicePath)
}
if !reflect.DeepEqual(vol.MountFlags, csiMounter.spec.PersistentVolume.Spec.MountOptions) {
t.Errorf("unexpected mount flags passed to driver: %+v", vol.MountFlags)
}
}
if vol.FSType != tc.fsType {
t.Error("unexpected FSType sent to driver:", vol.FSType)
}
if vol.Path != csiMounter.GetPath() {
t.Error("csi server may not have received NodePublishVolume call")
}
})
}
}
func TestMounterSetUpWithFSGroup(t *testing.T) {
fakeClient := fakeclient.NewSimpleClientset()
plug, tmpDir := newTestPlugin(t, fakeClient)
......
......@@ -74,6 +74,12 @@ type csiPlugin struct {
csiDriverInformer csiinformer.CSIDriverInformer
}
//TODO (vladimirvivien) add this type to storage api
type driverMode string
const persistentDriverMode driverMode = "persistent"
const ephemeralDriverMode driverMode = "ephemeral"
// ProbeVolumePlugins returns implemented plugins
func ProbeVolumePlugins() []volume.VolumePlugin {
p := &csiPlugin{
......@@ -303,7 +309,7 @@ func (p *csiPlugin) GetPluginName() string {
// GetvolumeName returns a concatenated string of CSIVolumeSource.Driver<volNameSe>CSIVolumeSource.VolumeHandle
// That string value is used in Detach() to extract driver name and volumeName.
func (p *csiPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
csi, err := getCSISourceFromSpec(spec)
csi, err := getPVSourceFromSpec(spec)
if err != nil {
klog.Error(log("plugin.GetVolumeName failed to extract volume source from spec: %v", err))
return "", err
......@@ -316,6 +322,14 @@ func (p *csiPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
func (p *csiPlugin) CanSupport(spec *volume.Spec) bool {
// TODO (vladimirvivien) CanSupport should also take into account
// the availability/registration of specified Driver in the volume source
if spec == nil {
return false
}
if utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.CSI != nil) ||
(spec.Volume != nil && spec.Volume.CSI != nil)
}
return spec.PersistentVolume != nil && spec.PersistentVolume.Spec.CSI != nil
}
......@@ -331,11 +345,34 @@ func (p *csiPlugin) NewMounter(
spec *volume.Spec,
pod *api.Pod,
_ volume.VolumeOptions) (volume.Mounter, error) {
pvSource, err := getCSISourceFromSpec(spec)
volSrc, pvSrc, err := getSourceFromSpec(spec)
if err != nil {
return nil, err
}
readOnly, err := getReadOnlyFromSpec(spec)
var (
driverName string
volumeHandle string
readOnly bool
)
switch {
case volSrc != nil && utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume):
volumeHandle = makeVolumeHandle(string(pod.UID), spec.Name())
driverName = volSrc.Driver
if volSrc.ReadOnly != nil {
readOnly = *volSrc.ReadOnly
}
case pvSrc != nil:
driverName = pvSrc.Driver
volumeHandle = pvSrc.VolumeHandle
readOnly = spec.ReadOnly
default:
return nil, fmt.Errorf("volume source not found in volume.Spec")
}
driverMode, err := p.getDriverMode(spec)
if err != nil {
return nil, err
}
......@@ -346,7 +383,7 @@ func (p *csiPlugin) NewMounter(
return nil, errors.New("failed to get a Kubernetes client")
}
csi, err := newCsiDriverClient(csiDriverName(pvSource.Driver))
csi, err := newCsiDriverClient(csiDriverName(driverName))
if err != nil {
return nil, err
}
......@@ -357,8 +394,9 @@ func (p *csiPlugin) NewMounter(
spec: spec,
pod: pod,
podUID: pod.UID,
driverName: csiDriverName(pvSource.Driver),
volumeID: pvSource.VolumeHandle,
driverName: csiDriverName(driverName),
driverMode: driverMode,
volumeID: volumeHandle,
specVolumeID: spec.Name(),
csiClient: csi,
readOnly: readOnly,
......@@ -376,15 +414,17 @@ func (p *csiPlugin) NewMounter(
// persist volume info data for teardown
node := string(p.host.GetNodeName())
attachID := getAttachmentName(pvSource.VolumeHandle, pvSource.Driver, node)
volData := map[string]string{
volDataKey.specVolID: spec.Name(),
volDataKey.volHandle: pvSource.VolumeHandle,
volDataKey.driverName: pvSource.Driver,
volDataKey.nodeName: node,
volDataKey.attachmentID: attachID,
volDataKey.specVolID: spec.Name(),
volDataKey.volHandle: volumeHandle,
volDataKey.driverName: driverName,
volDataKey.nodeName: node,
volDataKey.driverMode: string(driverMode),
}
attachID := getAttachmentName(volumeHandle, driverName, node)
volData[volDataKey.attachmentID] = attachID
if err := saveVolumeData(dataDir, volDataFileName, volData); err != nil {
klog.Error(log("failed to save volume info data: %v", err))
if err := os.RemoveAll(dataDir); err != nil {
......@@ -437,23 +477,58 @@ func (p *csiPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.S
klog.V(4).Info(log("plugin.ConstructVolumeSpec extracted [%#v]", volData))
var spec *volume.Spec
inlineEnabled := utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume)
if inlineEnabled {
mode := driverMode(volData[volDataKey.driverMode])
switch {
case mode == ephemeralDriverMode:
spec = p.constructVolSourceSpec(volData[volDataKey.specVolID], volData[volDataKey.driverName])
case mode == persistentDriverMode:
fallthrough
default:
spec = p.constructPVSourceSpec(volData[volDataKey.specVolID], volData[volDataKey.driverName], volData[volDataKey.volHandle])
}
} else {
spec = p.constructPVSourceSpec(volData[volDataKey.specVolID], volData[volDataKey.driverName], volData[volDataKey.volHandle])
}
return spec, nil
}
// constructVolSourceSpec constructs volume.Spec with CSIVolumeSource
func (p *csiPlugin) constructVolSourceSpec(volSpecName, driverName string) *volume.Spec {
vol := &api.Volume{
Name: volSpecName,
VolumeSource: api.VolumeSource{
CSI: &api.CSIVolumeSource{
Driver: driverName,
},
},
}
return volume.NewSpecFromVolume(vol)
}
//constructPVSourceSpec constructs volume.Spec with CSIPersistentVolumeSource
func (p *csiPlugin) constructPVSourceSpec(volSpecName, driverName, volumeHandle string) *volume.Spec {
fsMode := api.PersistentVolumeFilesystem
pv := &api.PersistentVolume{
ObjectMeta: meta.ObjectMeta{
Name: volData[volDataKey.specVolID],
Name: volSpecName,
},
Spec: api.PersistentVolumeSpec{
PersistentVolumeSource: api.PersistentVolumeSource{
CSI: &api.CSIPersistentVolumeSource{
Driver: volData[volDataKey.driverName],
VolumeHandle: volData[volDataKey.volHandle],
Driver: driverName,
VolumeHandle: volumeHandle,
},
},
VolumeMode: &fsMode,
},
}
return volume.NewSpecFromPersistentVolume(pv, false), nil
return volume.NewSpecFromPersistentVolume(pv, false)
}
func (p *csiPlugin) SupportsMountOption() bool {
......@@ -506,24 +581,31 @@ func (p *csiPlugin) NewDetacher() (volume.Detacher, error) {
}, nil
}
// TODO change CanAttach to return error to propagate ability
// to support Attachment or an error - see https://github.com/kubernetes/kubernetes/issues/74810
func (p *csiPlugin) CanAttach(spec *volume.Spec) bool {
if spec.PersistentVolume == nil {
klog.Error(log("plugin.CanAttach test failed, spec missing PersistentVolume"))
driverMode, err := p.getDriverMode(spec)
if err != nil {
return false
}
var driverName string
if spec.PersistentVolume.Spec.CSI != nil {
driverName = spec.PersistentVolume.Spec.CSI.Driver
} else {
klog.Error(log("plugin.CanAttach test failed, spec missing CSIPersistentVolume"))
if driverMode == ephemeralDriverMode {
klog.V(4).Info(log("driver ephemeral mode detected for spec %v", spec.Name))
return false
}
skipAttach, err := p.skipAttach(driverName)
pvSrc, err := getCSISourceFromSpec(spec)
if err != nil {
klog.Error(log("plugin.CanAttach failed to get info from spec: %s", err))
return false
}
driverName := pvSrc.Driver
skipAttach, err := p.skipAttach(driverName)
if err != nil {
klog.Error(log("plugin.CanAttach error when calling plugin.skipAttach for driver %s: %s", driverName, err))
return false
}
return !skipAttach
......@@ -675,6 +757,8 @@ func (p *csiPlugin) ConstructBlockVolumeSpec(podUID types.UID, specVolName, mapP
return volume.NewSpecFromPersistentVolume(pv, false), nil
}
// skipAttach looks up CSIDriver object associated with driver name
// to determine if driver requies attachment volume operation
func (p *csiPlugin) skipAttach(driver string) (bool, error) {
if !utilfeature.DefaultFeatureGate.Enabled(features.CSIDriverRegistry) {
return false, nil
......@@ -696,6 +780,26 @@ func (p *csiPlugin) skipAttach(driver string) (bool, error) {
return false, nil
}
// getDriverMode returns the driver mode for the specified spec: {persistent|ephemeral}.
// 1) If mode cannot be determined, it will default to "persistent".
// 2) If Mode cannot be resolved to either {persistent | ephemeral}, an error is returned
// See https://github.com/kubernetes/enhancements/blob/master/keps/sig-storage/20190122-csi-inline-volumes.md
func (p *csiPlugin) getDriverMode(spec *volume.Spec) (driverMode, error) {
// TODO (vladimirvivien) ultimately, mode will be retrieved from CSIDriver.Spec.Mode.
// However, in alpha version, mode is determined by the volume source:
// 1) if volume.Spec.Volume.CSI != nil -> mode is ephemeral
// 2) if volume.Spec.PersistentVolume.Spec.CSI != nil -> persistent
volSrc, _, err := getSourceFromSpec(spec)
if err != nil {
return "", err
}
if volSrc != nil && utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
return ephemeralDriverMode, nil
}
return persistentDriverMode, nil
}
func (p *csiPlugin) getPublishContext(client clientset.Interface, handle, driver, nodeName string) (map[string]string, error) {
skip, err := p.skipAttach(driver)
if err != nil {
......
......@@ -25,8 +25,10 @@ import (
api "k8s.io/api/core/v1"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/kubernetes"
"k8s.io/klog"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/volume"
utilstrings "k8s.io/utils/strings"
)
......@@ -90,12 +92,7 @@ func loadVolumeData(dir string, fileName string) (map[string]string, error) {
}
func getCSISourceFromSpec(spec *volume.Spec) (*api.CSIPersistentVolumeSource, error) {
if spec.PersistentVolume != nil &&
spec.PersistentVolume.Spec.CSI != nil {
return spec.PersistentVolume.Spec.CSI, nil
}
return nil, fmt.Errorf("CSIPersistentVolumeSource not defined in spec")
return getPVSourceFromSpec(spec)
}
func getReadOnlyFromSpec(spec *volume.Spec) (bool, error) {
......@@ -140,3 +137,34 @@ func hasReadWriteOnce(modes []api.PersistentVolumeAccessMode) bool {
}
return false
}
// getSourceFromSpec returns either CSIVolumeSource or CSIPersistentVolumeSource, but not both
func getSourceFromSpec(spec *volume.Spec) (*api.CSIVolumeSource, *api.CSIPersistentVolumeSource, error) {
if spec == nil {
return nil, nil, fmt.Errorf("volume.Spec nil")
}
if spec.Volume != nil && spec.PersistentVolume != nil {
return nil, nil, fmt.Errorf("volume.Spec has both volume and persistent volume sources")
}
if spec.Volume != nil && spec.Volume.CSI != nil && utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) {
return spec.Volume.CSI, nil, nil
}
if spec.PersistentVolume != nil &&
spec.PersistentVolume.Spec.CSI != nil {
return nil, spec.PersistentVolume.Spec.CSI, nil
}
return nil, nil, fmt.Errorf("volume source not found in volume.Spec")
}
// getPVSourceFromSpec ensures only CSIPersistentVolumeSource is present in volume.Spec
func getPVSourceFromSpec(spec *volume.Spec) (*api.CSIPersistentVolumeSource, error) {
volSrc, pvSrc, err := getSourceFromSpec(spec)
if err != nil {
return nil, err
}
if volSrc != nil {
return nil, fmt.Errorf("unexpected api.CSIVolumeSource found in volume.Spec")
}
return pvSrc, nil
}
......@@ -57,9 +57,12 @@ func (f *IdentityClient) Probe(ctx context.Context, in *csipb.ProbeRequest, opts
}
type CSIVolume struct {
VolumeContext map[string]string
Path string
MountFlags []string
VolumeHandle string
VolumeContext map[string]string
Path string
DeviceMountPath string
FSType string
MountFlags []string
}
// NodeClient returns CSI node client
......@@ -118,7 +121,6 @@ func (f *NodeClient) AddNodeStagedVolume(volID, deviceMountPath string, volumeCo
// NodePublishVolume implements CSI NodePublishVolume
func (f *NodeClient) NodePublishVolume(ctx context.Context, req *csipb.NodePublishVolumeRequest, opts ...grpc.CallOption) (*csipb.NodePublishVolumeResponse, error) {
if f.nextErr != nil {
return nil, f.nextErr
}
......@@ -135,9 +137,12 @@ func (f *NodeClient) NodePublishVolume(ctx context.Context, req *csipb.NodePubli
return nil, errors.New("invalid fstype")
}
f.nodePublishedVolumes[req.GetVolumeId()] = CSIVolume{
Path: req.GetTargetPath(),
VolumeContext: req.GetVolumeContext(),
MountFlags: req.GetVolumeCapability().GetMount().MountFlags,
VolumeHandle: req.GetVolumeId(),
Path: req.GetTargetPath(),
DeviceMountPath: req.GetStagingTargetPath(),
VolumeContext: req.GetVolumeContext(),
FSType: req.GetVolumeCapability().GetMount().GetFsType(),
MountFlags: req.GetVolumeCapability().GetMount().MountFlags,
}
return &csipb.NodePublishVolumeResponse{}, nil
}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -220,6 +220,37 @@ message CSIPersistentVolumeSource {
optional SecretReference nodePublishSecretRef = 8;
}
// Represents a source location of a volume to mount, managed by an external CSI driver
message CSIVolumeSource {
// Driver is the name of the CSI driver that handles this volume.
// Consult with your admin for the correct name as registered in the cluster.
optional string driver = 1;
// Specifies a read-only configuration for the volume.
// Defaults to false (read/write).
// +optional
optional bool readOnly = 2;
// Filesystem type to mount. Ex. "ext4", "xfs", "ntfs".
// If not provided, the empty value is passed to the associated CSI driver
// which will determine the default filesystem to apply.
// +optional
optional string fsType = 3;
// VolumeAttributes stores driver-specific properties that are passed to the CSI
// driver. Consult your driver's documentation for supported values.
// +optional
map<string, string> volumeAttributes = 4;
// NodePublishSecretRef is a reference to the secret object containing
// sensitive information to pass to the CSI driver to complete the CSI
// NodePublishVolume and NodeUnpublishVolume calls.
// This field is optional, and may be empty if no secret is required. If the
// secret object contains more than one secret, all secret references are passed.
// +optional
optional LocalObjectReference nodePublishSecretRef = 5;
}
// Adds and removes POSIX capabilities from running containers.
message Capabilities {
// Added capabilities
......@@ -2492,7 +2523,7 @@ message PersistentVolumeSource {
// +optional
optional StorageOSPersistentVolumeSource storageos = 21;
// CSI represents storage that handled by an external CSI driver (Beta feature).
// CSI represents storage that is handled by an external CSI driver (Beta feature).
// +optional
optional CSIPersistentVolumeSource csi = 22;
}
......@@ -4776,6 +4807,10 @@ message VolumeSource {
// StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.
// +optional
optional StorageOSVolumeSource storageos = 27;
// CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature).
// +optional
optional CSIVolumeSource csi = 28;
}
// Represents a vSphere volume resource.
......
......@@ -151,6 +151,9 @@ type VolumeSource struct {
// StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.
// +optional
StorageOS *StorageOSVolumeSource `json:"storageos,omitempty" protobuf:"bytes,27,opt,name=storageos"`
// CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature).
// +optional
CSI *CSIVolumeSource `json:"csi,omitempty" protobuf:"bytes,28,opt,name=csi"`
}
// PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace.
......@@ -248,7 +251,7 @@ type PersistentVolumeSource struct {
// More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md
// +optional
StorageOS *StorageOSPersistentVolumeSource `json:"storageos,omitempty" protobuf:"bytes,21,opt,name=storageos"`
// CSI represents storage that handled by an external CSI driver (Beta feature).
// CSI represents storage that is handled by an external CSI driver (Beta feature).
// +optional
CSI *CSIPersistentVolumeSource `json:"csi,omitempty" protobuf:"bytes,22,opt,name=csi"`
}
......@@ -1691,6 +1694,37 @@ type CSIPersistentVolumeSource struct {
NodePublishSecretRef *SecretReference `json:"nodePublishSecretRef,omitempty" protobuf:"bytes,8,opt,name=nodePublishSecretRef"`
}
// Represents a source location of a volume to mount, managed by an external CSI driver
type CSIVolumeSource struct {
// Driver is the name of the CSI driver that handles this volume.
// Consult with your admin for the correct name as registered in the cluster.
Driver string `json:"driver" protobuf:"bytes,1,opt,name=driver"`
// Specifies a read-only configuration for the volume.
// Defaults to false (read/write).
// +optional
ReadOnly *bool `json:"readOnly,omitempty" protobuf:"varint,2,opt,name=readOnly"`
// Filesystem type to mount. Ex. "ext4", "xfs", "ntfs".
// If not provided, the empty value is passed to the associated CSI driver
// which will determine the default filesystem to apply.
// +optional
FSType *string `json:"fsType,omitempty" protobuf:"bytes,3,opt,name=fsType"`
// VolumeAttributes stores driver-specific properties that are passed to the CSI
// driver. Consult your driver's documentation for supported values.
// +optional
VolumeAttributes map[string]string `json:"volumeAttributes,omitempty" protobuf:"bytes,4,rep,name=volumeAttributes"`
// NodePublishSecretRef is a reference to the secret object containing
// sensitive information to pass to the CSI driver to complete the CSI
// NodePublishVolume and NodeUnpublishVolume calls.
// This field is optional, and may be empty if no secret is required. If the
// secret object contains more than one secret, all secret references are passed.
// +optional
NodePublishSecretRef *LocalObjectReference `json:"nodePublishSecretRef,omitempty" protobuf:"bytes,5,opt,name=nodePublishSecretRef"`
}
// ContainerPort represents a network port in a single container.
type ContainerPort struct {
// If specified, this must be an IANA_SVC_NAME and unique within the pod. Each
......
......@@ -132,6 +132,19 @@ func (CSIPersistentVolumeSource) SwaggerDoc() map[string]string {
return map_CSIPersistentVolumeSource
}
var map_CSIVolumeSource = map[string]string{
"": "Represents a source location of a volume to mount, managed by an external CSI driver",
"driver": "Driver is the name of the CSI driver that handles this volume. Consult with your admin for the correct name as registered in the cluster.",
"readOnly": "Specifies a read-only configuration for the volume. Defaults to false (read/write).",
"fsType": "Filesystem type to mount. Ex. \"ext4\", \"xfs\", \"ntfs\". If not provided, the empty value is passed to the associated CSI driver which will determine the default filesystem to apply.",
"volumeAttributes": "VolumeAttributes stores driver-specific properties that are passed to the CSI driver. Consult your driver's documentation for supported values.",
"nodePublishSecretRef": "NodePublishSecretRef is a reference to the secret object containing sensitive information to pass to the CSI driver to complete the CSI NodePublishVolume and NodeUnpublishVolume calls. This field is optional, and may be empty if no secret is required. If the secret object contains more than one secret, all secret references are passed.",
}
func (CSIVolumeSource) SwaggerDoc() map[string]string {
return map_CSIVolumeSource
}
var map_Capabilities = map[string]string{
"": "Adds and removes POSIX capabilities from running containers.",
"add": "Added capabilities",
......@@ -1285,7 +1298,7 @@ var map_PersistentVolumeSource = map[string]string{
"scaleIO": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.",
"local": "Local represents directly-attached storage with node affinity",
"storageos": "StorageOS represents a StorageOS volume that is attached to the kubelet's host machine and mounted into the pod More info: https://releases.k8s.io/HEAD/examples/volumes/storageos/README.md",
"csi": "CSI represents storage that handled by an external CSI driver (Beta feature).",
"csi": "CSI represents storage that is handled by an external CSI driver (Beta feature).",
}
func (PersistentVolumeSource) SwaggerDoc() map[string]string {
......@@ -2317,6 +2330,7 @@ var map_VolumeSource = map[string]string{
"portworxVolume": "PortworxVolume represents a portworx volume attached and mounted on kubelets host machine",
"scaleIO": "ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.",
"storageos": "StorageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.",
"csi": "CSI (Container Storage Interface) represents storage that is handled by an external CSI driver (Alpha feature).",
}
func (VolumeSource) SwaggerDoc() map[string]string {
......
......@@ -251,6 +251,44 @@ func (in *CSIPersistentVolumeSource) DeepCopy() *CSIPersistentVolumeSource {
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CSIVolumeSource) DeepCopyInto(out *CSIVolumeSource) {
*out = *in
if in.ReadOnly != nil {
in, out := &in.ReadOnly, &out.ReadOnly
*out = new(bool)
**out = **in
}
if in.FSType != nil {
in, out := &in.FSType, &out.FSType
*out = new(string)
**out = **in
}
if in.VolumeAttributes != nil {
in, out := &in.VolumeAttributes, &out.VolumeAttributes
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.NodePublishSecretRef != nil {
in, out := &in.NodePublishSecretRef, &out.NodePublishSecretRef
*out = new(LocalObjectReference)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CSIVolumeSource.
func (in *CSIVolumeSource) DeepCopy() *CSIVolumeSource {
if in == nil {
return nil
}
out := new(CSIVolumeSource)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Capabilities) DeepCopyInto(out *Capabilities) {
*out = *in
if in.Add != nil {
......@@ -5383,6 +5421,11 @@ func (in *VolumeSource) DeepCopyInto(out *VolumeSource) {
*out = new(StorageOSVolumeSource)
(*in).DeepCopyInto(*out)
}
if in.CSI != nil {
in, out := &in.CSI, &out.CSI
*out = new(CSIVolumeSource)
(*in).DeepCopyInto(*out)
}
return
}
......
......@@ -30,6 +30,12 @@ import "k8s.io/apimachinery/pkg/util/intstr/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "v1beta1";
// AllowedCSIDriver represents a single inline CSI Driver that is allowed to be used.
message AllowedCSIDriver {
// Name is the registered name of the CSI driver
optional string name = 1;
}
// AllowedFlexVolume represents a single Flexvolume that is allowed to be used.
// Deprecated: use AllowedFlexVolume from policy API Group instead.
message AllowedFlexVolume {
......@@ -865,6 +871,11 @@ message PodSecurityPolicySpec {
// +optional
repeated AllowedFlexVolume allowedFlexVolumes = 18;
// AllowedCSIDrivers is a whitelist of inline CSI drivers that must be explicitly set to be embedded within a pod spec.
// An empty value means no CSI drivers can run inline within a pod spec.
// +optional
repeated AllowedCSIDriver allowedCSIDrivers = 23;
// allowedUnsafeSysctls is a list of explicitly allowed unsafe sysctls, defaults to none.
// Each entry is either a plain sysctl name or ends in "*" in which case it is considered
// as a prefix of allowed sysctls. Single * means all unsafe sysctls are allowed.
......
......@@ -928,6 +928,10 @@ type PodSecurityPolicySpec struct {
// is allowed in the "volumes" field.
// +optional
AllowedFlexVolumes []AllowedFlexVolume `json:"allowedFlexVolumes,omitempty" protobuf:"bytes,18,rep,name=allowedFlexVolumes"`
// AllowedCSIDrivers is a whitelist of inline CSI drivers that must be explicitly set to be embedded within a pod spec.
// An empty value means no CSI drivers can run inline within a pod spec.
// +optional
AllowedCSIDrivers []AllowedCSIDriver `json:"allowedCSIDrivers,omitempty" protobuf:"bytes,23,rep,name=allowedCSIDrivers"`
// allowedUnsafeSysctls is a list of explicitly allowed unsafe sysctls, defaults to none.
// Each entry is either a plain sysctl name or ends in "*" in which case it is considered
// as a prefix of allowed sysctls. Single * means all unsafe sysctls are allowed.
......@@ -998,6 +1002,7 @@ var (
ConfigMap FSType = "configMap"
Quobyte FSType = "quobyte"
AzureDisk FSType = "azureDisk"
CSI FSType = "csi"
All FSType = "*"
)
......@@ -1008,6 +1013,12 @@ type AllowedFlexVolume struct {
Driver string `json:"driver" protobuf:"bytes,1,opt,name=driver"`
}
// AllowedCSIDriver represents a single inline CSI Driver that is allowed to be used.
type AllowedCSIDriver struct {
// Name is the registered name of the CSI driver
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
}
// HostPortRange defines a range of host ports that will be enabled by a policy
// for pods to use. It requires both the start and end to be defined.
// Deprecated: use HostPortRange from policy API Group instead.
......
......@@ -27,6 +27,15 @@ package v1beta1
// Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT.
var map_AllowedCSIDriver = map[string]string{
"": "AllowedCSIDriver represents a single inline CSI Driver that is allowed to be used.",
"name": "Name is the registered name of the CSI driver",
}
func (AllowedCSIDriver) SwaggerDoc() map[string]string {
return map_AllowedCSIDriver
}
var map_AllowedFlexVolume = map[string]string{
"": "AllowedFlexVolume represents a single Flexvolume that is allowed to be used. Deprecated: use AllowedFlexVolume from policy API Group instead.",
"driver": "driver is the name of the Flexvolume driver.",
......@@ -461,6 +470,7 @@ var map_PodSecurityPolicySpec = map[string]string{
"allowPrivilegeEscalation": "allowPrivilegeEscalation determines if a pod can request to allow privilege escalation. If unspecified, defaults to true.",
"allowedHostPaths": "allowedHostPaths is a white list of allowed host paths. Empty indicates that all host paths may be used.",
"allowedFlexVolumes": "allowedFlexVolumes is a whitelist of allowed Flexvolumes. Empty or nil indicates that all Flexvolumes may be used. This parameter is effective only when the usage of the Flexvolumes is allowed in the \"volumes\" field.",
"allowedCSIDrivers": "AllowedCSIDrivers is a whitelist of inline CSI drivers that must be explicitly set to be embedded within a pod spec. An empty value means no CSI drivers can run inline within a pod spec.",
"allowedUnsafeSysctls": "allowedUnsafeSysctls is a list of explicitly allowed unsafe sysctls, defaults to none. Each entry is either a plain sysctl name or ends in \"*\" in which case it is considered as a prefix of allowed sysctls. Single * means all unsafe sysctls are allowed. Kubelet has to whitelist all allowed unsafe sysctls explicitly to avoid rejection.\n\nExamples: e.g. \"foo/*\" allows \"foo/bar\", \"foo/baz\", etc. e.g. \"foo.*\" allows \"foo.bar\", \"foo.baz\", etc.",
"forbiddenSysctls": "forbiddenSysctls is a list of explicitly forbidden sysctls, defaults to none. Each entry is either a plain sysctl name or ends in \"*\" in which case it is considered as a prefix of forbidden sysctls. Single * means all sysctls are forbidden.\n\nExamples: e.g. \"foo/*\" forbids \"foo/bar\", \"foo/baz\", etc. e.g. \"foo.*\" forbids \"foo.bar\", \"foo.baz\", etc.",
"allowedProcMountTypes": "AllowedProcMountTypes is a whitelist of allowed ProcMountTypes. Empty or nil indicates that only the DefaultProcMountType may be used. This requires the ProcMountType feature flag to be enabled.",
......
......@@ -28,6 +28,22 @@ import (
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllowedCSIDriver) DeepCopyInto(out *AllowedCSIDriver) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AllowedCSIDriver.
func (in *AllowedCSIDriver) DeepCopy() *AllowedCSIDriver {
if in == nil {
return nil
}
out := new(AllowedCSIDriver)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllowedFlexVolume) DeepCopyInto(out *AllowedFlexVolume) {
*out = *in
return
......@@ -1049,6 +1065,11 @@ func (in *PodSecurityPolicySpec) DeepCopyInto(out *PodSecurityPolicySpec) {
*out = make([]AllowedFlexVolume, len(*in))
copy(*out, *in)
}
if in.AllowedCSIDrivers != nil {
in, out := &in.AllowedCSIDrivers, &out.AllowedCSIDrivers
*out = make([]AllowedCSIDriver, len(*in))
copy(*out, *in)
}
if in.AllowedUnsafeSysctls != nil {
in, out := &in.AllowedUnsafeSysctls, &out.AllowedUnsafeSysctls
*out = make([]string, len(*in))
......
......@@ -30,6 +30,12 @@ import "k8s.io/apimachinery/pkg/util/intstr/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "v1beta1";
// AllowedCSIDriver represents a single inline CSI Driver that is allowed to be used.
message AllowedCSIDriver {
// Name is the registered name of the CSI driver
optional string name = 1;
}
// AllowedFlexVolume represents a single Flexvolume that is allowed to be used.
message AllowedFlexVolume {
// driver is the name of the Flexvolume driver.
......@@ -292,6 +298,11 @@ message PodSecurityPolicySpec {
// +optional
repeated AllowedFlexVolume allowedFlexVolumes = 18;
// AllowedCSIDrivers is a whitelist of inline CSI drivers that must be explicitly set to be embedded within a pod spec.
// An empty value means no CSI drivers can run inline within a pod spec.
// +optional
repeated AllowedCSIDriver allowedCSIDrivers = 23;
// allowedUnsafeSysctls is a list of explicitly allowed unsafe sysctls, defaults to none.
// Each entry is either a plain sysctl name or ends in "*" in which case it is considered
// as a prefix of allowed sysctls. Single * means all unsafe sysctls are allowed.
......
......@@ -216,6 +216,10 @@ type PodSecurityPolicySpec struct {
// is allowed in the "volumes" field.
// +optional
AllowedFlexVolumes []AllowedFlexVolume `json:"allowedFlexVolumes,omitempty" protobuf:"bytes,18,rep,name=allowedFlexVolumes"`
// AllowedCSIDrivers is a whitelist of inline CSI drivers that must be explicitly set to be embedded within a pod spec.
// An empty value means no CSI drivers can run inline within a pod spec.
// +optional
AllowedCSIDrivers []AllowedCSIDriver `json:"allowedCSIDrivers,omitempty" protobuf:"bytes,23,rep,name=allowedCSIDrivers"`
// allowedUnsafeSysctls is a list of explicitly allowed unsafe sysctls, defaults to none.
// Each entry is either a plain sysctl name or ends in "*" in which case it is considered
// as a prefix of allowed sysctls. Single * means all unsafe sysctls are allowed.
......@@ -304,6 +308,12 @@ type AllowedFlexVolume struct {
Driver string `json:"driver" protobuf:"bytes,1,opt,name=driver"`
}
// AllowedCSIDriver represents a single inline CSI Driver that is allowed to be used.
type AllowedCSIDriver struct {
// Name is the registered name of the CSI driver
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
}
// HostPortRange defines a range of host ports that will be enabled by a policy
// for pods to use. It requires both the start and end to be defined.
type HostPortRange struct {
......
......@@ -27,6 +27,15 @@ package v1beta1
// Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT.
var map_AllowedCSIDriver = map[string]string{
"": "AllowedCSIDriver represents a single inline CSI Driver that is allowed to be used.",
"name": "Name is the registered name of the CSI driver",
}
func (AllowedCSIDriver) SwaggerDoc() map[string]string {
return map_AllowedCSIDriver
}
var map_AllowedFlexVolume = map[string]string{
"": "AllowedFlexVolume represents a single Flexvolume that is allowed to be used.",
"driver": "driver is the name of the Flexvolume driver.",
......@@ -170,6 +179,7 @@ var map_PodSecurityPolicySpec = map[string]string{
"allowPrivilegeEscalation": "allowPrivilegeEscalation determines if a pod can request to allow privilege escalation. If unspecified, defaults to true.",
"allowedHostPaths": "allowedHostPaths is a white list of allowed host paths. Empty indicates that all host paths may be used.",
"allowedFlexVolumes": "allowedFlexVolumes is a whitelist of allowed Flexvolumes. Empty or nil indicates that all Flexvolumes may be used. This parameter is effective only when the usage of the Flexvolumes is allowed in the \"volumes\" field.",
"allowedCSIDrivers": "AllowedCSIDrivers is a whitelist of inline CSI drivers that must be explicitly set to be embedded within a pod spec. An empty value means no CSI drivers can run inline within a pod spec.",
"allowedUnsafeSysctls": "allowedUnsafeSysctls is a list of explicitly allowed unsafe sysctls, defaults to none. Each entry is either a plain sysctl name or ends in \"*\" in which case it is considered as a prefix of allowed sysctls. Single * means all unsafe sysctls are allowed. Kubelet has to whitelist all allowed unsafe sysctls explicitly to avoid rejection.\n\nExamples: e.g. \"foo/*\" allows \"foo/bar\", \"foo/baz\", etc. e.g. \"foo.*\" allows \"foo.bar\", \"foo.baz\", etc.",
"forbiddenSysctls": "forbiddenSysctls is a list of explicitly forbidden sysctls, defaults to none. Each entry is either a plain sysctl name or ends in \"*\" in which case it is considered as a prefix of forbidden sysctls. Single * means all sysctls are forbidden.\n\nExamples: e.g. \"foo/*\" forbids \"foo/bar\", \"foo/baz\", etc. e.g. \"foo.*\" forbids \"foo.bar\", \"foo.baz\", etc.",
"allowedProcMountTypes": "AllowedProcMountTypes is a whitelist of allowed ProcMountTypes. Empty or nil indicates that only the DefaultProcMountType may be used. This requires the ProcMountType feature flag to be enabled.",
......
......@@ -28,6 +28,22 @@ import (
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllowedCSIDriver) DeepCopyInto(out *AllowedCSIDriver) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AllowedCSIDriver.
func (in *AllowedCSIDriver) DeepCopy() *AllowedCSIDriver {
if in == nil {
return nil
}
out := new(AllowedCSIDriver)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllowedFlexVolume) DeepCopyInto(out *AllowedFlexVolume) {
*out = *in
return
......@@ -375,6 +391,11 @@ func (in *PodSecurityPolicySpec) DeepCopyInto(out *PodSecurityPolicySpec) {
*out = make([]AllowedFlexVolume, len(*in))
copy(*out, *in)
}
if in.AllowedCSIDrivers != nil {
in, out := &in.AllowedCSIDrivers, &out.AllowedCSIDrivers
*out = make([]AllowedCSIDriver, len(*in))
copy(*out, *in)
}
if in.AllowedUnsafeSysctls != nil {
in, out := &in.AllowedUnsafeSysctls, &out.AllowedUnsafeSysctls
*out = make([]string, len(*in))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment