mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Add API and validation for CrossNamespaceVolumeDataSource
This commit is contained in:
		@@ -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
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
 
 | 
			
		||||
@@ -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"))
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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},
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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(),
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user