mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-20 18:31:15 +00:00
Add API and validation for CrossNamespaceVolumeDataSource
This commit is contained in:
parent
623376bc82
commit
87c1ca88d4
@ -32,11 +32,22 @@ const (
|
|||||||
|
|
||||||
// DropDisabledFields removes disabled fields from the pvc spec.
|
// DropDisabledFields removes disabled fields from the pvc spec.
|
||||||
// This should be called from PrepareForCreate/PrepareForUpdate for all resources containing a 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
|
// Drop the contents of the dataSourceRef field if the AnyVolumeDataSource
|
||||||
// feature gate is disabled.
|
// feature gate is disabled.
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.AnyVolumeDataSource) {
|
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
|
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
|
// NormalizeDataSources ensures that DataSource and DataSourceRef have the same contents
|
||||||
// as long as both are not explicitly set.
|
// as long as both are not explicitly set.
|
||||||
// This should be used by creates/gets of PVCs, but not updates
|
// 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 {
|
if pvcSpec.DataSource != nil && pvcSpec.DataSourceRef == nil {
|
||||||
// Using the old way of setting a data source
|
// 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 {
|
} else if pvcSpec.DataSourceRef != nil && pvcSpec.DataSource == nil {
|
||||||
// Using the new way of setting a data source
|
if pvcSpec.DataSourceRef.Namespace == nil || len(*pvcSpec.DataSourceRef.Namespace) == 0 {
|
||||||
pvcSpec.DataSource = pvcSpec.DataSourceRef.DeepCopy()
|
// 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
|
var (
|
||||||
func TestAnyDataSourceFilter(t *testing.T) {
|
coreGroup = ""
|
||||||
makeDataSource := func(apiGroup, kind, name string) *core.TypedLocalObjectReference {
|
snapGroup = "snapshot.storage.k8s.io"
|
||||||
return &core.TypedLocalObjectReference{
|
genericGroup = "generic.storage.k8s.io"
|
||||||
APIGroup: &apiGroup,
|
pvcKind = "PersistentVolumeClaim"
|
||||||
Kind: kind,
|
snapKind = "VolumeSnapshot"
|
||||||
Name: name,
|
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 {
|
var tests = map[string]struct {
|
||||||
spec core.PersistentVolumeClaimSpec
|
spec core.PersistentVolumeClaimSpec
|
||||||
|
oldSpec core.PersistentVolumeClaimSpec
|
||||||
anyEnabled bool
|
anyEnabled bool
|
||||||
|
xnsEnabled bool
|
||||||
want *core.TypedLocalObjectReference
|
want *core.TypedLocalObjectReference
|
||||||
wantRef *core.TypedLocalObjectReference
|
wantRef *core.TypedObjectReference
|
||||||
}{
|
}{
|
||||||
"any disabled with empty ds": {
|
"any disabled with empty ds": {
|
||||||
spec: core.PersistentVolumeClaimSpec{},
|
spec: core.PersistentVolumeClaimSpec{},
|
||||||
@ -180,10 +204,10 @@ func TestAnyDataSourceFilter(t *testing.T) {
|
|||||||
want: volumeDataSource,
|
want: volumeDataSource,
|
||||||
},
|
},
|
||||||
"any disabled with volume ds ref": {
|
"any disabled with volume ds ref": {
|
||||||
spec: core.PersistentVolumeClaimSpec{DataSourceRef: volumeDataSource},
|
spec: core.PersistentVolumeClaimSpec{DataSourceRef: volumeDataSourceRef},
|
||||||
},
|
},
|
||||||
"any disabled with both data sources": {
|
"any disabled with both data sources": {
|
||||||
spec: core.PersistentVolumeClaimSpec{DataSource: volumeDataSource, DataSourceRef: volumeDataSource},
|
spec: core.PersistentVolumeClaimSpec{DataSource: volumeDataSource, DataSourceRef: volumeDataSourceRef},
|
||||||
want: volumeDataSource,
|
want: volumeDataSource,
|
||||||
},
|
},
|
||||||
"any enabled with empty ds": {
|
"any enabled with empty ds": {
|
||||||
@ -196,25 +220,63 @@ func TestAnyDataSourceFilter(t *testing.T) {
|
|||||||
want: volumeDataSource,
|
want: volumeDataSource,
|
||||||
},
|
},
|
||||||
"any enabled with volume ds ref": {
|
"any enabled with volume ds ref": {
|
||||||
spec: core.PersistentVolumeClaimSpec{DataSourceRef: volumeDataSource},
|
spec: core.PersistentVolumeClaimSpec{DataSourceRef: volumeDataSourceRef},
|
||||||
anyEnabled: true,
|
anyEnabled: true,
|
||||||
wantRef: volumeDataSource,
|
wantRef: volumeDataSourceRef,
|
||||||
},
|
},
|
||||||
"any enabled with both data sources": {
|
"any enabled with both data sources": {
|
||||||
spec: core.PersistentVolumeClaimSpec{DataSource: volumeDataSource, DataSourceRef: volumeDataSource},
|
spec: core.PersistentVolumeClaimSpec{DataSource: volumeDataSource, DataSourceRef: volumeDataSourceRef},
|
||||||
anyEnabled: true,
|
anyEnabled: true,
|
||||||
want: volumeDataSource,
|
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 {
|
for testName, test := range tests {
|
||||||
t.Run(testName, func(t *testing.T) {
|
t.Run(testName, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AnyVolumeDataSource, test.anyEnabled)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AnyVolumeDataSource, test.anyEnabled)()
|
||||||
DropDisabledFields(&test.spec)
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CrossNamespaceVolumeDataSource, test.xnsEnabled)()
|
||||||
if test.spec.DataSource != test.want || test.spec.DataSourceRef != test.wantRef {
|
DropDisabledFields(&test.spec, &test.oldSpec)
|
||||||
t.Errorf("expected condition was not met, test: %s, anyEnabled: %v, spec: %v, expected: %v %v",
|
if test.spec.DataSource != test.want {
|
||||||
testName, test.anyEnabled, test.spec, test.want, test.wantRef)
|
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
|
// TestDataSourceRef checks to ensure the DataSourceRef field handles backwards
|
||||||
// compatibility with the DataSource field
|
// compatibility with the DataSource field
|
||||||
func TestDataSourceRef(t *testing.T) {
|
func TestDataSourceRef(t *testing.T) {
|
||||||
makeDataSource := func(apiGroup, kind, name string) *core.TypedLocalObjectReference {
|
ns := "ns1"
|
||||||
return &core.TypedLocalObjectReference{
|
volumeDataSource := makeDataSource(coreGroup, pvcKind, "my-vol")
|
||||||
APIGroup: &apiGroup,
|
volumeDataSourceRef := makeDataSourceRef(coreGroup, pvcKind, "my-vol", nil)
|
||||||
Kind: kind,
|
xnsVolumeDataSourceRef := makeDataSourceRef(coreGroup, pvcKind, "my-vol", &ns)
|
||||||
Name: name,
|
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")
|
||||||
volumeDataSource := makeDataSource("", "PersistentVolumeClaim", "my-vol")
|
genericDataSourceRef := makeDataSourceRef(genericGroup, genericKind, "my-foo", nil)
|
||||||
snapshotDataSource := makeDataSource("snapshot.storage.k8s.io", "VolumeSnapshot", "my-snap")
|
xnsGenericDataSourceRef := makeDataSourceRef(genericGroup, genericKind, "my-foo", &ns)
|
||||||
genericDataSource := makeDataSource("generic.storage.k8s.io", "Generic", "my-foo")
|
coreDataSource := makeDataSource(coreGroup, podKind, "my-pod")
|
||||||
coreDataSource := makeDataSource("", "Pod", "my-pod")
|
coreDataSourceRef := makeDataSourceRef(coreGroup, podKind, "my-pod", nil)
|
||||||
|
xnsCoreDataSourceRef := makeDataSourceRef(coreGroup, podKind, "my-pod", &ns)
|
||||||
|
|
||||||
var tests = map[string]struct {
|
var tests = map[string]struct {
|
||||||
spec core.PersistentVolumeClaimSpec
|
spec core.PersistentVolumeClaimSpec
|
||||||
want *core.TypedLocalObjectReference
|
want *core.TypedLocalObjectReference
|
||||||
|
wantRef *core.TypedObjectReference
|
||||||
}{
|
}{
|
||||||
"empty ds": {
|
"empty ds": {
|
||||||
spec: core.PersistentVolumeClaimSpec{},
|
spec: core.PersistentVolumeClaimSpec{},
|
||||||
},
|
},
|
||||||
"volume ds": {
|
"volume ds": {
|
||||||
spec: core.PersistentVolumeClaimSpec{DataSource: volumeDataSource},
|
spec: core.PersistentVolumeClaimSpec{DataSource: volumeDataSource},
|
||||||
want: volumeDataSource,
|
want: volumeDataSource,
|
||||||
|
wantRef: volumeDataSourceRef,
|
||||||
},
|
},
|
||||||
"snapshot ds": {
|
"snapshot ds": {
|
||||||
spec: core.PersistentVolumeClaimSpec{DataSource: snapshotDataSource},
|
spec: core.PersistentVolumeClaimSpec{DataSource: snapshotDataSource},
|
||||||
want: snapshotDataSource,
|
want: snapshotDataSource,
|
||||||
|
wantRef: snapshotDataSourceRef,
|
||||||
},
|
},
|
||||||
"generic ds": {
|
"generic ds": {
|
||||||
spec: core.PersistentVolumeClaimSpec{DataSource: genericDataSource},
|
spec: core.PersistentVolumeClaimSpec{DataSource: genericDataSource},
|
||||||
want: genericDataSource,
|
want: genericDataSource,
|
||||||
|
wantRef: genericDataSourceRef,
|
||||||
},
|
},
|
||||||
"core ds": {
|
"core ds": {
|
||||||
spec: core.PersistentVolumeClaimSpec{DataSource: coreDataSource},
|
spec: core.PersistentVolumeClaimSpec{DataSource: coreDataSource},
|
||||||
want: coreDataSource,
|
want: coreDataSource,
|
||||||
|
wantRef: coreDataSourceRef,
|
||||||
},
|
},
|
||||||
"volume ds ref": {
|
"volume ds ref": {
|
||||||
spec: core.PersistentVolumeClaimSpec{DataSourceRef: volumeDataSource},
|
spec: core.PersistentVolumeClaimSpec{DataSourceRef: volumeDataSourceRef},
|
||||||
want: volumeDataSource,
|
want: volumeDataSource,
|
||||||
|
wantRef: volumeDataSourceRef,
|
||||||
},
|
},
|
||||||
"snapshot ds ref": {
|
"snapshot ds ref": {
|
||||||
spec: core.PersistentVolumeClaimSpec{DataSourceRef: snapshotDataSource},
|
spec: core.PersistentVolumeClaimSpec{DataSourceRef: snapshotDataSourceRef},
|
||||||
want: snapshotDataSource,
|
want: snapshotDataSource,
|
||||||
|
wantRef: snapshotDataSourceRef,
|
||||||
},
|
},
|
||||||
"generic ds ref": {
|
"generic ds ref": {
|
||||||
spec: core.PersistentVolumeClaimSpec{DataSourceRef: genericDataSource},
|
spec: core.PersistentVolumeClaimSpec{DataSourceRef: genericDataSourceRef},
|
||||||
want: genericDataSource,
|
want: genericDataSource,
|
||||||
|
wantRef: genericDataSourceRef,
|
||||||
},
|
},
|
||||||
"core ds ref": {
|
"core ds ref": {
|
||||||
spec: core.PersistentVolumeClaimSpec{DataSourceRef: coreDataSource},
|
spec: core.PersistentVolumeClaimSpec{DataSourceRef: coreDataSourceRef},
|
||||||
want: coreDataSource,
|
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.AnyVolumeDataSource, true)()
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CrossNamespaceVolumeDataSource, true)()
|
||||||
|
|
||||||
for testName, test := range tests {
|
for testName, test := range tests {
|
||||||
t.Run(testName, func(t *testing.T) {
|
t.Run(testName, func(t *testing.T) {
|
||||||
NormalizeDataSources(&test.spec)
|
NormalizeDataSources(&test.spec)
|
||||||
if !reflect.DeepEqual(test.spec.DataSource, test.want) ||
|
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.datasource: %+v, want: %+v",
|
||||||
t.Errorf("expected condition was not met, test: %s, spec: %v, expected: %v",
|
testName, test.spec.DataSource, test.want)
|
||||||
testName, test.spec, 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)
|
// * An existing PVC (PersistentVolumeClaim)
|
||||||
// If the provisioner or an external controller can support the specified data source,
|
// 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.
|
// 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
|
// When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef,
|
||||||
// the same contents as the DataSourceRef field.
|
// 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
|
// +optional
|
||||||
DataSource *TypedLocalObjectReference
|
DataSource *TypedLocalObjectReference
|
||||||
// Specifies the object from which to populate the volume with data, if a non-empty
|
// 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.
|
// core object) or a PersistentVolumeClaim object.
|
||||||
// When this field is specified, volume binding will only succeed if the type of
|
// When this field is specified, volume binding will only succeed if the type of
|
||||||
// the specified object matches some installed volume populator or dynamic
|
// the specified object matches some installed volume populator or dynamic
|
||||||
// provisioner.
|
// 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
|
// 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.
|
// value automatically if one of them is empty and the other is non-empty.
|
||||||
// There are two important differences between DataSource and DataSourceRef:
|
// When namespace is specified in dataSourceRef,
|
||||||
// * While DataSource only allows two specific types of objects, 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.
|
// 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
|
// preserves all values, and generates an error if a disallowed value is
|
||||||
// specified.
|
// 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.
|
// (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
|
// +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.
|
// PersistentVolumeClaimConditionType defines the condition of PV claim.
|
||||||
|
@ -2104,7 +2104,34 @@ func validateDataSource(dataSource *core.TypedLocalObjectReference, fldPath *fie
|
|||||||
apiGroup = *dataSource.APIGroup
|
apiGroup = *dataSource.APIGroup
|
||||||
}
|
}
|
||||||
if len(apiGroup) == 0 && dataSource.Kind != "PersistentVolumeClaim" {
|
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
|
return allErrs
|
||||||
@ -2166,10 +2193,15 @@ func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fld
|
|||||||
allErrs = append(allErrs, validateDataSource(spec.DataSource, fldPath.Child("dataSource"))...)
|
allErrs = append(allErrs, validateDataSource(spec.DataSource, fldPath.Child("dataSource"))...)
|
||||||
}
|
}
|
||||||
if spec.DataSourceRef != nil {
|
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 spec.DataSourceRef != nil && spec.DataSourceRef.Namespace != nil && len(*spec.DataSourceRef.Namespace) > 0 {
|
||||||
if !apiequality.Semantic.DeepEqual(spec.DataSource, spec.DataSourceRef) {
|
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"),
|
allErrs = append(allErrs, field.Invalid(fldPath, fldPath.Child("dataSource"),
|
||||||
"must match dataSourceRef"))
|
"must match dataSourceRef"))
|
||||||
}
|
}
|
||||||
@ -2178,6 +2210,10 @@ func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fld
|
|||||||
return allErrs
|
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
|
// ValidatePersistentVolumeClaimUpdate validates an update to a PersistentVolumeClaim
|
||||||
func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeClaim, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
|
func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeClaim, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
|
||||||
allErrs := ValidateObjectMetaUpdate(&newPvc.ObjectMeta, &oldPvc.ObjectMeta, field.NewPath("metadata"))
|
allErrs := ValidateObjectMetaUpdate(&newPvc.ObjectMeta, &oldPvc.ObjectMeta, field.NewPath("metadata"))
|
||||||
|
@ -1672,7 +1672,7 @@ func testValidatePVC(t *testing.T, ephemeral bool) {
|
|||||||
Kind: "PersistentVolumeClaim",
|
Kind: "PersistentVolumeClaim",
|
||||||
Name: "pvc1",
|
Name: "pvc1",
|
||||||
},
|
},
|
||||||
DataSourceRef: &core.TypedLocalObjectReference{
|
DataSourceRef: &core.TypedObjectReference{
|
||||||
Kind: "PersistentVolumeClaim",
|
Kind: "PersistentVolumeClaim",
|
||||||
Name: "pvc2",
|
Name: "pvc2",
|
||||||
},
|
},
|
||||||
@ -19816,7 +19816,11 @@ func testAnyDataSource(t *testing.T, ds, dsRef bool) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
if dsRef {
|
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 {
|
if !ds {
|
||||||
tc.claimSpec.DataSource = nil
|
tc.claimSpec.DataSource = nil
|
||||||
@ -19840,6 +19844,110 @@ func TestAnyDataSource(t *testing.T) {
|
|||||||
testAnyDataSource(t, true, false)
|
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) {
|
func TestValidateTopologySpreadConstraints(t *testing.T) {
|
||||||
fieldPath := field.NewPath("field")
|
fieldPath := field.NewPath("field")
|
||||||
subFldPath0 := fieldPath.Index(0)
|
subFldPath0 := fieldPath.Index(0)
|
||||||
|
@ -36,6 +36,13 @@ const (
|
|||||||
// of code conflicts because changes are more likely to be scattered
|
// of code conflicts because changes are more likely to be scattered
|
||||||
// across the file.
|
// 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
|
// owner: @bswartz
|
||||||
// alpha: v1.18
|
// alpha: v1.18
|
||||||
// beta: v1.24
|
// beta: v1.24
|
||||||
@ -893,6 +900,8 @@ func init() {
|
|||||||
// Entries are separated from each other with blank lines to avoid sweeping gofmt changes
|
// Entries are separated from each other with blank lines to avoid sweeping gofmt changes
|
||||||
// when adding or removing one entry.
|
// when adding or removing one entry.
|
||||||
var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
|
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
|
AnyVolumeDataSource: {Default: true, PreRelease: featuregate.Beta}, // on by default in 1.24
|
||||||
|
|
||||||
APISelfSubjectReview: {Default: false, PreRelease: featuregate.Alpha},
|
APISelfSubjectReview: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
@ -216,11 +216,14 @@ func TestDefaultOnReadPvc(t *testing.T) {
|
|||||||
storage, _, server := newStorage(t)
|
storage, _, server := newStorage(t)
|
||||||
defer server.Terminate(t)
|
defer server.Terminate(t)
|
||||||
defer storage.Store.DestroyFunc()
|
defer storage.Store.DestroyFunc()
|
||||||
|
|
||||||
dataSource := api.TypedLocalObjectReference{
|
dataSource := api.TypedLocalObjectReference{
|
||||||
Kind: "PersistentVolumeClaim",
|
Kind: "PersistentVolumeClaim",
|
||||||
Name: "my-pvc",
|
Name: "my-pvc",
|
||||||
}
|
}
|
||||||
|
dataSourceRef := api.TypedObjectReference{
|
||||||
|
Kind: "PersistentVolumeClaim",
|
||||||
|
Name: "my-pvc",
|
||||||
|
}
|
||||||
|
|
||||||
var tests = map[string]struct {
|
var tests = map[string]struct {
|
||||||
anyEnabled bool
|
anyEnabled bool
|
||||||
@ -278,15 +281,15 @@ func TestDefaultOnReadPvc(t *testing.T) {
|
|||||||
pvc.Spec.DataSource = dataSource.DeepCopy()
|
pvc.Spec.DataSource = dataSource.DeepCopy()
|
||||||
}
|
}
|
||||||
if test.dataSourceRef {
|
if test.dataSourceRef {
|
||||||
pvc.Spec.DataSourceRef = dataSource.DeepCopy()
|
pvc.Spec.DataSourceRef = dataSourceRef.DeepCopy()
|
||||||
}
|
}
|
||||||
var expectDataSource *api.TypedLocalObjectReference
|
var expectDataSource *api.TypedLocalObjectReference
|
||||||
if test.want {
|
if test.want {
|
||||||
expectDataSource = &dataSource
|
expectDataSource = &dataSource
|
||||||
}
|
}
|
||||||
var expectDataSourceRef *api.TypedLocalObjectReference
|
var expectDataSourceRef *api.TypedObjectReference
|
||||||
if test.wantRef {
|
if test.wantRef {
|
||||||
expectDataSourceRef = &dataSource
|
expectDataSourceRef = &dataSourceRef
|
||||||
}
|
}
|
||||||
|
|
||||||
// Method under test
|
// Method under test
|
||||||
|
@ -65,7 +65,7 @@ func (persistentvolumeclaimStrategy) GetResetFields() map[fieldpath.APIVersion]*
|
|||||||
func (persistentvolumeclaimStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
func (persistentvolumeclaimStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
|
||||||
pvc := obj.(*api.PersistentVolumeClaim)
|
pvc := obj.(*api.PersistentVolumeClaim)
|
||||||
pvc.Status = api.PersistentVolumeClaimStatus{}
|
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
|
// 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)
|
oldPvc := old.(*api.PersistentVolumeClaim)
|
||||||
newPvc.Status = oldPvc.Status
|
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
|
// 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
|
// 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) {
|
func TestPrepareForCreate(t *testing.T) {
|
||||||
ctx := genericapirequest.NewDefaultContext()
|
ctx := genericapirequest.NewDefaultContext()
|
||||||
|
|
||||||
makeDataSource := func(apiGroup, kind, name string) *api.TypedLocalObjectReference {
|
ns := "ns1"
|
||||||
return &api.TypedLocalObjectReference{
|
volumeDataSource := makeDataSource(coreGroup, pvcKind, "my-vol")
|
||||||
APIGroup: &apiGroup,
|
volumeDataSourceRef := makeDataSourceRef(coreGroup, pvcKind, "my-vol", nil)
|
||||||
Kind: kind,
|
xnsVolumeDataSourceRef := makeDataSourceRef(coreGroup, pvcKind, "my-vol", &ns)
|
||||||
Name: name,
|
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")
|
||||||
volumeDataSource := makeDataSource("", "PersistentVolumeClaim", "my-vol")
|
genericDataSourceRef := makeDataSourceRef(genericGroup, genericKind, "my-foo", nil)
|
||||||
snapshotDataSource := makeDataSource("snapshot.storage.k8s.io", "VolumeSnapshot", "my-snap")
|
xnsGenericDataSourceRef := makeDataSourceRef(genericGroup, genericKind, "my-foo", &ns)
|
||||||
genericDataSource := makeDataSource("generic.storage.k8s.io", "Generic", "my-foo")
|
coreDataSource := makeDataSource(coreGroup, podKind, "my-pod")
|
||||||
coreDataSource := makeDataSource("", "Pod", "my-pod")
|
coreDataSourceRef := makeDataSourceRef(coreGroup, podKind, "my-pod", nil)
|
||||||
|
xnsCoreDataSourceRef := makeDataSourceRef(coreGroup, podKind, "my-pod", &ns)
|
||||||
|
|
||||||
var tests = map[string]struct {
|
var tests = map[string]struct {
|
||||||
anyEnabled bool
|
anyEnabled bool
|
||||||
|
xnsEnabled bool
|
||||||
dataSource *api.TypedLocalObjectReference
|
dataSource *api.TypedLocalObjectReference
|
||||||
dataSourceRef *api.TypedLocalObjectReference
|
dataSourceRef *api.TypedObjectReference
|
||||||
want *api.TypedLocalObjectReference
|
want *api.TypedLocalObjectReference
|
||||||
wantRef *api.TypedLocalObjectReference
|
wantRef *api.TypedObjectReference
|
||||||
}{
|
}{
|
||||||
"any disabled with empty ds": {
|
"any disabled with empty ds": {
|
||||||
want: nil,
|
want: nil,
|
||||||
@ -150,16 +180,16 @@ func TestPrepareForCreate(t *testing.T) {
|
|||||||
want: nil,
|
want: nil,
|
||||||
},
|
},
|
||||||
"any disabled with volume ds ref": {
|
"any disabled with volume ds ref": {
|
||||||
dataSourceRef: volumeDataSource,
|
dataSourceRef: volumeDataSourceRef,
|
||||||
},
|
},
|
||||||
"any disabled with snapshot ds ref": {
|
"any disabled with snapshot ds ref": {
|
||||||
dataSourceRef: snapshotDataSource,
|
dataSourceRef: snapshotDataSourceRef,
|
||||||
},
|
},
|
||||||
"any disabled with generic ds ref": {
|
"any disabled with generic ds ref": {
|
||||||
dataSourceRef: genericDataSource,
|
dataSourceRef: genericDataSourceRef,
|
||||||
},
|
},
|
||||||
"any disabled with invalid ds ref": {
|
"any disabled with invalid ds ref": {
|
||||||
dataSourceRef: coreDataSource,
|
dataSourceRef: coreDataSourceRef,
|
||||||
},
|
},
|
||||||
"any enabled with empty ds": {
|
"any enabled with empty ds": {
|
||||||
anyEnabled: true,
|
anyEnabled: true,
|
||||||
@ -169,13 +199,13 @@ func TestPrepareForCreate(t *testing.T) {
|
|||||||
dataSource: volumeDataSource,
|
dataSource: volumeDataSource,
|
||||||
anyEnabled: true,
|
anyEnabled: true,
|
||||||
want: volumeDataSource,
|
want: volumeDataSource,
|
||||||
wantRef: volumeDataSource,
|
wantRef: volumeDataSourceRef,
|
||||||
},
|
},
|
||||||
"any enabled with snapshot ds": {
|
"any enabled with snapshot ds": {
|
||||||
dataSource: snapshotDataSource,
|
dataSource: snapshotDataSource,
|
||||||
anyEnabled: true,
|
anyEnabled: true,
|
||||||
want: snapshotDataSource,
|
want: snapshotDataSource,
|
||||||
wantRef: snapshotDataSource,
|
wantRef: snapshotDataSourceRef,
|
||||||
},
|
},
|
||||||
"any enabled with generic ds": {
|
"any enabled with generic ds": {
|
||||||
dataSource: genericDataSource,
|
dataSource: genericDataSource,
|
||||||
@ -186,41 +216,135 @@ func TestPrepareForCreate(t *testing.T) {
|
|||||||
anyEnabled: true,
|
anyEnabled: true,
|
||||||
},
|
},
|
||||||
"any enabled with volume ds ref": {
|
"any enabled with volume ds ref": {
|
||||||
dataSourceRef: volumeDataSource,
|
dataSourceRef: volumeDataSourceRef,
|
||||||
anyEnabled: true,
|
anyEnabled: true,
|
||||||
want: volumeDataSource,
|
want: volumeDataSource,
|
||||||
wantRef: volumeDataSource,
|
wantRef: volumeDataSourceRef,
|
||||||
},
|
},
|
||||||
"any enabled with snapshot ds ref": {
|
"any enabled with snapshot ds ref": {
|
||||||
dataSourceRef: snapshotDataSource,
|
dataSourceRef: snapshotDataSourceRef,
|
||||||
anyEnabled: true,
|
anyEnabled: true,
|
||||||
want: snapshotDataSource,
|
want: snapshotDataSource,
|
||||||
wantRef: snapshotDataSource,
|
wantRef: snapshotDataSourceRef,
|
||||||
},
|
},
|
||||||
"any enabled with generic ds ref": {
|
"any enabled with generic ds ref": {
|
||||||
dataSourceRef: genericDataSource,
|
dataSourceRef: genericDataSourceRef,
|
||||||
anyEnabled: true,
|
anyEnabled: true,
|
||||||
want: genericDataSource,
|
want: genericDataSource,
|
||||||
wantRef: genericDataSource,
|
wantRef: genericDataSourceRef,
|
||||||
},
|
},
|
||||||
"any enabled with invalid ds ref": {
|
"any enabled with invalid ds ref": {
|
||||||
dataSourceRef: coreDataSource,
|
dataSourceRef: coreDataSourceRef,
|
||||||
anyEnabled: true,
|
anyEnabled: true,
|
||||||
want: coreDataSource,
|
want: coreDataSource,
|
||||||
wantRef: coreDataSource,
|
wantRef: coreDataSourceRef,
|
||||||
},
|
},
|
||||||
"any enabled with mismatched data sources": {
|
"any enabled with mismatched data sources": {
|
||||||
dataSource: volumeDataSource,
|
dataSource: volumeDataSource,
|
||||||
dataSourceRef: snapshotDataSource,
|
dataSourceRef: snapshotDataSourceRef,
|
||||||
anyEnabled: true,
|
anyEnabled: true,
|
||||||
want: volumeDataSource,
|
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 {
|
for testName, test := range tests {
|
||||||
t.Run(testName, func(t *testing.T) {
|
t.Run(testName, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AnyVolumeDataSource, test.anyEnabled)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AnyVolumeDataSource, test.anyEnabled)()
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.CrossNamespaceVolumeDataSource, test.xnsEnabled)()
|
||||||
pvc := api.PersistentVolumeClaim{
|
pvc := api.PersistentVolumeClaim{
|
||||||
Spec: api.PersistentVolumeClaimSpec{
|
Spec: api.PersistentVolumeClaimSpec{
|
||||||
DataSource: test.dataSource,
|
DataSource: test.dataSource,
|
||||||
|
@ -497,29 +497,54 @@ type PersistentVolumeClaimSpec struct {
|
|||||||
// * An existing PVC (PersistentVolumeClaim)
|
// * An existing PVC (PersistentVolumeClaim)
|
||||||
// If the provisioner or an external controller can support the specified data source,
|
// 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.
|
// 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
|
// When the AnyVolumeDataSource feature gate is enabled, dataSource contents will be copied to dataSourceRef,
|
||||||
// the same contents as the DataSourceRef field.
|
// 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
|
// +optional
|
||||||
DataSource *TypedLocalObjectReference `json:"dataSource,omitempty" protobuf:"bytes,7,opt,name=dataSource"`
|
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
|
// 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.
|
// core object) or a PersistentVolumeClaim object.
|
||||||
// When this field is specified, volume binding will only succeed if the type of
|
// When this field is specified, volume binding will only succeed if the type of
|
||||||
// the specified object matches some installed volume populator or dynamic
|
// the specified object matches some installed volume populator or dynamic
|
||||||
// provisioner.
|
// 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
|
// 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.
|
// value automatically if one of them is empty and the other is non-empty.
|
||||||
// There are two important differences between DataSource and DataSourceRef:
|
// When namespace is specified in dataSourceRef,
|
||||||
// * While DataSource only allows two specific types of objects, 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.
|
// 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
|
// preserves all values, and generates an error if a disallowed value is
|
||||||
// specified.
|
// 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.
|
// (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
|
// +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
|
// 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)
|
testConfig := storageframework.ConvertTestConfig(l.config)
|
||||||
dc := l.config.Framework.DynamicClient
|
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
|
// Create 2nd PVC for testing
|
||||||
pvc2 := &v1.PersistentVolumeClaim{
|
pvc2 := &v1.PersistentVolumeClaim{
|
||||||
@ -353,7 +353,7 @@ func (t *multiVolumeTestSuite) DefineTests(driver storageframework.TestDriver, p
|
|||||||
}
|
}
|
||||||
resource.Pvc.Spec.DeepCopyInto(&pvc2.Spec)
|
resource.Pvc.Spec.DeepCopyInto(&pvc2.Spec)
|
||||||
pvc2.Spec.VolumeName = ""
|
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{})
|
pvc2, err := l.cs.CoreV1().PersistentVolumeClaims(pvc2.Namespace).Create(context.TODO(), pvc2, metav1.CreateOptions{})
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
@ -386,7 +386,7 @@ func (t *multiVolumeTestSuite) DefineTests(driver storageframework.TestDriver, p
|
|||||||
l.resources = append(l.resources, resource)
|
l.resources = append(l.resources, resource)
|
||||||
pvcs := []*v1.PersistentVolumeClaim{resource.Pvc}
|
pvcs := []*v1.PersistentVolumeClaim{resource.Pvc}
|
||||||
testConfig := storageframework.ConvertTestConfig(l.config)
|
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
|
// Create 2nd PVC for testing
|
||||||
pvc2 := &v1.PersistentVolumeClaim{
|
pvc2 := &v1.PersistentVolumeClaim{
|
||||||
@ -397,7 +397,7 @@ func (t *multiVolumeTestSuite) DefineTests(driver storageframework.TestDriver, p
|
|||||||
}
|
}
|
||||||
resource.Pvc.Spec.DeepCopyInto(&pvc2.Spec)
|
resource.Pvc.Spec.DeepCopyInto(&pvc2.Spec)
|
||||||
pvc2.Spec.VolumeName = ""
|
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{})
|
pvc2, err := l.cs.CoreV1().PersistentVolumeClaims(pvc2.Namespace).Create(context.TODO(), pvc2, metav1.CreateOptions{})
|
||||||
framework.ExpectNoError(err)
|
framework.ExpectNoError(err)
|
||||||
|
@ -214,9 +214,9 @@ func (p *provisioningTestSuite) DefineTests(driver storageframework.TestDriver,
|
|||||||
dc := l.config.Framework.DynamicClient
|
dc := l.config.Framework.DynamicClient
|
||||||
testConfig := storageframework.ConvertTestConfig(l.config)
|
testConfig := storageframework.ConvertTestConfig(l.config)
|
||||||
expectedContent := fmt.Sprintf("Hello from namespace %s", f.Namespace.Name)
|
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) {
|
l.testCase.PvCheck = func(claim *v1.PersistentVolumeClaim) {
|
||||||
ginkgo.By("checking whether the created volume has the pre-populated data")
|
ginkgo.By("checking whether the created volume has the pre-populated data")
|
||||||
tests := []e2evolume.Test{
|
tests := []e2evolume.Test{
|
||||||
@ -386,7 +386,7 @@ func (p *provisioningTestSuite) DefineTests(driver storageframework.TestDriver,
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
apiGroup := "hello.example.com"
|
apiGroup := "hello.example.com"
|
||||||
l.pvc.Spec.DataSourceRef = &v1.TypedLocalObjectReference{
|
l.pvc.Spec.DataSourceRef = &v1.TypedObjectReference{
|
||||||
APIGroup: &apiGroup,
|
APIGroup: &apiGroup,
|
||||||
Kind: "Hello",
|
Kind: "Hello",
|
||||||
Name: helloCRName,
|
Name: helloCRName,
|
||||||
@ -427,8 +427,8 @@ func (p *provisioningTestSuite) DefineTests(driver storageframework.TestDriver,
|
|||||||
}
|
}
|
||||||
testConfig := storageframework.ConvertTestConfig(l.config)
|
testConfig := storageframework.ConvertTestConfig(l.config)
|
||||||
expectedContent := fmt.Sprintf("Hello from namespace %s", f.Namespace.Name)
|
expectedContent := fmt.Sprintf("Hello from namespace %s", f.Namespace.Name)
|
||||||
dataSource := preparePVCDataSourceForProvisioning(ctx, f, testConfig, l.cs, l.sourcePVC, l.sc, pattern.VolMode, expectedContent)
|
dataSourceRef := preparePVCDataSourceForProvisioning(ctx, f, testConfig, l.cs, l.sourcePVC, l.sc, pattern.VolMode, expectedContent)
|
||||||
l.pvc.Spec.DataSource = dataSource
|
l.pvc.Spec.DataSourceRef = dataSourceRef
|
||||||
l.testCase.NodeSelection = testConfig.ClientNodeSelection
|
l.testCase.NodeSelection = testConfig.ClientNodeSelection
|
||||||
l.testCase.PvCheck = func(claim *v1.PersistentVolumeClaim) {
|
l.testCase.PvCheck = func(claim *v1.PersistentVolumeClaim) {
|
||||||
ginkgo.By("checking whether the created volume has the pre-populated data")
|
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)
|
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.
|
// 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)
|
e2evolume.WaitForVolumeAttachmentTerminated(volumeAttachment, f.ClientSet, f.Timeouts.DataSourceProvision)
|
||||||
l.testCase.TestDynamicProvisioning(ctx)
|
l.testCase.TestDynamicProvisioning(ctx)
|
||||||
})
|
})
|
||||||
@ -468,8 +468,8 @@ func (p *provisioningTestSuite) DefineTests(driver storageframework.TestDriver,
|
|||||||
}
|
}
|
||||||
testConfig := storageframework.ConvertTestConfig(l.config)
|
testConfig := storageframework.ConvertTestConfig(l.config)
|
||||||
expectedContent := fmt.Sprintf("Hello from namespace %s", f.Namespace.Name)
|
expectedContent := fmt.Sprintf("Hello from namespace %s", f.Namespace.Name)
|
||||||
dataSource := preparePVCDataSourceForProvisioning(ctx, f, testConfig, l.cs, l.sourcePVC, l.sc, pattern.VolMode, expectedContent)
|
dataSourceRef := preparePVCDataSourceForProvisioning(ctx, f, testConfig, l.cs, l.sourcePVC, l.sc, pattern.VolMode, expectedContent)
|
||||||
l.pvc.Spec.DataSource = dataSource
|
l.pvc.Spec.DataSourceRef = dataSourceRef
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
@ -497,7 +497,7 @@ func (p *provisioningTestSuite) DefineTests(driver storageframework.TestDriver,
|
|||||||
e2evolume.TestVolumeClientSlow(f, myTestConfig, nil, "", tests)
|
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.
|
// 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)
|
e2evolume.WaitForVolumeAttachmentTerminated(volumeAttachment, f.ClientSet, f.Timeouts.DataSourceProvision)
|
||||||
t.TestDynamicProvisioning(ctx)
|
t.TestDynamicProvisioning(ctx)
|
||||||
}(i)
|
}(i)
|
||||||
@ -1049,7 +1049,7 @@ func prepareSnapshotDataSourceForProvisioning(
|
|||||||
sDriver storageframework.SnapshottableTestDriver,
|
sDriver storageframework.SnapshottableTestDriver,
|
||||||
mode v1.PersistentVolumeMode,
|
mode v1.PersistentVolumeMode,
|
||||||
injectContent string,
|
injectContent string,
|
||||||
) *v1.TypedLocalObjectReference {
|
) *v1.TypedObjectReference {
|
||||||
SetupStorageClass(ctx, client, class)
|
SetupStorageClass(ctx, client, class)
|
||||||
|
|
||||||
if initClaim.ResourceVersion != "" {
|
if initClaim.ResourceVersion != "" {
|
||||||
@ -1078,7 +1078,7 @@ func prepareSnapshotDataSourceForProvisioning(
|
|||||||
parameters := map[string]string{}
|
parameters := map[string]string{}
|
||||||
snapshotResource := storageframework.CreateSnapshotResource(sDriver, perTestConfig, pattern, initClaim.GetName(), initClaim.GetNamespace(), f.Timeouts, parameters)
|
snapshotResource := storageframework.CreateSnapshotResource(sDriver, perTestConfig, pattern, initClaim.GetName(), initClaim.GetNamespace(), f.Timeouts, parameters)
|
||||||
group := "snapshot.storage.k8s.io"
|
group := "snapshot.storage.k8s.io"
|
||||||
dataSourceRef := &v1.TypedLocalObjectReference{
|
dataSourceRef := &v1.TypedObjectReference{
|
||||||
APIGroup: &group,
|
APIGroup: &group,
|
||||||
Kind: "VolumeSnapshot",
|
Kind: "VolumeSnapshot",
|
||||||
Name: snapshotResource.Vs.GetName(),
|
Name: snapshotResource.Vs.GetName(),
|
||||||
@ -1108,7 +1108,7 @@ func preparePVCDataSourceForProvisioning(
|
|||||||
class *storagev1.StorageClass,
|
class *storagev1.StorageClass,
|
||||||
mode v1.PersistentVolumeMode,
|
mode v1.PersistentVolumeMode,
|
||||||
injectContent string,
|
injectContent string,
|
||||||
) *v1.TypedLocalObjectReference {
|
) *v1.TypedObjectReference {
|
||||||
SetupStorageClass(ctx, client, class)
|
SetupStorageClass(ctx, client, class)
|
||||||
|
|
||||||
if source.ResourceVersion != "" {
|
if source.ResourceVersion != "" {
|
||||||
@ -1130,7 +1130,7 @@ func preparePVCDataSourceForProvisioning(
|
|||||||
}
|
}
|
||||||
e2evolume.InjectContent(f, config, nil, "", tests)
|
e2evolume.InjectContent(f, config, nil, "", tests)
|
||||||
|
|
||||||
dataSourceRef := &v1.TypedLocalObjectReference{
|
dataSourceRef := &v1.TypedObjectReference{
|
||||||
Kind: "PersistentVolumeClaim",
|
Kind: "PersistentVolumeClaim",
|
||||||
Name: source.GetName(),
|
Name: source.GetName(),
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user