Merge pull request #113186 from ttakahashi21/KEP-3294

Introduce APIs to support CrossNamespaceSourceProvisioning
This commit is contained in:
Kubernetes Prow Robot 2022-11-10 08:06:54 -08:00 committed by GitHub
commit d94261e904
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
90 changed files with 2269 additions and 1108 deletions

View File

@ -7014,11 +7014,11 @@
}, },
"dataSource": { "dataSource": {
"$ref": "#/definitions/io.k8s.api.core.v1.TypedLocalObjectReference", "$ref": "#/definitions/io.k8s.api.core.v1.TypedLocalObjectReference",
"description": "dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * 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." "description": "dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * 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. 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."
}, },
"dataSourceRef": { "dataSourceRef": {
"$ref": "#/definitions/io.k8s.api.core.v1.TypedLocalObjectReference", "$ref": "#/definitions/io.k8s.api.core.v1.TypedObjectReference",
"description": "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 core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the DataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, both fields (DataSource and DataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. There are two important differences between DataSource and DataSourceRef: * While DataSource only allows two specific types of objects, DataSourceRef\n allows any non-core object, as well as PersistentVolumeClaim objects.\n* While DataSource ignores disallowed values (dropping them), DataSourceRef\n preserves all values, and generates an error if a disallowed value is\n specified.\n(Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled." "description": "dataSourceRef specifies the object from which to populate the volume with data, if a non-empty 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 if both fields are non-empty, they must have the same value. For backwards 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. 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\n allows any non-core object, as well as PersistentVolumeClaim objects.\n* While dataSource ignores disallowed values (dropping them), dataSourceRef\n preserves all values, and generates an error if a disallowed value is\n specified.\n* While dataSource only allows local objects, dataSourceRef allows objects\n in any namespaces.\n(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."
}, },
"resources": { "resources": {
"$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements", "$ref": "#/definitions/io.k8s.api.core.v1.ResourceRequirements",
@ -9619,6 +9619,31 @@
"type": "object", "type": "object",
"x-kubernetes-map-type": "atomic" "x-kubernetes-map-type": "atomic"
}, },
"io.k8s.api.core.v1.TypedObjectReference": {
"properties": {
"apiGroup": {
"description": "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.",
"type": "string"
},
"kind": {
"description": "Kind is the type of resource being referenced",
"type": "string"
},
"name": {
"description": "Name is the name of resource being referenced",
"type": "string"
},
"namespace": {
"description": "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.",
"type": "string"
}
},
"required": [
"kind",
"name"
],
"type": "object"
},
"io.k8s.api.core.v1.Volume": { "io.k8s.api.core.v1.Volume": {
"description": "Volume represents a named volume in a pod that may be accessed by any container in the pod.", "description": "Volume represents a named volume in a pod that may be accessed by any container in the pod.",
"properties": { "properties": {

View File

@ -4080,15 +4080,15 @@
"$ref": "#/components/schemas/io.k8s.api.core.v1.TypedLocalObjectReference" "$ref": "#/components/schemas/io.k8s.api.core.v1.TypedLocalObjectReference"
} }
], ],
"description": "dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * 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." "description": "dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * 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. 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."
}, },
"dataSourceRef": { "dataSourceRef": {
"allOf": [ "allOf": [
{ {
"$ref": "#/components/schemas/io.k8s.api.core.v1.TypedLocalObjectReference" "$ref": "#/components/schemas/io.k8s.api.core.v1.TypedObjectReference"
} }
], ],
"description": "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 core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the DataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, both fields (DataSource and DataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. There are two important differences between DataSource and DataSourceRef: * While DataSource only allows two specific types of objects, DataSourceRef\n allows any non-core object, as well as PersistentVolumeClaim objects.\n* While DataSource ignores disallowed values (dropping them), DataSourceRef\n preserves all values, and generates an error if a disallowed value is\n specified.\n(Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled." "description": "dataSourceRef specifies the object from which to populate the volume with data, if a non-empty 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 if both fields are non-empty, they must have the same value. For backwards 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. 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\n allows any non-core object, as well as PersistentVolumeClaim objects.\n* While dataSource ignores disallowed values (dropping them), dataSourceRef\n preserves all values, and generates an error if a disallowed value is\n specified.\n* While dataSource only allows local objects, dataSourceRef allows objects\n in any namespaces.\n(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."
}, },
"resources": { "resources": {
"allOf": [ "allOf": [
@ -7359,6 +7359,33 @@
"type": "object", "type": "object",
"x-kubernetes-map-type": "atomic" "x-kubernetes-map-type": "atomic"
}, },
"io.k8s.api.core.v1.TypedObjectReference": {
"properties": {
"apiGroup": {
"description": "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.",
"type": "string"
},
"kind": {
"default": "",
"description": "Kind is the type of resource being referenced",
"type": "string"
},
"name": {
"default": "",
"description": "Name is the name of resource being referenced",
"type": "string"
},
"namespace": {
"description": "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.",
"type": "string"
}
},
"required": [
"kind",
"name"
],
"type": "object"
},
"io.k8s.api.core.v1.Volume": { "io.k8s.api.core.v1.Volume": {
"description": "Volume represents a named volume in a pod that may be accessed by any container in the pod.", "description": "Volume represents a named volume in a pod that may be accessed by any container in the pod.",
"properties": { "properties": {

View File

@ -2973,15 +2973,15 @@
"$ref": "#/components/schemas/io.k8s.api.core.v1.TypedLocalObjectReference" "$ref": "#/components/schemas/io.k8s.api.core.v1.TypedLocalObjectReference"
} }
], ],
"description": "dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * 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." "description": "dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * 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. 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."
}, },
"dataSourceRef": { "dataSourceRef": {
"allOf": [ "allOf": [
{ {
"$ref": "#/components/schemas/io.k8s.api.core.v1.TypedLocalObjectReference" "$ref": "#/components/schemas/io.k8s.api.core.v1.TypedObjectReference"
} }
], ],
"description": "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 core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the DataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, both fields (DataSource and DataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. There are two important differences between DataSource and DataSourceRef: * While DataSource only allows two specific types of objects, DataSourceRef\n allows any non-core object, as well as PersistentVolumeClaim objects.\n* While DataSource ignores disallowed values (dropping them), DataSourceRef\n preserves all values, and generates an error if a disallowed value is\n specified.\n(Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled." "description": "dataSourceRef specifies the object from which to populate the volume with data, if a non-empty 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 if both fields are non-empty, they must have the same value. For backwards 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. 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\n allows any non-core object, as well as PersistentVolumeClaim objects.\n* While dataSource ignores disallowed values (dropping them), dataSourceRef\n preserves all values, and generates an error if a disallowed value is\n specified.\n* While dataSource only allows local objects, dataSourceRef allows objects\n in any namespaces.\n(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."
}, },
"resources": { "resources": {
"allOf": [ "allOf": [
@ -4475,6 +4475,33 @@
"type": "object", "type": "object",
"x-kubernetes-map-type": "atomic" "x-kubernetes-map-type": "atomic"
}, },
"io.k8s.api.core.v1.TypedObjectReference": {
"properties": {
"apiGroup": {
"description": "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.",
"type": "string"
},
"kind": {
"default": "",
"description": "Kind is the type of resource being referenced",
"type": "string"
},
"name": {
"default": "",
"description": "Name is the name of resource being referenced",
"type": "string"
},
"namespace": {
"description": "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.",
"type": "string"
}
},
"required": [
"kind",
"name"
],
"type": "object"
},
"io.k8s.api.core.v1.Volume": { "io.k8s.api.core.v1.Volume": {
"description": "Volume represents a named volume in a pod that may be accessed by any container in the pod.", "description": "Volume represents a named volume in a pod that may be accessed by any container in the pod.",
"properties": { "properties": {

View File

@ -2207,15 +2207,15 @@
"$ref": "#/components/schemas/io.k8s.api.core.v1.TypedLocalObjectReference" "$ref": "#/components/schemas/io.k8s.api.core.v1.TypedLocalObjectReference"
} }
], ],
"description": "dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * 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." "description": "dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * 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. 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."
}, },
"dataSourceRef": { "dataSourceRef": {
"allOf": [ "allOf": [
{ {
"$ref": "#/components/schemas/io.k8s.api.core.v1.TypedLocalObjectReference" "$ref": "#/components/schemas/io.k8s.api.core.v1.TypedObjectReference"
} }
], ],
"description": "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 core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the DataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, both fields (DataSource and DataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. There are two important differences between DataSource and DataSourceRef: * While DataSource only allows two specific types of objects, DataSourceRef\n allows any non-core object, as well as PersistentVolumeClaim objects.\n* While DataSource ignores disallowed values (dropping them), DataSourceRef\n preserves all values, and generates an error if a disallowed value is\n specified.\n(Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled." "description": "dataSourceRef specifies the object from which to populate the volume with data, if a non-empty 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 if both fields are non-empty, they must have the same value. For backwards 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. 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\n allows any non-core object, as well as PersistentVolumeClaim objects.\n* While dataSource ignores disallowed values (dropping them), dataSourceRef\n preserves all values, and generates an error if a disallowed value is\n specified.\n* While dataSource only allows local objects, dataSourceRef allows objects\n in any namespaces.\n(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."
}, },
"resources": { "resources": {
"allOf": [ "allOf": [
@ -3649,6 +3649,33 @@
"type": "object", "type": "object",
"x-kubernetes-map-type": "atomic" "x-kubernetes-map-type": "atomic"
}, },
"io.k8s.api.core.v1.TypedObjectReference": {
"properties": {
"apiGroup": {
"description": "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.",
"type": "string"
},
"kind": {
"default": "",
"description": "Kind is the type of resource being referenced",
"type": "string"
},
"name": {
"default": "",
"description": "Name is the name of resource being referenced",
"type": "string"
},
"namespace": {
"description": "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.",
"type": "string"
}
},
"required": [
"kind",
"name"
],
"type": "object"
},
"io.k8s.api.core.v1.Volume": { "io.k8s.api.core.v1.Volume": {
"description": "Volume represents a named volume in a pod that may be accessed by any container in the pod.", "description": "Volume represents a named volume in a pod that may be accessed by any container in the pod.",
"properties": { "properties": {

View File

@ -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
}
}
} }
} }

View File

@ -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)
} }
}) })
} }

View File

@ -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.

View File

@ -1977,6 +1977,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil { }); err != nil {
return err return err
} }
if err := s.AddGeneratedConversionFunc((*v1.TypedObjectReference)(nil), (*core.TypedObjectReference)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_TypedObjectReference_To_core_TypedObjectReference(a.(*v1.TypedObjectReference), b.(*core.TypedObjectReference), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*core.TypedObjectReference)(nil), (*v1.TypedObjectReference)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_core_TypedObjectReference_To_v1_TypedObjectReference(a.(*core.TypedObjectReference), b.(*v1.TypedObjectReference), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.VolumeDevice)(nil), (*core.VolumeDevice)(nil), func(a, b interface{}, scope conversion.Scope) error { if err := s.AddGeneratedConversionFunc((*v1.VolumeDevice)(nil), (*core.VolumeDevice)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_VolumeDevice_To_core_VolumeDevice(a.(*v1.VolumeDevice), b.(*core.VolumeDevice), scope) return Convert_v1_VolumeDevice_To_core_VolumeDevice(a.(*v1.VolumeDevice), b.(*core.VolumeDevice), scope)
}); err != nil { }); err != nil {
@ -5182,7 +5192,7 @@ func autoConvert_v1_PersistentVolumeClaimSpec_To_core_PersistentVolumeClaimSpec(
out.StorageClassName = (*string)(unsafe.Pointer(in.StorageClassName)) out.StorageClassName = (*string)(unsafe.Pointer(in.StorageClassName))
out.VolumeMode = (*core.PersistentVolumeMode)(unsafe.Pointer(in.VolumeMode)) out.VolumeMode = (*core.PersistentVolumeMode)(unsafe.Pointer(in.VolumeMode))
out.DataSource = (*core.TypedLocalObjectReference)(unsafe.Pointer(in.DataSource)) out.DataSource = (*core.TypedLocalObjectReference)(unsafe.Pointer(in.DataSource))
out.DataSourceRef = (*core.TypedLocalObjectReference)(unsafe.Pointer(in.DataSourceRef)) out.DataSourceRef = (*core.TypedObjectReference)(unsafe.Pointer(in.DataSourceRef))
return nil return nil
} }
@ -5201,7 +5211,7 @@ func autoConvert_core_PersistentVolumeClaimSpec_To_v1_PersistentVolumeClaimSpec(
out.StorageClassName = (*string)(unsafe.Pointer(in.StorageClassName)) out.StorageClassName = (*string)(unsafe.Pointer(in.StorageClassName))
out.VolumeMode = (*v1.PersistentVolumeMode)(unsafe.Pointer(in.VolumeMode)) out.VolumeMode = (*v1.PersistentVolumeMode)(unsafe.Pointer(in.VolumeMode))
out.DataSource = (*v1.TypedLocalObjectReference)(unsafe.Pointer(in.DataSource)) out.DataSource = (*v1.TypedLocalObjectReference)(unsafe.Pointer(in.DataSource))
out.DataSourceRef = (*v1.TypedLocalObjectReference)(unsafe.Pointer(in.DataSourceRef)) out.DataSourceRef = (*v1.TypedObjectReference)(unsafe.Pointer(in.DataSourceRef))
return nil return nil
} }
@ -8083,6 +8093,32 @@ func Convert_core_TypedLocalObjectReference_To_v1_TypedLocalObjectReference(in *
return autoConvert_core_TypedLocalObjectReference_To_v1_TypedLocalObjectReference(in, out, s) return autoConvert_core_TypedLocalObjectReference_To_v1_TypedLocalObjectReference(in, out, s)
} }
func autoConvert_v1_TypedObjectReference_To_core_TypedObjectReference(in *v1.TypedObjectReference, out *core.TypedObjectReference, s conversion.Scope) error {
out.APIGroup = (*string)(unsafe.Pointer(in.APIGroup))
out.Kind = in.Kind
out.Name = in.Name
out.Namespace = (*string)(unsafe.Pointer(in.Namespace))
return nil
}
// Convert_v1_TypedObjectReference_To_core_TypedObjectReference is an autogenerated conversion function.
func Convert_v1_TypedObjectReference_To_core_TypedObjectReference(in *v1.TypedObjectReference, out *core.TypedObjectReference, s conversion.Scope) error {
return autoConvert_v1_TypedObjectReference_To_core_TypedObjectReference(in, out, s)
}
func autoConvert_core_TypedObjectReference_To_v1_TypedObjectReference(in *core.TypedObjectReference, out *v1.TypedObjectReference, s conversion.Scope) error {
out.APIGroup = (*string)(unsafe.Pointer(in.APIGroup))
out.Kind = in.Kind
out.Name = in.Name
out.Namespace = (*string)(unsafe.Pointer(in.Namespace))
return nil
}
// Convert_core_TypedObjectReference_To_v1_TypedObjectReference is an autogenerated conversion function.
func Convert_core_TypedObjectReference_To_v1_TypedObjectReference(in *core.TypedObjectReference, out *v1.TypedObjectReference, s conversion.Scope) error {
return autoConvert_core_TypedObjectReference_To_v1_TypedObjectReference(in, out, s)
}
func autoConvert_v1_Volume_To_core_Volume(in *v1.Volume, out *core.Volume, s conversion.Scope) error { func autoConvert_v1_Volume_To_core_Volume(in *v1.Volume, out *core.Volume, s conversion.Scope) error {
out.Name = in.Name out.Name = in.Name
if err := Convert_v1_VolumeSource_To_core_VolumeSource(&in.VolumeSource, &out.VolumeSource, s); err != nil { if err := Convert_v1_VolumeSource_To_core_VolumeSource(&in.VolumeSource, &out.VolumeSource, s); err != nil {

View File

@ -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"))

View File

@ -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)

View File

@ -2965,7 +2965,7 @@ func (in *PersistentVolumeClaimSpec) DeepCopyInto(out *PersistentVolumeClaimSpec
} }
if in.DataSourceRef != nil { if in.DataSourceRef != nil {
in, out := &in.DataSourceRef, &out.DataSourceRef in, out := &in.DataSourceRef, &out.DataSourceRef
*out = new(TypedLocalObjectReference) *out = new(TypedObjectReference)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
return return
@ -5714,6 +5714,32 @@ func (in *TypedLocalObjectReference) DeepCopy() *TypedLocalObjectReference {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TypedObjectReference) DeepCopyInto(out *TypedObjectReference) {
*out = *in
if in.APIGroup != nil {
in, out := &in.APIGroup, &out.APIGroup
*out = new(string)
**out = **in
}
if in.Namespace != nil {
in, out := &in.Namespace, &out.Namespace
*out = new(string)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TypedObjectReference.
func (in *TypedObjectReference) DeepCopy() *TypedObjectReference {
if in == nil {
return nil
}
out := new(TypedObjectReference)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Volume) DeepCopyInto(out *Volume) { func (in *Volume) DeepCopyInto(out *Volume) {
*out = *in *out = *in

View File

@ -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},

View File

@ -526,6 +526,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"k8s.io/api/core/v1.TopologySelectorTerm": schema_k8sio_api_core_v1_TopologySelectorTerm(ref), "k8s.io/api/core/v1.TopologySelectorTerm": schema_k8sio_api_core_v1_TopologySelectorTerm(ref),
"k8s.io/api/core/v1.TopologySpreadConstraint": schema_k8sio_api_core_v1_TopologySpreadConstraint(ref), "k8s.io/api/core/v1.TopologySpreadConstraint": schema_k8sio_api_core_v1_TopologySpreadConstraint(ref),
"k8s.io/api/core/v1.TypedLocalObjectReference": schema_k8sio_api_core_v1_TypedLocalObjectReference(ref), "k8s.io/api/core/v1.TypedLocalObjectReference": schema_k8sio_api_core_v1_TypedLocalObjectReference(ref),
"k8s.io/api/core/v1.TypedObjectReference": schema_k8sio_api_core_v1_TypedObjectReference(ref),
"k8s.io/api/core/v1.Volume": schema_k8sio_api_core_v1_Volume(ref), "k8s.io/api/core/v1.Volume": schema_k8sio_api_core_v1_Volume(ref),
"k8s.io/api/core/v1.VolumeDevice": schema_k8sio_api_core_v1_VolumeDevice(ref), "k8s.io/api/core/v1.VolumeDevice": schema_k8sio_api_core_v1_VolumeDevice(ref),
"k8s.io/api/core/v1.VolumeMount": schema_k8sio_api_core_v1_VolumeMount(ref), "k8s.io/api/core/v1.VolumeMount": schema_k8sio_api_core_v1_VolumeMount(ref),
@ -21490,21 +21491,21 @@ func schema_k8sio_api_core_v1_PersistentVolumeClaimSpec(ref common.ReferenceCall
}, },
"dataSource": { "dataSource": {
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * 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.", Description: "dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * 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. 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.",
Ref: ref("k8s.io/api/core/v1.TypedLocalObjectReference"), Ref: ref("k8s.io/api/core/v1.TypedLocalObjectReference"),
}, },
}, },
"dataSourceRef": { "dataSourceRef": {
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "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 core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the DataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, both fields (DataSource and DataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. There are two important differences between DataSource and DataSourceRef: * While DataSource only allows two specific types of objects, DataSourceRef\n allows any non-core object, as well as PersistentVolumeClaim objects.\n* While DataSource ignores disallowed values (dropping them), DataSourceRef\n preserves all values, and generates an error if a disallowed value is\n specified.\n(Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled.", Description: "dataSourceRef specifies the object from which to populate the volume with data, if a non-empty 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 if both fields are non-empty, they must have the same value. For backwards 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. 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\n allows any non-core object, as well as PersistentVolumeClaim objects.\n* While dataSource ignores disallowed values (dropping them), dataSourceRef\n preserves all values, and generates an error if a disallowed value is\n specified.\n* While dataSource only allows local objects, dataSourceRef allows objects\n in any namespaces.\n(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.",
Ref: ref("k8s.io/api/core/v1.TypedLocalObjectReference"), Ref: ref("k8s.io/api/core/v1.TypedObjectReference"),
}, },
}, },
}, },
}, },
}, },
Dependencies: []string{ Dependencies: []string{
"k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.TypedLocalObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"}, "k8s.io/api/core/v1.ResourceRequirements", "k8s.io/api/core/v1.TypedLocalObjectReference", "k8s.io/api/core/v1.TypedObjectReference", "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"},
} }
} }
@ -26813,6 +26814,49 @@ func schema_k8sio_api_core_v1_TypedLocalObjectReference(ref common.ReferenceCall
} }
} }
func schema_k8sio_api_core_v1_TypedObjectReference(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"apiGroup": {
SchemaProps: spec.SchemaProps{
Description: "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.",
Type: []string{"string"},
Format: "",
},
},
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is the type of resource being referenced",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"name": {
SchemaProps: spec.SchemaProps{
Description: "Name is the name of resource being referenced",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"namespace": {
SchemaProps: spec.SchemaProps{
Description: "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.",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"kind", "name"},
},
},
}
}
func schema_k8sio_api_core_v1_Volume(ref common.ReferenceCallback) common.OpenAPIDefinition { func schema_k8sio_api_core_v1_Volume(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{ return common.OpenAPIDefinition{
Schema: spec.Schema{ Schema: spec.Schema{

View File

@ -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

View File

@ -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

View File

@ -107,28 +107,58 @@ func TestDropConditions(t *testing.T) {
} }
var (
coreGroup = ""
snapGroup = "snapshot.storage.k8s.io"
genericGroup = "generic.storage.k8s.io"
pvcKind = "PersistentVolumeClaim"
snapKind = "VolumeSnapshot"
genericKind = "Generic"
podKind = "Pod"
)
func makeDataSource(apiGroup, kind, name string) *api.TypedLocalObjectReference {
return &api.TypedLocalObjectReference{
APIGroup: &apiGroup,
Kind: kind,
Name: name,
}
}
func makeDataSourceRef(apiGroup, kind, name string, namespace *string) *api.TypedObjectReference {
return &api.TypedObjectReference{
APIGroup: &apiGroup,
Kind: kind,
Name: name,
Namespace: namespace,
}
}
func TestPrepareForCreate(t *testing.T) { 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,

File diff suppressed because it is too large Load Diff

View File

@ -2711,30 +2711,37 @@ message PersistentVolumeClaimSpec {
// * 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
optional TypedLocalObjectReference dataSource = 7; optional TypedLocalObjectReference dataSource = 7;
// 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
optional TypedLocalObjectReference dataSourceRef = 8; optional TypedObjectReference dataSourceRef = 8;
} }
// PersistentVolumeClaimStatus is the current status of a persistent volume claim. // PersistentVolumeClaimStatus is the current status of a persistent volume claim.
@ -5563,6 +5570,27 @@ message TypedLocalObjectReference {
optional string name = 3; optional string name = 3;
} }
message TypedObjectReference {
// 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
optional string apiGroup = 1;
// Kind is the type of resource being referenced
optional string kind = 2;
// Name is the name of resource being referenced
optional string name = 3;
// 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
optional string namespace = 4;
}
// Volume represents a named volume in a pod that may be accessed by any container in the pod. // Volume represents a named volume in a pod that may be accessed by any container in the pod.
message Volume { message Volume {
// name of the volume. // name of the volume.

View File

@ -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

View File

@ -1311,8 +1311,8 @@ var map_PersistentVolumeClaimSpec = map[string]string{
"volumeName": "volumeName is the binding reference to the PersistentVolume backing this claim.", "volumeName": "volumeName is the binding reference to the PersistentVolume backing this claim.",
"storageClassName": "storageClassName is the name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1", "storageClassName": "storageClassName is the name of the StorageClass required by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1",
"volumeMode": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec.", "volumeMode": "volumeMode defines what type of volume is required by the claim. Value of Filesystem is implied when not included in claim spec.",
"dataSource": "dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * 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.", "dataSource": "dataSource field can be used to specify either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) * 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. 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.",
"dataSourceRef": "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 core object) or a PersistentVolumeClaim object. When this field is specified, volume binding will only succeed if the type of the specified object matches some installed volume populator or dynamic provisioner. This field will replace the functionality of the DataSource field and as such if both fields are non-empty, they must have the same value. For backwards compatibility, both fields (DataSource and DataSourceRef) will be set to the same value automatically if one of them is empty and the other is non-empty. There are two important differences between DataSource and DataSourceRef: * While DataSource only allows two specific types of objects, DataSourceRef\n allows any non-core object, as well as PersistentVolumeClaim objects.\n* While DataSource ignores disallowed values (dropping them), DataSourceRef\n preserves all values, and generates an error if a disallowed value is\n specified.\n(Beta) Using this field requires the AnyVolumeDataSource feature gate to be enabled.", "dataSourceRef": "dataSourceRef specifies the object from which to populate the volume with data, if a non-empty 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 if both fields are non-empty, they must have the same value. For backwards 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. 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\n allows any non-core object, as well as PersistentVolumeClaim objects.\n* While dataSource ignores disallowed values (dropping them), dataSourceRef\n preserves all values, and generates an error if a disallowed value is\n specified.\n* While dataSource only allows local objects, dataSourceRef allows objects\n in any namespaces.\n(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.",
} }
func (PersistentVolumeClaimSpec) SwaggerDoc() map[string]string { func (PersistentVolumeClaimSpec) SwaggerDoc() map[string]string {
@ -2437,6 +2437,17 @@ func (TypedLocalObjectReference) SwaggerDoc() map[string]string {
return map_TypedLocalObjectReference return map_TypedLocalObjectReference
} }
var map_TypedObjectReference = map[string]string{
"apiGroup": "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.",
"kind": "Kind is the type of resource being referenced",
"name": "Name is the name of resource being referenced",
"namespace": "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.",
}
func (TypedObjectReference) SwaggerDoc() map[string]string {
return map_TypedObjectReference
}
var map_Volume = map[string]string{ var map_Volume = map[string]string{
"": "Volume represents a named volume in a pod that may be accessed by any container in the pod.", "": "Volume represents a named volume in a pod that may be accessed by any container in the pod.",
"name": "name of the volume. Must be a DNS_LABEL and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names", "name": "name of the volume. Must be a DNS_LABEL and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names",

View File

@ -2963,7 +2963,7 @@ func (in *PersistentVolumeClaimSpec) DeepCopyInto(out *PersistentVolumeClaimSpec
} }
if in.DataSourceRef != nil { if in.DataSourceRef != nil {
in, out := &in.DataSourceRef, &out.DataSourceRef in, out := &in.DataSourceRef, &out.DataSourceRef
*out = new(TypedLocalObjectReference) *out = new(TypedObjectReference)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
return return
@ -5729,6 +5729,32 @@ func (in *TypedLocalObjectReference) DeepCopy() *TypedLocalObjectReference {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *TypedObjectReference) DeepCopyInto(out *TypedObjectReference) {
*out = *in
if in.APIGroup != nil {
in, out := &in.APIGroup, &out.APIGroup
*out = new(string)
**out = **in
}
if in.Namespace != nil {
in, out := &in.Namespace, &out.Namespace
*out = new(string)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TypedObjectReference.
func (in *TypedObjectReference) DeepCopy() *TypedObjectReference {
if in == nil {
return nil
}
out := new(TypedObjectReference)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Volume) DeepCopyInto(out *Volume) { func (in *Volume) DeepCopyInto(out *Volume) {
*out = *in *out = *in

View File

@ -465,7 +465,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -965,6 +965,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -466,7 +466,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -973,6 +973,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -467,7 +467,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -965,6 +965,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -466,7 +466,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }
@ -1716,7 +1717,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
}, },
"status": { "status": {

View File

@ -973,6 +973,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"
@ -1184,6 +1185,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -466,7 +466,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -975,6 +975,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -466,7 +466,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }
@ -1716,7 +1717,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
}, },
"status": { "status": {

View File

@ -973,6 +973,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"
@ -1184,6 +1185,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -465,7 +465,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -965,6 +965,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -466,7 +466,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -973,6 +973,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -467,7 +467,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -965,6 +965,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -466,7 +466,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }
@ -1716,7 +1717,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
}, },
"status": { "status": {

View File

@ -973,6 +973,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"
@ -1184,6 +1185,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -539,7 +539,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -1017,6 +1017,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -490,7 +490,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -981,6 +981,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -539,7 +539,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -1017,6 +1017,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -533,7 +533,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -1014,6 +1014,7 @@ template:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -80,7 +80,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
}, },
"status": { "status": {

View File

@ -43,6 +43,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -407,7 +407,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -921,6 +921,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -450,7 +450,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -954,6 +954,7 @@ template:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -456,7 +456,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -959,6 +959,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -465,7 +465,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -965,6 +965,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -466,7 +466,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -975,6 +975,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -467,7 +467,8 @@
"dataSourceRef": { "dataSourceRef": {
"apiGroup": "apiGroupValue", "apiGroup": "apiGroupValue",
"kind": "kindValue", "kind": "kindValue",
"name": "nameValue" "name": "nameValue",
"namespace": "namespaceValue"
} }
} }
} }

View File

@ -965,6 +965,7 @@ spec:
apiGroup: apiGroupValue apiGroup: apiGroupValue
kind: kindValue kind: kindValue
name: nameValue name: nameValue
namespace: namespaceValue
resources: resources:
limits: limits:
limitsKey: "0" limitsKey: "0"

View File

@ -33,7 +33,7 @@ type PersistentVolumeClaimSpecApplyConfiguration struct {
StorageClassName *string `json:"storageClassName,omitempty"` StorageClassName *string `json:"storageClassName,omitempty"`
VolumeMode *v1.PersistentVolumeMode `json:"volumeMode,omitempty"` VolumeMode *v1.PersistentVolumeMode `json:"volumeMode,omitempty"`
DataSource *TypedLocalObjectReferenceApplyConfiguration `json:"dataSource,omitempty"` DataSource *TypedLocalObjectReferenceApplyConfiguration `json:"dataSource,omitempty"`
DataSourceRef *TypedLocalObjectReferenceApplyConfiguration `json:"dataSourceRef,omitempty"` DataSourceRef *TypedObjectReferenceApplyConfiguration `json:"dataSourceRef,omitempty"`
} }
// PersistentVolumeClaimSpecApplyConfiguration constructs an declarative configuration of the PersistentVolumeClaimSpec type for use with // PersistentVolumeClaimSpecApplyConfiguration constructs an declarative configuration of the PersistentVolumeClaimSpec type for use with
@ -103,7 +103,7 @@ func (b *PersistentVolumeClaimSpecApplyConfiguration) WithDataSource(value *Type
// WithDataSourceRef sets the DataSourceRef field in the declarative configuration to the given value // WithDataSourceRef sets the DataSourceRef field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations. // and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the DataSourceRef field is set to the value of the last call. // If called multiple times, the DataSourceRef field is set to the value of the last call.
func (b *PersistentVolumeClaimSpecApplyConfiguration) WithDataSourceRef(value *TypedLocalObjectReferenceApplyConfiguration) *PersistentVolumeClaimSpecApplyConfiguration { func (b *PersistentVolumeClaimSpecApplyConfiguration) WithDataSourceRef(value *TypedObjectReferenceApplyConfiguration) *PersistentVolumeClaimSpecApplyConfiguration {
b.DataSourceRef = value b.DataSourceRef = value
return b return b
} }

View File

@ -0,0 +1,66 @@
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by applyconfiguration-gen. DO NOT EDIT.
package v1
// TypedObjectReferenceApplyConfiguration represents an declarative configuration of the TypedObjectReference type for use
// with apply.
type TypedObjectReferenceApplyConfiguration struct {
APIGroup *string `json:"apiGroup,omitempty"`
Kind *string `json:"kind,omitempty"`
Name *string `json:"name,omitempty"`
Namespace *string `json:"namespace,omitempty"`
}
// TypedObjectReferenceApplyConfiguration constructs an declarative configuration of the TypedObjectReference type for use with
// apply.
func TypedObjectReference() *TypedObjectReferenceApplyConfiguration {
return &TypedObjectReferenceApplyConfiguration{}
}
// WithAPIGroup sets the APIGroup field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the APIGroup field is set to the value of the last call.
func (b *TypedObjectReferenceApplyConfiguration) WithAPIGroup(value string) *TypedObjectReferenceApplyConfiguration {
b.APIGroup = &value
return b
}
// WithKind sets the Kind field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Kind field is set to the value of the last call.
func (b *TypedObjectReferenceApplyConfiguration) WithKind(value string) *TypedObjectReferenceApplyConfiguration {
b.Kind = &value
return b
}
// WithName sets the Name field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Name field is set to the value of the last call.
func (b *TypedObjectReferenceApplyConfiguration) WithName(value string) *TypedObjectReferenceApplyConfiguration {
b.Name = &value
return b
}
// WithNamespace sets the Namespace field in the declarative configuration to the given value
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
// If called multiple times, the Namespace field is set to the value of the last call.
func (b *TypedObjectReferenceApplyConfiguration) WithNamespace(value string) *TypedObjectReferenceApplyConfiguration {
b.Namespace = &value
return b
}

View File

@ -5544,7 +5544,7 @@ var schemaYAML = typed.YAMLObject(`types:
namedType: io.k8s.api.core.v1.TypedLocalObjectReference namedType: io.k8s.api.core.v1.TypedLocalObjectReference
- name: dataSourceRef - name: dataSourceRef
type: type:
namedType: io.k8s.api.core.v1.TypedLocalObjectReference namedType: io.k8s.api.core.v1.TypedObjectReference
- name: resources - name: resources
type: type:
namedType: io.k8s.api.core.v1.ResourceRequirements namedType: io.k8s.api.core.v1.ResourceRequirements
@ -7133,6 +7133,23 @@ var schemaYAML = typed.YAMLObject(`types:
scalar: string scalar: string
default: "" default: ""
elementRelationship: atomic elementRelationship: atomic
- name: io.k8s.api.core.v1.TypedObjectReference
map:
fields:
- name: apiGroup
type:
scalar: string
- name: kind
type:
scalar: string
default: ""
- name: name
type:
scalar: string
default: ""
- name: namespace
type:
scalar: string
- name: io.k8s.api.core.v1.Volume - name: io.k8s.api.core.v1.Volume
map: map:
fields: fields:

View File

@ -867,6 +867,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &applyconfigurationscorev1.TopologySpreadConstraintApplyConfiguration{} return &applyconfigurationscorev1.TopologySpreadConstraintApplyConfiguration{}
case corev1.SchemeGroupVersion.WithKind("TypedLocalObjectReference"): case corev1.SchemeGroupVersion.WithKind("TypedLocalObjectReference"):
return &applyconfigurationscorev1.TypedLocalObjectReferenceApplyConfiguration{} return &applyconfigurationscorev1.TypedLocalObjectReferenceApplyConfiguration{}
case corev1.SchemeGroupVersion.WithKind("TypedObjectReference"):
return &applyconfigurationscorev1.TypedObjectReferenceApplyConfiguration{}
case corev1.SchemeGroupVersion.WithKind("Volume"): case corev1.SchemeGroupVersion.WithKind("Volume"):
return &applyconfigurationscorev1.VolumeApplyConfiguration{} return &applyconfigurationscorev1.VolumeApplyConfiguration{}
case corev1.SchemeGroupVersion.WithKind("VolumeDevice"): case corev1.SchemeGroupVersion.WithKind("VolumeDevice"):

View File

@ -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)

View File

@ -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(),
} }