Add API and validation for CrossNamespaceVolumeDataSource

This commit is contained in:
Takafumi Takahashi 2022-11-01 03:54:34 +00:00
parent 623376bc82
commit 87c1ca88d4
12 changed files with 596 additions and 137 deletions

View File

@ -32,11 +32,22 @@ const (
// DropDisabledFields removes disabled fields from the pvc spec.
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a pvc spec.
func DropDisabledFields(pvcSpec *core.PersistentVolumeClaimSpec) {
func DropDisabledFields(pvcSpec, oldPVCSpec *core.PersistentVolumeClaimSpec) {
// Drop the contents of the dataSourceRef field if the AnyVolumeDataSource
// feature gate is disabled.
if !utilfeature.DefaultFeatureGate.Enabled(features.AnyVolumeDataSource) {
pvcSpec.DataSourceRef = nil
if !dataSourceRefInUse(oldPVCSpec) {
pvcSpec.DataSourceRef = nil
}
}
// Drop the contents of the dataSourceRef field if the CrossNamespaceVolumeDataSource
// feature gate is disabled and dataSourceRef.Namespace is specified.
if !utilfeature.DefaultFeatureGate.Enabled(features.CrossNamespaceVolumeDataSource) &&
pvcSpec.DataSourceRef != nil && pvcSpec.DataSourceRef.Namespace != nil && len(*pvcSpec.DataSourceRef.Namespace) != 0 {
if !dataSourceRefInUse(oldPVCSpec) {
pvcSpec.DataSourceRef = nil
}
}
}
@ -116,6 +127,16 @@ func dataSourceIsPvcOrSnapshot(dataSource *core.TypedLocalObjectReference) bool
return false
}
func dataSourceRefInUse(oldPVCSpec *core.PersistentVolumeClaimSpec) bool {
if oldPVCSpec == nil {
return false
}
if oldPVCSpec.DataSourceRef != nil {
return true
}
return false
}
// NormalizeDataSources ensures that DataSource and DataSourceRef have the same contents
// as long as both are not explicitly set.
// This should be used by creates/gets of PVCs, but not updates
@ -126,10 +147,26 @@ func NormalizeDataSources(pvcSpec *core.PersistentVolumeClaimSpec) {
}
if pvcSpec.DataSource != nil && pvcSpec.DataSourceRef == nil {
// Using the old way of setting a data source
pvcSpec.DataSourceRef = pvcSpec.DataSource.DeepCopy()
pvcSpec.DataSourceRef = &core.TypedObjectReference{
Kind: pvcSpec.DataSource.Kind,
Name: pvcSpec.DataSource.Name,
}
if pvcSpec.DataSource.APIGroup != nil {
apiGroup := *pvcSpec.DataSource.APIGroup
pvcSpec.DataSourceRef.APIGroup = &apiGroup
}
} else if pvcSpec.DataSourceRef != nil && pvcSpec.DataSource == nil {
// Using the new way of setting a data source
pvcSpec.DataSource = pvcSpec.DataSourceRef.DeepCopy()
if pvcSpec.DataSourceRef.Namespace == nil || len(*pvcSpec.DataSourceRef.Namespace) == 0 {
// Using the new way of setting a data source
pvcSpec.DataSource = &core.TypedLocalObjectReference{
Kind: pvcSpec.DataSourceRef.Kind,
Name: pvcSpec.DataSourceRef.Name,
}
if pvcSpec.DataSourceRef.APIGroup != nil {
apiGroup := *pvcSpec.DataSourceRef.APIGroup
pvcSpec.DataSource.APIGroup = &apiGroup
}
}
}
}

View File

@ -154,23 +154,47 @@ func TestPVCDataSourceSpecFilter(t *testing.T) {
}
}
// TestAnyDataSourceFilter checks to ensure the AnyVolumeDataSource feature gate works
func TestAnyDataSourceFilter(t *testing.T) {
makeDataSource := func(apiGroup, kind, name string) *core.TypedLocalObjectReference {
return &core.TypedLocalObjectReference{
APIGroup: &apiGroup,
Kind: kind,
Name: name,
}
}
var (
coreGroup = ""
snapGroup = "snapshot.storage.k8s.io"
genericGroup = "generic.storage.k8s.io"
pvcKind = "PersistentVolumeClaim"
snapKind = "VolumeSnapshot"
genericKind = "Generic"
podKind = "Pod"
)
volumeDataSource := makeDataSource("", "PersistentVolumeClaim", "my-vol")
func makeDataSource(apiGroup, kind, name string) *core.TypedLocalObjectReference {
return &core.TypedLocalObjectReference{
APIGroup: &apiGroup,
Kind: kind,
Name: name,
}
}
func makeDataSourceRef(apiGroup, kind, name string, namespace *string) *core.TypedObjectReference {
return &core.TypedObjectReference{
APIGroup: &apiGroup,
Kind: kind,
Name: name,
Namespace: namespace,
}
}
// TestDataSourceFilter checks to ensure the AnyVolumeDataSource feature gate and CrossNamespaceVolumeDataSource works
func TestDataSourceFilter(t *testing.T) {
ns := "ns1"
volumeDataSource := makeDataSource(coreGroup, pvcKind, "my-vol")
volumeDataSourceRef := makeDataSourceRef(coreGroup, pvcKind, "my-vol", nil)
xnsVolumeDataSourceRef := makeDataSourceRef(coreGroup, pvcKind, "my-vol", &ns)
var tests = map[string]struct {
spec core.PersistentVolumeClaimSpec
oldSpec core.PersistentVolumeClaimSpec
anyEnabled bool
xnsEnabled bool
want *core.TypedLocalObjectReference
wantRef *core.TypedLocalObjectReference
wantRef *core.TypedObjectReference
}{
"any disabled with empty ds": {
spec: core.PersistentVolumeClaimSpec{},
@ -180,10 +204,10 @@ func TestAnyDataSourceFilter(t *testing.T) {
want: volumeDataSource,
},
"any disabled with volume ds ref": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: volumeDataSource},
spec: core.PersistentVolumeClaimSpec{DataSourceRef: volumeDataSourceRef},
},
"any disabled with both data sources": {
spec: core.PersistentVolumeClaimSpec{DataSource: volumeDataSource, DataSourceRef: volumeDataSource},
spec: core.PersistentVolumeClaimSpec{DataSource: volumeDataSource, DataSourceRef: volumeDataSourceRef},
want: volumeDataSource,
},
"any enabled with empty ds": {
@ -196,25 +220,63 @@ func TestAnyDataSourceFilter(t *testing.T) {
want: volumeDataSource,
},
"any enabled with volume ds ref": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: volumeDataSource},
spec: core.PersistentVolumeClaimSpec{DataSourceRef: volumeDataSourceRef},
anyEnabled: true,
wantRef: volumeDataSource,
wantRef: volumeDataSourceRef,
},
"any enabled with both data sources": {
spec: core.PersistentVolumeClaimSpec{DataSource: volumeDataSource, DataSourceRef: volumeDataSource},
spec: core.PersistentVolumeClaimSpec{DataSource: volumeDataSource, DataSourceRef: volumeDataSourceRef},
anyEnabled: true,
want: volumeDataSource,
wantRef: volumeDataSource,
wantRef: volumeDataSourceRef,
},
"both any and xns enabled with xns volume ds": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: xnsVolumeDataSourceRef},
anyEnabled: true,
xnsEnabled: true,
wantRef: xnsVolumeDataSourceRef,
},
"both any and xns enabled with xns volume ds when xns volume exists in oldSpec": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: xnsVolumeDataSourceRef},
oldSpec: core.PersistentVolumeClaimSpec{DataSourceRef: xnsVolumeDataSourceRef},
anyEnabled: true,
xnsEnabled: true,
wantRef: xnsVolumeDataSourceRef,
},
"only xns enabled with xns volume ds": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: xnsVolumeDataSourceRef},
xnsEnabled: true,
},
"only any enabled with xns volume ds": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: xnsVolumeDataSourceRef},
anyEnabled: true,
},
"only any enabled with xns volume ds when xns volume exists in oldSpec": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: xnsVolumeDataSourceRef},
oldSpec: core.PersistentVolumeClaimSpec{DataSourceRef: xnsVolumeDataSourceRef},
anyEnabled: true,
wantRef: xnsVolumeDataSourceRef, // existing field isn't dropped.
},
"only any enabled with xns volume ds when volume exists in oldSpec": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: xnsVolumeDataSourceRef},
oldSpec: core.PersistentVolumeClaimSpec{DataSourceRef: volumeDataSourceRef},
anyEnabled: true,
wantRef: xnsVolumeDataSourceRef, // existing field isn't dropped.
},
}
for testName, test := range tests {
t.Run(testName, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AnyVolumeDataSource, test.anyEnabled)()
DropDisabledFields(&test.spec)
if test.spec.DataSource != test.want || test.spec.DataSourceRef != test.wantRef {
t.Errorf("expected condition was not met, test: %s, anyEnabled: %v, spec: %v, expected: %v %v",
testName, test.anyEnabled, test.spec, test.want, test.wantRef)
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CrossNamespaceVolumeDataSource, test.xnsEnabled)()
DropDisabledFields(&test.spec, &test.oldSpec)
if test.spec.DataSource != test.want {
t.Errorf("expected condition was not met, test: %s, anyEnabled: %v, xnsEnabled: %v, spec: %+v, expected DataSource: %+v",
testName, test.anyEnabled, test.xnsEnabled, test.spec, test.want)
}
if test.spec.DataSourceRef != test.wantRef {
t.Errorf("expected condition was not met, test: %s, anyEnabled: %v, xnsEnabled: %v, spec: %+v, expected DataSourceRef: %+v",
testName, test.anyEnabled, test.xnsEnabled, test.spec, test.wantRef)
}
})
}
@ -223,69 +285,99 @@ func TestAnyDataSourceFilter(t *testing.T) {
// TestDataSourceRef checks to ensure the DataSourceRef field handles backwards
// compatibility with the DataSource field
func TestDataSourceRef(t *testing.T) {
makeDataSource := func(apiGroup, kind, name string) *core.TypedLocalObjectReference {
return &core.TypedLocalObjectReference{
APIGroup: &apiGroup,
Kind: kind,
Name: name,
}
}
volumeDataSource := makeDataSource("", "PersistentVolumeClaim", "my-vol")
snapshotDataSource := makeDataSource("snapshot.storage.k8s.io", "VolumeSnapshot", "my-snap")
genericDataSource := makeDataSource("generic.storage.k8s.io", "Generic", "my-foo")
coreDataSource := makeDataSource("", "Pod", "my-pod")
ns := "ns1"
volumeDataSource := makeDataSource(coreGroup, pvcKind, "my-vol")
volumeDataSourceRef := makeDataSourceRef(coreGroup, pvcKind, "my-vol", nil)
xnsVolumeDataSourceRef := makeDataSourceRef(coreGroup, pvcKind, "my-vol", &ns)
snapshotDataSource := makeDataSource(snapGroup, snapKind, "my-snap")
snapshotDataSourceRef := makeDataSourceRef(snapGroup, snapKind, "my-snap", nil)
xnsSnapshotDataSourceRef := makeDataSourceRef(snapGroup, snapKind, "my-snap", &ns)
genericDataSource := makeDataSource(genericGroup, genericKind, "my-foo")
genericDataSourceRef := makeDataSourceRef(genericGroup, genericKind, "my-foo", nil)
xnsGenericDataSourceRef := makeDataSourceRef(genericGroup, genericKind, "my-foo", &ns)
coreDataSource := makeDataSource(coreGroup, podKind, "my-pod")
coreDataSourceRef := makeDataSourceRef(coreGroup, podKind, "my-pod", nil)
xnsCoreDataSourceRef := makeDataSourceRef(coreGroup, podKind, "my-pod", &ns)
var tests = map[string]struct {
spec core.PersistentVolumeClaimSpec
want *core.TypedLocalObjectReference
spec core.PersistentVolumeClaimSpec
want *core.TypedLocalObjectReference
wantRef *core.TypedObjectReference
}{
"empty ds": {
spec: core.PersistentVolumeClaimSpec{},
},
"volume ds": {
spec: core.PersistentVolumeClaimSpec{DataSource: volumeDataSource},
want: volumeDataSource,
spec: core.PersistentVolumeClaimSpec{DataSource: volumeDataSource},
want: volumeDataSource,
wantRef: volumeDataSourceRef,
},
"snapshot ds": {
spec: core.PersistentVolumeClaimSpec{DataSource: snapshotDataSource},
want: snapshotDataSource,
spec: core.PersistentVolumeClaimSpec{DataSource: snapshotDataSource},
want: snapshotDataSource,
wantRef: snapshotDataSourceRef,
},
"generic ds": {
spec: core.PersistentVolumeClaimSpec{DataSource: genericDataSource},
want: genericDataSource,
spec: core.PersistentVolumeClaimSpec{DataSource: genericDataSource},
want: genericDataSource,
wantRef: genericDataSourceRef,
},
"core ds": {
spec: core.PersistentVolumeClaimSpec{DataSource: coreDataSource},
want: coreDataSource,
spec: core.PersistentVolumeClaimSpec{DataSource: coreDataSource},
want: coreDataSource,
wantRef: coreDataSourceRef,
},
"volume ds ref": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: volumeDataSource},
want: volumeDataSource,
spec: core.PersistentVolumeClaimSpec{DataSourceRef: volumeDataSourceRef},
want: volumeDataSource,
wantRef: volumeDataSourceRef,
},
"snapshot ds ref": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: snapshotDataSource},
want: snapshotDataSource,
spec: core.PersistentVolumeClaimSpec{DataSourceRef: snapshotDataSourceRef},
want: snapshotDataSource,
wantRef: snapshotDataSourceRef,
},
"generic ds ref": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: genericDataSource},
want: genericDataSource,
spec: core.PersistentVolumeClaimSpec{DataSourceRef: genericDataSourceRef},
want: genericDataSource,
wantRef: genericDataSourceRef,
},
"core ds ref": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: coreDataSource},
want: coreDataSource,
spec: core.PersistentVolumeClaimSpec{DataSourceRef: coreDataSourceRef},
want: coreDataSource,
wantRef: coreDataSourceRef,
},
"xns volume ds ref": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: xnsVolumeDataSourceRef},
wantRef: xnsVolumeDataSourceRef,
},
"xns snapshot ds ref": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: xnsSnapshotDataSourceRef},
wantRef: xnsSnapshotDataSourceRef,
},
"xns generic ds ref": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: xnsGenericDataSourceRef},
wantRef: xnsGenericDataSourceRef,
},
"xns core ds ref": {
spec: core.PersistentVolumeClaimSpec{DataSourceRef: xnsCoreDataSourceRef},
wantRef: xnsCoreDataSourceRef,
},
}
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AnyVolumeDataSource, true)()
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CrossNamespaceVolumeDataSource, true)()
for testName, test := range tests {
t.Run(testName, func(t *testing.T) {
NormalizeDataSources(&test.spec)
if !reflect.DeepEqual(test.spec.DataSource, test.want) ||
!reflect.DeepEqual(test.spec.DataSourceRef, test.want) {
t.Errorf("expected condition was not met, test: %s, spec: %v, expected: %v",
testName, test.spec, test.want)
if !reflect.DeepEqual(test.spec.DataSource, test.want) {
t.Errorf("expected condition was not met, test: %s, spec.datasource: %+v, want: %+v",
testName, test.spec.DataSource, test.want)
}
if !reflect.DeepEqual(test.spec.DataSourceRef, test.wantRef) {
t.Errorf("expected condition was not met, test: %s, spec.datasourceRef: %+v, wantRef: %+v",
testName, test.spec.DataSourceRef, test.wantRef)
}
})
}

View File

@ -452,29 +452,54 @@ type PersistentVolumeClaimSpec struct {
// * An existing PVC (PersistentVolumeClaim)
// 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.
// When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef,
// and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified.
// If the namespace is specified, then dataSourceRef will not be copied to dataSource.
// +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
// volume is desired. This may be any 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
// 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
// compatibility, when namespace isn't specified in dataSourceRef,
// 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
// When namespace is specified in dataSourceRef,
// dataSource isn't set to the same value and must be empty.
// There are three 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
// * While dataSource ignores disallowed values (dropping them), dataSourceRef
// preserves all values, and generates an error if a disallowed value is
// specified.
// * While dataSource only allows local objects, dataSourceRef allows objects
// in any namespaces.
// (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled.
// (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled.
// +optional
DataSourceRef *TypedLocalObjectReference
DataSourceRef *TypedObjectReference
}
type TypedObjectReference struct {
// APIGroup is the group for the resource being referenced.
// If APIGroup is not specified, the specified Kind must be in the core API group.
// For any other third-party types, APIGroup is required.
// +optional
APIGroup *string
// Kind is the type of resource being referenced
Kind string
// Name is the name of resource being referenced
Name string
// Namespace is the namespace of resource being referenced
// Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details.
// (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled.
// +featureGate=CrossNamespaceVolumeDataSource
// +optional
Namespace *string
}
// PersistentVolumeClaimConditionType defines the condition of PV claim.

View File

@ -2104,7 +2104,34 @@ func validateDataSource(dataSource *core.TypedLocalObjectReference, fldPath *fie
apiGroup = *dataSource.APIGroup
}
if len(apiGroup) == 0 && dataSource.Kind != "PersistentVolumeClaim" {
allErrs = append(allErrs, field.Invalid(fldPath, dataSource.Kind, ""))
allErrs = append(allErrs, field.Invalid(fldPath, dataSource.Kind, "must be 'PersistentVolumeClaim' when referencing the default apiGroup"))
}
return allErrs
}
// validateDataSourceRef validates a DataSourceRef in a PersistentVolumeClaimSpec
func validateDataSourceRef(dataSourceRef *core.TypedObjectReference, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if len(dataSourceRef.Name) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
}
if len(dataSourceRef.Kind) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("kind"), ""))
}
apiGroup := ""
if dataSourceRef.APIGroup != nil {
apiGroup = *dataSourceRef.APIGroup
}
if len(apiGroup) == 0 && dataSourceRef.Kind != "PersistentVolumeClaim" {
allErrs = append(allErrs, field.Invalid(fldPath, dataSourceRef.Kind, "must be 'PersistentVolumeClaim' when referencing the default apiGroup"))
}
if dataSourceRef.Namespace != nil && len(*dataSourceRef.Namespace) > 0 {
for _, msg := range ValidateNameFunc(ValidateNamespaceName)(*dataSourceRef.Namespace, false) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), *dataSourceRef.Namespace, msg))
}
}
return allErrs
@ -2166,10 +2193,15 @@ func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fld
allErrs = append(allErrs, validateDataSource(spec.DataSource, fldPath.Child("dataSource"))...)
}
if spec.DataSourceRef != nil {
allErrs = append(allErrs, validateDataSource(spec.DataSourceRef, fldPath.Child("dataSourceRef"))...)
allErrs = append(allErrs, validateDataSourceRef(spec.DataSourceRef, fldPath.Child("dataSourceRef"))...)
}
if spec.DataSource != nil && spec.DataSourceRef != nil {
if !apiequality.Semantic.DeepEqual(spec.DataSource, spec.DataSourceRef) {
if spec.DataSourceRef != nil && spec.DataSourceRef.Namespace != nil && len(*spec.DataSourceRef.Namespace) > 0 {
if spec.DataSource != nil {
allErrs = append(allErrs, field.Invalid(fldPath, fldPath.Child("dataSource"),
"may not be specified when dataSourceRef.namespace is specified"))
}
} else if spec.DataSource != nil && spec.DataSourceRef != nil {
if !isDataSourceEqualDataSourceRef(spec.DataSource, spec.DataSourceRef) {
allErrs = append(allErrs, field.Invalid(fldPath, fldPath.Child("dataSource"),
"must match dataSourceRef"))
}
@ -2178,6 +2210,10 @@ func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fld
return allErrs
}
func isDataSourceEqualDataSourceRef(dataSource *core.TypedLocalObjectReference, dataSourceRef *core.TypedObjectReference) bool {
return reflect.DeepEqual(dataSource.APIGroup, dataSourceRef.APIGroup) && dataSource.Kind == dataSourceRef.Kind && dataSource.Name == dataSourceRef.Name
}
// ValidatePersistentVolumeClaimUpdate validates an update to a PersistentVolumeClaim
func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeClaim, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
allErrs := ValidateObjectMetaUpdate(&newPvc.ObjectMeta, &oldPvc.ObjectMeta, field.NewPath("metadata"))

View File

@ -1672,7 +1672,7 @@ func testValidatePVC(t *testing.T, ephemeral bool) {
Kind: "PersistentVolumeClaim",
Name: "pvc1",
},
DataSourceRef: &core.TypedLocalObjectReference{
DataSourceRef: &core.TypedObjectReference{
Kind: "PersistentVolumeClaim",
Name: "pvc2",
},
@ -19816,7 +19816,11 @@ func testAnyDataSource(t *testing.T, ds, dsRef bool) {
for _, tc := range testCases {
if dsRef {
tc.claimSpec.DataSourceRef = tc.claimSpec.DataSource.DeepCopy()
tc.claimSpec.DataSourceRef = &core.TypedObjectReference{
APIGroup: tc.claimSpec.DataSource.APIGroup,
Kind: tc.claimSpec.DataSource.Kind,
Name: tc.claimSpec.DataSource.Name,
}
}
if !ds {
tc.claimSpec.DataSource = nil
@ -19840,6 +19844,110 @@ func TestAnyDataSource(t *testing.T) {
testAnyDataSource(t, true, false)
}
func pvcSpecWithCrossNamespaceSource(apiGroup *string, kind string, namespace *string, name string, isDataSourceSet bool) *core.PersistentVolumeClaimSpec {
scName := "csi-plugin"
spec := core.PersistentVolumeClaimSpec{
AccessModes: []core.PersistentVolumeAccessMode{
core.ReadOnlyMany,
},
Resources: core.ResourceRequirements{
Requests: core.ResourceList{
core.ResourceName(core.ResourceStorage): resource.MustParse("10G"),
},
},
StorageClassName: &scName,
DataSourceRef: &core.TypedObjectReference{
APIGroup: apiGroup,
Kind: kind,
Namespace: namespace,
Name: name,
},
}
if isDataSourceSet {
spec.DataSource = &core.TypedLocalObjectReference{
APIGroup: apiGroup,
Kind: kind,
Name: name,
}
}
return &spec
}
func TestCrossNamespaceSource(t *testing.T) {
snapAPIGroup := "snapshot.storage.k8s.io"
coreAPIGroup := ""
unsupportedAPIGroup := "unsupported.example.com"
snapKind := "VolumeSnapshot"
pvcKind := "PersistentVolumeClaim"
goodNS := "ns1"
badNS := "a*b"
emptyNS := ""
goodName := "snapshot1"
testCases := []struct {
testName string
expectedFail bool
claimSpec *core.PersistentVolumeClaimSpec
}{
{
testName: "Feature gate enabled and valid xns DataSourceRef specified",
expectedFail: false,
claimSpec: pvcSpecWithCrossNamespaceSource(&snapAPIGroup, snapKind, &goodNS, goodName, false),
},
{
testName: "Feature gate enabled and xns DataSourceRef with PVC source specified",
expectedFail: false,
claimSpec: pvcSpecWithCrossNamespaceSource(&coreAPIGroup, pvcKind, &goodNS, goodName, false),
},
{
testName: "Feature gate enabled and xns DataSourceRef with unsupported source specified",
expectedFail: false,
claimSpec: pvcSpecWithCrossNamespaceSource(&unsupportedAPIGroup, "UnsupportedKind", &goodNS, goodName, false),
},
{
testName: "Feature gate enabled and xns DataSourceRef with nil apiGroup",
expectedFail: true,
claimSpec: pvcSpecWithCrossNamespaceSource(nil, "UnsupportedKind", &goodNS, goodName, false),
},
{
testName: "Feature gate enabled and xns DataSourceRef with invalid namspace specified",
expectedFail: true,
claimSpec: pvcSpecWithCrossNamespaceSource(&snapAPIGroup, snapKind, &badNS, goodName, false),
},
{
testName: "Feature gate enabled and xns DataSourceRef with nil namspace specified",
expectedFail: false,
claimSpec: pvcSpecWithCrossNamespaceSource(&snapAPIGroup, snapKind, nil, goodName, false),
},
{
testName: "Feature gate enabled and xns DataSourceRef with empty namspace specified",
expectedFail: false,
claimSpec: pvcSpecWithCrossNamespaceSource(&snapAPIGroup, snapKind, &emptyNS, goodName, false),
},
{
testName: "Feature gate enabled and both xns DataSourceRef and DataSource specified",
expectedFail: true,
claimSpec: pvcSpecWithCrossNamespaceSource(&snapAPIGroup, snapKind, &goodNS, goodName, true),
},
}
for _, tc := range testCases {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AnyVolumeDataSource, true)()
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CrossNamespaceVolumeDataSource, true)()
opts := PersistentVolumeClaimSpecValidationOptions{}
if tc.expectedFail {
if errs := ValidatePersistentVolumeClaimSpec(tc.claimSpec, field.NewPath("spec"), opts); len(errs) == 0 {
t.Errorf("%s: expected failure: %v", tc.testName, errs)
}
} else {
if errs := ValidatePersistentVolumeClaimSpec(tc.claimSpec, field.NewPath("spec"), opts); len(errs) != 0 {
t.Errorf("%s: expected success: %v", tc.testName, errs)
}
}
}
}
func TestValidateTopologySpreadConstraints(t *testing.T) {
fieldPath := field.NewPath("field")
subFldPath0 := fieldPath.Index(0)

View File

@ -36,6 +36,13 @@ const (
// of code conflicts because changes are more likely to be scattered
// across the file.
// owner: @ttakahashi21 @mkimuram
// kep: https://kep.k8s.io/3294
// alpha: v1.26
//
// Enable usage of Provision of PVCs from snapshots in other namespaces
CrossNamespaceVolumeDataSource featuregate.Feature = "CrossNamespaceVolumeDataSource"
// owner: @bswartz
// alpha: v1.18
// beta: v1.24
@ -893,6 +900,8 @@ func init() {
// Entries are separated from each other with blank lines to avoid sweeping gofmt changes
// when adding or removing one entry.
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
CrossNamespaceVolumeDataSource: {Default: false, PreRelease: featuregate.Alpha},
AnyVolumeDataSource: {Default: true, PreRelease: featuregate.Beta}, // on by default in 1.24
APISelfSubjectReview: {Default: false, PreRelease: featuregate.Alpha},

View File

@ -216,11 +216,14 @@ func TestDefaultOnReadPvc(t *testing.T) {
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
dataSource := api.TypedLocalObjectReference{
Kind: "PersistentVolumeClaim",
Name: "my-pvc",
}
dataSourceRef := api.TypedObjectReference{
Kind: "PersistentVolumeClaim",
Name: "my-pvc",
}
var tests = map[string]struct {
anyEnabled bool
@ -278,15 +281,15 @@ func TestDefaultOnReadPvc(t *testing.T) {
pvc.Spec.DataSource = dataSource.DeepCopy()
}
if test.dataSourceRef {
pvc.Spec.DataSourceRef = dataSource.DeepCopy()
pvc.Spec.DataSourceRef = dataSourceRef.DeepCopy()
}
var expectDataSource *api.TypedLocalObjectReference
if test.want {
expectDataSource = &dataSource
}
var expectDataSourceRef *api.TypedLocalObjectReference
var expectDataSourceRef *api.TypedObjectReference
if test.wantRef {
expectDataSourceRef = &dataSource
expectDataSourceRef = &dataSourceRef
}
// Method under test

View File

@ -65,7 +65,7 @@ func (persistentvolumeclaimStrategy) GetResetFields() map[fieldpath.APIVersion]*
func (persistentvolumeclaimStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
pvc := obj.(*api.PersistentVolumeClaim)
pvc.Status = api.PersistentVolumeClaimStatus{}
pvcutil.DropDisabledFields(&pvc.Spec)
pvcutil.DropDisabledFields(&pvc.Spec, nil)
// For data sources, we need to do 2 things to implement KEP 1495
@ -103,7 +103,7 @@ func (persistentvolumeclaimStrategy) PrepareForUpdate(ctx context.Context, obj,
oldPvc := old.(*api.PersistentVolumeClaim)
newPvc.Status = oldPvc.Status
pvcutil.DropDisabledFields(&newPvc.Spec)
pvcutil.DropDisabledFields(&newPvc.Spec, &oldPvc.Spec)
// We need to use similar logic to PrepareForCreate here both to preserve backwards
// compatibility with the old behavior (ignoring of garbage dataSources at both create

View File

@ -107,28 +107,58 @@ func TestDropConditions(t *testing.T) {
}
var (
coreGroup = ""
snapGroup = "snapshot.storage.k8s.io"
genericGroup = "generic.storage.k8s.io"
pvcKind = "PersistentVolumeClaim"
snapKind = "VolumeSnapshot"
genericKind = "Generic"
podKind = "Pod"
)
func makeDataSource(apiGroup, kind, name string) *api.TypedLocalObjectReference {
return &api.TypedLocalObjectReference{
APIGroup: &apiGroup,
Kind: kind,
Name: name,
}
}
func makeDataSourceRef(apiGroup, kind, name string, namespace *string) *api.TypedObjectReference {
return &api.TypedObjectReference{
APIGroup: &apiGroup,
Kind: kind,
Name: name,
Namespace: namespace,
}
}
func TestPrepareForCreate(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
makeDataSource := func(apiGroup, kind, name string) *api.TypedLocalObjectReference {
return &api.TypedLocalObjectReference{
APIGroup: &apiGroup,
Kind: kind,
Name: name,
}
}
volumeDataSource := makeDataSource("", "PersistentVolumeClaim", "my-vol")
snapshotDataSource := makeDataSource("snapshot.storage.k8s.io", "VolumeSnapshot", "my-snap")
genericDataSource := makeDataSource("generic.storage.k8s.io", "Generic", "my-foo")
coreDataSource := makeDataSource("", "Pod", "my-pod")
ns := "ns1"
volumeDataSource := makeDataSource(coreGroup, pvcKind, "my-vol")
volumeDataSourceRef := makeDataSourceRef(coreGroup, pvcKind, "my-vol", nil)
xnsVolumeDataSourceRef := makeDataSourceRef(coreGroup, pvcKind, "my-vol", &ns)
snapshotDataSource := makeDataSource(snapGroup, snapKind, "my-snap")
snapshotDataSourceRef := makeDataSourceRef(snapGroup, snapKind, "my-snap", nil)
xnsSnapshotDataSourceRef := makeDataSourceRef(snapGroup, snapKind, "my-snap", &ns)
genericDataSource := makeDataSource(genericGroup, genericKind, "my-foo")
genericDataSourceRef := makeDataSourceRef(genericGroup, genericKind, "my-foo", nil)
xnsGenericDataSourceRef := makeDataSourceRef(genericGroup, genericKind, "my-foo", &ns)
coreDataSource := makeDataSource(coreGroup, podKind, "my-pod")
coreDataSourceRef := makeDataSourceRef(coreGroup, podKind, "my-pod", nil)
xnsCoreDataSourceRef := makeDataSourceRef(coreGroup, podKind, "my-pod", &ns)
var tests = map[string]struct {
anyEnabled bool
xnsEnabled bool
dataSource *api.TypedLocalObjectReference
dataSourceRef *api.TypedLocalObjectReference
dataSourceRef *api.TypedObjectReference
want *api.TypedLocalObjectReference
wantRef *api.TypedLocalObjectReference
wantRef *api.TypedObjectReference
}{
"any disabled with empty ds": {
want: nil,
@ -150,16 +180,16 @@ func TestPrepareForCreate(t *testing.T) {
want: nil,
},
"any disabled with volume ds ref": {
dataSourceRef: volumeDataSource,
dataSourceRef: volumeDataSourceRef,
},
"any disabled with snapshot ds ref": {
dataSourceRef: snapshotDataSource,
dataSourceRef: snapshotDataSourceRef,
},
"any disabled with generic ds ref": {
dataSourceRef: genericDataSource,
dataSourceRef: genericDataSourceRef,
},
"any disabled with invalid ds ref": {
dataSourceRef: coreDataSource,
dataSourceRef: coreDataSourceRef,
},
"any enabled with empty ds": {
anyEnabled: true,
@ -169,13 +199,13 @@ func TestPrepareForCreate(t *testing.T) {
dataSource: volumeDataSource,
anyEnabled: true,
want: volumeDataSource,
wantRef: volumeDataSource,
wantRef: volumeDataSourceRef,
},
"any enabled with snapshot ds": {
dataSource: snapshotDataSource,
anyEnabled: true,
want: snapshotDataSource,
wantRef: snapshotDataSource,
wantRef: snapshotDataSourceRef,
},
"any enabled with generic ds": {
dataSource: genericDataSource,
@ -186,41 +216,135 @@ func TestPrepareForCreate(t *testing.T) {
anyEnabled: true,
},
"any enabled with volume ds ref": {
dataSourceRef: volumeDataSource,
dataSourceRef: volumeDataSourceRef,
anyEnabled: true,
want: volumeDataSource,
wantRef: volumeDataSource,
wantRef: volumeDataSourceRef,
},
"any enabled with snapshot ds ref": {
dataSourceRef: snapshotDataSource,
dataSourceRef: snapshotDataSourceRef,
anyEnabled: true,
want: snapshotDataSource,
wantRef: snapshotDataSource,
wantRef: snapshotDataSourceRef,
},
"any enabled with generic ds ref": {
dataSourceRef: genericDataSource,
dataSourceRef: genericDataSourceRef,
anyEnabled: true,
want: genericDataSource,
wantRef: genericDataSource,
wantRef: genericDataSourceRef,
},
"any enabled with invalid ds ref": {
dataSourceRef: coreDataSource,
dataSourceRef: coreDataSourceRef,
anyEnabled: true,
want: coreDataSource,
wantRef: coreDataSource,
wantRef: coreDataSourceRef,
},
"any enabled with mismatched data sources": {
dataSource: volumeDataSource,
dataSourceRef: snapshotDataSource,
dataSourceRef: snapshotDataSourceRef,
anyEnabled: true,
want: volumeDataSource,
wantRef: snapshotDataSource,
wantRef: snapshotDataSourceRef,
},
"both any and xns enabled with empty ds": {
anyEnabled: true,
xnsEnabled: true,
want: nil,
},
"both any and xns enabled with volume ds": {
dataSource: volumeDataSource,
anyEnabled: true,
xnsEnabled: true,
want: volumeDataSource,
wantRef: volumeDataSourceRef,
},
"both any and xns enabled with snapshot ds": {
dataSource: snapshotDataSource,
anyEnabled: true,
xnsEnabled: true,
want: snapshotDataSource,
wantRef: snapshotDataSourceRef,
},
"both any and xns enabled with generic ds": {
dataSource: genericDataSource,
anyEnabled: true,
xnsEnabled: true,
},
"both any and xns enabled with invalid ds": {
dataSource: coreDataSource,
anyEnabled: true,
xnsEnabled: true,
},
"both any and xns enabled with volume ds ref": {
dataSourceRef: volumeDataSourceRef,
anyEnabled: true,
xnsEnabled: true,
want: volumeDataSource,
wantRef: volumeDataSourceRef,
},
"both any and xns enabled with snapshot ds ref": {
dataSourceRef: snapshotDataSourceRef,
anyEnabled: true,
xnsEnabled: true,
want: snapshotDataSource,
wantRef: snapshotDataSourceRef,
},
"both any and xns enabled with generic ds ref": {
dataSourceRef: genericDataSourceRef,
anyEnabled: true,
xnsEnabled: true,
want: genericDataSource,
wantRef: genericDataSourceRef,
},
"both any and xns enabled with invalid ds ref": {
dataSourceRef: coreDataSourceRef,
anyEnabled: true,
xnsEnabled: true,
want: coreDataSource,
wantRef: coreDataSourceRef,
},
"both any and xns enabled with mismatched data sources": {
dataSource: volumeDataSource,
dataSourceRef: snapshotDataSourceRef,
anyEnabled: true,
xnsEnabled: true,
want: volumeDataSource,
wantRef: snapshotDataSourceRef,
},
"both any and xns enabled with volume xns ds ref": {
dataSourceRef: xnsVolumeDataSourceRef,
anyEnabled: true,
xnsEnabled: true,
wantRef: xnsVolumeDataSourceRef,
},
"both any and xns enabled with snapshot xns ds ref": {
dataSourceRef: xnsSnapshotDataSourceRef,
anyEnabled: true,
xnsEnabled: true,
wantRef: xnsSnapshotDataSourceRef,
},
"both any and xns enabled with generic xns ds ref": {
dataSourceRef: xnsGenericDataSourceRef,
anyEnabled: true,
xnsEnabled: true,
wantRef: xnsGenericDataSourceRef,
},
"both any and xns enabled with invalid xns ds ref": {
dataSourceRef: xnsCoreDataSourceRef,
anyEnabled: true,
xnsEnabled: true,
wantRef: xnsCoreDataSourceRef,
},
"only xns enabled with snapshot xns ds ref": {
dataSourceRef: xnsSnapshotDataSourceRef,
xnsEnabled: true,
},
}
for testName, test := range tests {
t.Run(testName, func(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AnyVolumeDataSource, test.anyEnabled)()
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CrossNamespaceVolumeDataSource, test.xnsEnabled)()
pvc := api.PersistentVolumeClaim{
Spec: api.PersistentVolumeClaimSpec{
DataSource: test.dataSource,

View File

@ -497,29 +497,54 @@ type PersistentVolumeClaimSpec struct {
// * An existing PVC (PersistentVolumeClaim)
// 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.
// When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef,
// and dataSourceRef contents will be copied to dataSource when dataSourceRef.namespace is not specified.
// If the namespace is specified, then dataSourceRef will not be copied to dataSource.
// +optional
DataSource *TypedLocalObjectReference `json:"dataSource,omitempty" protobuf:"bytes,7,opt,name=dataSource"`
// dataSourceRef 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
// volume is desired. This may be any 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
// 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
// compatibility, when namespace isn't specified in dataSourceRef,
// 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
// When namespace is specified in dataSourceRef,
// dataSource isn't set to the same value and must be empty.
// There are three 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
// * While dataSource ignores disallowed values (dropping them), dataSourceRef
// preserves all values, and generates an error if a disallowed value is
// specified.
// * While dataSource only allows local objects, dataSourceRef allows objects
// in any namespaces.
// (Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled.
// (Alpha) Using the namespace field of dataSourceRef requires the CrossNamespaceVolumeDataSource feature gate to be enabled.
// +optional
DataSourceRef *TypedLocalObjectReference `json:"dataSourceRef,omitempty" protobuf:"bytes,8,opt,name=dataSourceRef"`
DataSourceRef *TypedObjectReference `json:"dataSourceRef,omitempty" protobuf:"bytes,8,opt,name=dataSourceRef"`
}
type TypedObjectReference struct {
// APIGroup is the group for the resource being referenced.
// If APIGroup is not specified, the specified Kind must be in the core API group.
// For any other third-party types, APIGroup is required.
// +optional
APIGroup *string `json:"apiGroup" protobuf:"bytes,1,opt,name=apiGroup"`
// Kind is the type of resource being referenced
Kind string `json:"kind" protobuf:"bytes,2,opt,name=kind"`
// Name is the name of resource being referenced
Name string `json:"name" protobuf:"bytes,3,opt,name=name"`
// Namespace is the namespace of resource being referenced
// Note that when a namespace is specified, a gateway.networking.k8s.io/ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details.
// (Alpha) This field requires the CrossNamespaceVolumeDataSource feature gate to be enabled.
// +featureGate=CrossNamespaceVolumeDataSource
// +optional
Namespace *string `json:"namespace,omitempty" protobuf:"bytes,4,opt,name=namespace"`
}
// PersistentVolumeClaimConditionType is a valid value of PersistentVolumeClaimCondition.Type

View File

@ -342,7 +342,7 @@ func (t *multiVolumeTestSuite) DefineTests(driver storageframework.TestDriver, p
}
testConfig := storageframework.ConvertTestConfig(l.config)
dc := l.config.Framework.DynamicClient
dataSource := prepareSnapshotDataSourceForProvisioning(ctx, f, testConfig, l.config, pattern, l.cs, dc, resource.Pvc, resource.Sc, sDriver, pattern.VolMode, expectedContent)
dataSourceRef := prepareSnapshotDataSourceForProvisioning(ctx, f, testConfig, l.config, pattern, l.cs, dc, resource.Pvc, resource.Sc, sDriver, pattern.VolMode, expectedContent)
// Create 2nd PVC for testing
pvc2 := &v1.PersistentVolumeClaim{
@ -353,7 +353,7 @@ func (t *multiVolumeTestSuite) DefineTests(driver storageframework.TestDriver, p
}
resource.Pvc.Spec.DeepCopyInto(&pvc2.Spec)
pvc2.Spec.VolumeName = ""
pvc2.Spec.DataSource = dataSource
pvc2.Spec.DataSourceRef = dataSourceRef
pvc2, err := l.cs.CoreV1().PersistentVolumeClaims(pvc2.Namespace).Create(context.TODO(), pvc2, metav1.CreateOptions{})
framework.ExpectNoError(err)
@ -386,7 +386,7 @@ func (t *multiVolumeTestSuite) DefineTests(driver storageframework.TestDriver, p
l.resources = append(l.resources, resource)
pvcs := []*v1.PersistentVolumeClaim{resource.Pvc}
testConfig := storageframework.ConvertTestConfig(l.config)
dataSource := preparePVCDataSourceForProvisioning(ctx, f, testConfig, l.cs, resource.Pvc, resource.Sc, pattern.VolMode, expectedContent)
dataSourceRef := preparePVCDataSourceForProvisioning(ctx, f, testConfig, l.cs, resource.Pvc, resource.Sc, pattern.VolMode, expectedContent)
// Create 2nd PVC for testing
pvc2 := &v1.PersistentVolumeClaim{
@ -397,7 +397,7 @@ func (t *multiVolumeTestSuite) DefineTests(driver storageframework.TestDriver, p
}
resource.Pvc.Spec.DeepCopyInto(&pvc2.Spec)
pvc2.Spec.VolumeName = ""
pvc2.Spec.DataSource = dataSource
pvc2.Spec.DataSourceRef = dataSourceRef
pvc2, err := l.cs.CoreV1().PersistentVolumeClaims(pvc2.Namespace).Create(context.TODO(), pvc2, metav1.CreateOptions{})
framework.ExpectNoError(err)

View File

@ -214,9 +214,9 @@ func (p *provisioningTestSuite) DefineTests(driver storageframework.TestDriver,
dc := l.config.Framework.DynamicClient
testConfig := storageframework.ConvertTestConfig(l.config)
expectedContent := fmt.Sprintf("Hello from namespace %s", f.Namespace.Name)
dataSource := prepareSnapshotDataSourceForProvisioning(ctx, f, testConfig, l.config, pattern, l.cs, dc, l.pvc, l.sc, sDriver, pattern.VolMode, expectedContent)
dataSourceRef := prepareSnapshotDataSourceForProvisioning(ctx, f, testConfig, l.config, pattern, l.cs, dc, l.pvc, l.sc, sDriver, pattern.VolMode, expectedContent)
l.pvc.Spec.DataSource = dataSource
l.pvc.Spec.DataSourceRef = dataSourceRef
l.testCase.PvCheck = func(claim *v1.PersistentVolumeClaim) {
ginkgo.By("checking whether the created volume has the pre-populated data")
tests := []e2evolume.Test{
@ -386,7 +386,7 @@ func (p *provisioningTestSuite) DefineTests(driver storageframework.TestDriver,
}()
apiGroup := "hello.example.com"
l.pvc.Spec.DataSourceRef = &v1.TypedLocalObjectReference{
l.pvc.Spec.DataSourceRef = &v1.TypedObjectReference{
APIGroup: &apiGroup,
Kind: "Hello",
Name: helloCRName,
@ -427,8 +427,8 @@ func (p *provisioningTestSuite) DefineTests(driver storageframework.TestDriver,
}
testConfig := storageframework.ConvertTestConfig(l.config)
expectedContent := fmt.Sprintf("Hello from namespace %s", f.Namespace.Name)
dataSource := preparePVCDataSourceForProvisioning(ctx, f, testConfig, l.cs, l.sourcePVC, l.sc, pattern.VolMode, expectedContent)
l.pvc.Spec.DataSource = dataSource
dataSourceRef := preparePVCDataSourceForProvisioning(ctx, f, testConfig, l.cs, l.sourcePVC, l.sc, pattern.VolMode, expectedContent)
l.pvc.Spec.DataSourceRef = dataSourceRef
l.testCase.NodeSelection = testConfig.ClientNodeSelection
l.testCase.PvCheck = func(claim *v1.PersistentVolumeClaim) {
ginkgo.By("checking whether the created volume has the pre-populated data")
@ -443,7 +443,7 @@ func (p *provisioningTestSuite) DefineTests(driver storageframework.TestDriver,
e2evolume.TestVolumeClientSlow(f, testConfig, nil, "", tests)
}
// Cloning fails if the source disk is still in the process of detaching, so we wait for the VolumeAttachment to be removed before cloning.
volumeAttachment := e2evolume.GetVolumeAttachmentName(f.ClientSet, testConfig, l.testCase.Provisioner, dataSource.Name, l.sourcePVC.Namespace)
volumeAttachment := e2evolume.GetVolumeAttachmentName(f.ClientSet, testConfig, l.testCase.Provisioner, dataSourceRef.Name, l.sourcePVC.Namespace)
e2evolume.WaitForVolumeAttachmentTerminated(volumeAttachment, f.ClientSet, f.Timeouts.DataSourceProvision)
l.testCase.TestDynamicProvisioning(ctx)
})
@ -468,8 +468,8 @@ func (p *provisioningTestSuite) DefineTests(driver storageframework.TestDriver,
}
testConfig := storageframework.ConvertTestConfig(l.config)
expectedContent := fmt.Sprintf("Hello from namespace %s", f.Namespace.Name)
dataSource := preparePVCDataSourceForProvisioning(ctx, f, testConfig, l.cs, l.sourcePVC, l.sc, pattern.VolMode, expectedContent)
l.pvc.Spec.DataSource = dataSource
dataSourceRef := preparePVCDataSourceForProvisioning(ctx, f, testConfig, l.cs, l.sourcePVC, l.sc, pattern.VolMode, expectedContent)
l.pvc.Spec.DataSourceRef = dataSourceRef
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
@ -497,7 +497,7 @@ func (p *provisioningTestSuite) DefineTests(driver storageframework.TestDriver,
e2evolume.TestVolumeClientSlow(f, myTestConfig, nil, "", tests)
}
// Cloning fails if the source disk is still in the process of detaching, so we wait for the VolumeAttachment to be removed before cloning.
volumeAttachment := e2evolume.GetVolumeAttachmentName(f.ClientSet, testConfig, l.testCase.Provisioner, dataSource.Name, l.sourcePVC.Namespace)
volumeAttachment := e2evolume.GetVolumeAttachmentName(f.ClientSet, testConfig, l.testCase.Provisioner, dataSourceRef.Name, l.sourcePVC.Namespace)
e2evolume.WaitForVolumeAttachmentTerminated(volumeAttachment, f.ClientSet, f.Timeouts.DataSourceProvision)
t.TestDynamicProvisioning(ctx)
}(i)
@ -1049,7 +1049,7 @@ func prepareSnapshotDataSourceForProvisioning(
sDriver storageframework.SnapshottableTestDriver,
mode v1.PersistentVolumeMode,
injectContent string,
) *v1.TypedLocalObjectReference {
) *v1.TypedObjectReference {
SetupStorageClass(ctx, client, class)
if initClaim.ResourceVersion != "" {
@ -1078,7 +1078,7 @@ func prepareSnapshotDataSourceForProvisioning(
parameters := map[string]string{}
snapshotResource := storageframework.CreateSnapshotResource(sDriver, perTestConfig, pattern, initClaim.GetName(), initClaim.GetNamespace(), f.Timeouts, parameters)
group := "snapshot.storage.k8s.io"
dataSourceRef := &v1.TypedLocalObjectReference{
dataSourceRef := &v1.TypedObjectReference{
APIGroup: &group,
Kind: "VolumeSnapshot",
Name: snapshotResource.Vs.GetName(),
@ -1108,7 +1108,7 @@ func preparePVCDataSourceForProvisioning(
class *storagev1.StorageClass,
mode v1.PersistentVolumeMode,
injectContent string,
) *v1.TypedLocalObjectReference {
) *v1.TypedObjectReference {
SetupStorageClass(ctx, client, class)
if source.ResourceVersion != "" {
@ -1130,7 +1130,7 @@ func preparePVCDataSourceForProvisioning(
}
e2evolume.InjectContent(f, config, nil, "", tests)
dataSourceRef := &v1.TypedLocalObjectReference{
dataSourceRef := &v1.TypedObjectReference{
Kind: "PersistentVolumeClaim",
Name: source.GetName(),
}