Merge pull request #103276 from NetApp/data-source-ref
Add DataSourceRef field to PVC spec
This commit is contained in:
@@ -450,13 +450,31 @@ type PersistentVolumeClaimSpec struct {
|
||||
// This field can be used to specify either:
|
||||
// * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot)
|
||||
// * An existing PVC (PersistentVolumeClaim)
|
||||
// * An existing custom resource that implements data population (Alpha)
|
||||
// In order to use custom resource types that implement data population,
|
||||
// the AnyVolumeDataSource feature gate must be enabled.
|
||||
// If the provisioner or an external controller can support the specified data source,
|
||||
// it will create a new volume based on the contents of the specified data source.
|
||||
// If the AnyVolumeDataSource feature gate is enabled, this field will always have
|
||||
// the same contents as the DataSourceRef field.
|
||||
// +optional
|
||||
DataSource *TypedLocalObjectReference
|
||||
// Specifies the object from which to populate the volume with data, if a non-empty
|
||||
// volume is desired. This may be any local object from a non-empty API group (non
|
||||
// core object) or a PersistentVolumeClaim object.
|
||||
// When this field is specified, volume binding will only succeed if the type of
|
||||
// the specified object matches some installed volume populator or dynamic
|
||||
// provisioner.
|
||||
// This field will replace the functionality of the DataSource field and as such
|
||||
// if both fields are non-empty, they must have the same value. For backwards
|
||||
// compatibility, both fields (DataSource and DataSourceRef) will be set to the same
|
||||
// value automatically if one of them is empty and the other is non-empty.
|
||||
// There are two important differences between DataSource and DataSourceRef:
|
||||
// * While DataSource only allows two specific types of objects, DataSourceRef
|
||||
// allows any non-core object, as well as PersistentVolumeClaim objects.
|
||||
// * While DataSource ignores disallowed values (dropping them), DataSourceRef
|
||||
// preserves all values, and generates an error if a disallowed value is
|
||||
// specified.
|
||||
// (Alpha) Using this field requires the AnyVolumeDataSource feature gate to be enabled.
|
||||
// +optional
|
||||
DataSourceRef *TypedLocalObjectReference
|
||||
}
|
||||
|
||||
// PersistentVolumeClaimConditionType defines the condition of PV claim.
|
||||
|
2
pkg/apis/core/v1/zz_generated.conversion.go
generated
2
pkg/apis/core/v1/zz_generated.conversion.go
generated
@@ -5117,6 +5117,7 @@ func autoConvert_v1_PersistentVolumeClaimSpec_To_core_PersistentVolumeClaimSpec(
|
||||
out.StorageClassName = (*string)(unsafe.Pointer(in.StorageClassName))
|
||||
out.VolumeMode = (*core.PersistentVolumeMode)(unsafe.Pointer(in.VolumeMode))
|
||||
out.DataSource = (*core.TypedLocalObjectReference)(unsafe.Pointer(in.DataSource))
|
||||
out.DataSourceRef = (*core.TypedLocalObjectReference)(unsafe.Pointer(in.DataSourceRef))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -5135,6 +5136,7 @@ func autoConvert_core_PersistentVolumeClaimSpec_To_v1_PersistentVolumeClaimSpec(
|
||||
out.StorageClassName = (*string)(unsafe.Pointer(in.StorageClassName))
|
||||
out.VolumeMode = (*v1.PersistentVolumeMode)(unsafe.Pointer(in.VolumeMode))
|
||||
out.DataSource = (*v1.TypedLocalObjectReference)(unsafe.Pointer(in.DataSource))
|
||||
out.DataSourceRef = (*v1.TypedLocalObjectReference)(unsafe.Pointer(in.DataSourceRef))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -2046,6 +2046,27 @@ func ValidatePersistentVolumeClaim(pvc *core.PersistentVolumeClaim, opts Persist
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validateDataSource validates a DataSource/DataSourceRef in a PersistentVolumeClaimSpec
|
||||
func validateDataSource(dataSource *core.TypedLocalObjectReference, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if len(dataSource.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
|
||||
}
|
||||
if len(dataSource.Kind) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("kind"), ""))
|
||||
}
|
||||
apiGroup := ""
|
||||
if dataSource.APIGroup != nil {
|
||||
apiGroup = *dataSource.APIGroup
|
||||
}
|
||||
if len(apiGroup) == 0 && dataSource.Kind != "PersistentVolumeClaim" {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, dataSource.Kind, ""))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePersistentVolumeClaimSpec validates a PersistentVolumeClaimSpec
|
||||
func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fldPath *field.Path, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
@@ -2096,18 +2117,15 @@ func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fld
|
||||
}
|
||||
|
||||
if spec.DataSource != nil {
|
||||
if len(spec.DataSource.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("dataSource", "name"), ""))
|
||||
}
|
||||
if len(spec.DataSource.Kind) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("dataSource", "kind"), ""))
|
||||
}
|
||||
apiGroup := ""
|
||||
if spec.DataSource.APIGroup != nil {
|
||||
apiGroup = *spec.DataSource.APIGroup
|
||||
}
|
||||
if len(apiGroup) == 0 && spec.DataSource.Kind != "PersistentVolumeClaim" {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("dataSource"), spec.DataSource.Kind, ""))
|
||||
allErrs = append(allErrs, validateDataSource(spec.DataSource, fldPath.Child("dataSource"))...)
|
||||
}
|
||||
if spec.DataSourceRef != nil {
|
||||
allErrs = append(allErrs, validateDataSource(spec.DataSourceRef, fldPath.Child("dataSourceRef"))...)
|
||||
}
|
||||
if spec.DataSource != nil && spec.DataSourceRef != nil {
|
||||
if !apiequality.Semantic.DeepEqual(spec.DataSource, spec.DataSourceRef) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, fldPath.Child("dataSource"),
|
||||
"must match dataSourceRef"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1462,6 +1462,27 @@ func testValidatePVC(t *testing.T, ephemeral bool) {
|
||||
VolumeMode: &invalidMode,
|
||||
}),
|
||||
},
|
||||
"mismatch-data-source-and-ref": {
|
||||
isExpectedFailure: true,
|
||||
claim: testVolumeClaim(goodName, goodNS, core.PersistentVolumeClaimSpec{
|
||||
AccessModes: []core.PersistentVolumeAccessMode{
|
||||
core.ReadWriteOnce,
|
||||
},
|
||||
Resources: core.ResourceRequirements{
|
||||
Requests: core.ResourceList{
|
||||
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
|
||||
},
|
||||
},
|
||||
DataSource: &core.TypedLocalObjectReference{
|
||||
Kind: "PersistentVolumeClaim",
|
||||
Name: "pvc1",
|
||||
},
|
||||
DataSourceRef: &core.TypedLocalObjectReference{
|
||||
Kind: "PersistentVolumeClaim",
|
||||
Name: "pvc2",
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
for name, scenario := range scenarios {
|
||||
@@ -16966,7 +16987,7 @@ func TestValidateWindowsSecurityContextOptions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testDataSourceInSpec(name string, kind string, apiGroup string) *core.PersistentVolumeClaimSpec {
|
||||
func testDataSourceInSpec(name, kind, apiGroup string) *core.PersistentVolumeClaimSpec {
|
||||
scName := "csi-plugin"
|
||||
dataSourceInSpec := core.PersistentVolumeClaimSpec{
|
||||
AccessModes: []core.PersistentVolumeAccessMode{
|
||||
@@ -17024,14 +17045,13 @@ func TestAlphaVolumePVCDataSource(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
opts := PersistentVolumeClaimSpecValidationOptions{}
|
||||
if tc.expectedFail {
|
||||
opts := PersistentVolumeClaimSpecValidationOptions{}
|
||||
if errs := ValidatePersistentVolumeClaimSpec(&tc.claimSpec, field.NewPath("spec"), opts); len(errs) == 0 {
|
||||
t.Errorf("expected failure: %v", errs)
|
||||
}
|
||||
|
||||
} else {
|
||||
opts := PersistentVolumeClaimSpecValidationOptions{}
|
||||
if errs := ValidatePersistentVolumeClaimSpec(&tc.claimSpec, field.NewPath("spec"), opts); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
@@ -17039,6 +17059,67 @@ func TestAlphaVolumePVCDataSource(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testAnyDataSource(t *testing.T, ds, dsRef bool) {
|
||||
testCases := []struct {
|
||||
testName string
|
||||
claimSpec core.PersistentVolumeClaimSpec
|
||||
expectedFail bool
|
||||
}{
|
||||
{
|
||||
testName: "test create from valid snapshot source",
|
||||
claimSpec: *testDataSourceInSpec("test_snapshot", "VolumeSnapshot", "snapshot.storage.k8s.io"),
|
||||
},
|
||||
{
|
||||
testName: "test create from valid pvc source",
|
||||
claimSpec: *testDataSourceInSpec("test_pvc", "PersistentVolumeClaim", ""),
|
||||
},
|
||||
{
|
||||
testName: "test missing name in snapshot datasource should fail",
|
||||
claimSpec: *testDataSourceInSpec("", "VolumeSnapshot", "snapshot.storage.k8s.io"),
|
||||
expectedFail: true,
|
||||
},
|
||||
{
|
||||
testName: "test missing kind in snapshot datasource should fail",
|
||||
claimSpec: *testDataSourceInSpec("test_snapshot", "", "snapshot.storage.k8s.io"),
|
||||
expectedFail: true,
|
||||
},
|
||||
{
|
||||
testName: "test create from valid generic custom resource source",
|
||||
claimSpec: *testDataSourceInSpec("test_generic", "Generic", "generic.storage.k8s.io"),
|
||||
},
|
||||
{
|
||||
testName: "test invalid datasource should fail",
|
||||
claimSpec: *testDataSourceInSpec("test_pod", "Pod", ""),
|
||||
expectedFail: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
if dsRef {
|
||||
tc.claimSpec.DataSourceRef = tc.claimSpec.DataSource.DeepCopy()
|
||||
}
|
||||
if !ds {
|
||||
tc.claimSpec.DataSource = nil
|
||||
}
|
||||
opts := PersistentVolumeClaimSpecValidationOptions{}
|
||||
if tc.expectedFail {
|
||||
if errs := ValidatePersistentVolumeClaimSpec(&tc.claimSpec, field.NewPath("spec"), opts); len(errs) == 0 {
|
||||
t.Errorf("expected failure: %v", errs)
|
||||
}
|
||||
} else {
|
||||
if errs := ValidatePersistentVolumeClaimSpec(&tc.claimSpec, field.NewPath("spec"), opts); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnyDataSource(t *testing.T) {
|
||||
testAnyDataSource(t, true, false)
|
||||
testAnyDataSource(t, false, true)
|
||||
testAnyDataSource(t, true, false)
|
||||
}
|
||||
|
||||
func TestValidateTopologySpreadConstraints(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
5
pkg/apis/core/zz_generated.deepcopy.go
generated
5
pkg/apis/core/zz_generated.deepcopy.go
generated
@@ -2936,6 +2936,11 @@ func (in *PersistentVolumeClaimSpec) DeepCopyInto(out *PersistentVolumeClaimSpec
|
||||
*out = new(TypedLocalObjectReference)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.DataSourceRef != nil {
|
||||
in, out := &in.DataSourceRef, &out.DataSourceRef
|
||||
*out = new(TypedLocalObjectReference)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user