Merge pull request #128240 from LionelJouin/KEP-4817

DRA: Implementation of ResourceClaim.Status.Devices (KEP-4817)
This commit is contained in:
Kubernetes Prow Robot
2024-11-08 02:21:24 +00:00
committed by GitHub
47 changed files with 4913 additions and 364 deletions

View File

@@ -56,10 +56,12 @@ API rule violation: names_match,k8s.io/api/resource/v1alpha3,DeviceAttribute,Boo
API rule violation: names_match,k8s.io/api/resource/v1alpha3,DeviceAttribute,IntValue
API rule violation: names_match,k8s.io/api/resource/v1alpha3,DeviceAttribute,StringValue
API rule violation: names_match,k8s.io/api/resource/v1alpha3,DeviceAttribute,VersionValue
API rule violation: names_match,k8s.io/api/resource/v1alpha3,NetworkDeviceData,IPs
API rule violation: names_match,k8s.io/api/resource/v1beta1,DeviceAttribute,BoolValue
API rule violation: names_match,k8s.io/api/resource/v1beta1,DeviceAttribute,IntValue
API rule violation: names_match,k8s.io/api/resource/v1beta1,DeviceAttribute,StringValue
API rule violation: names_match,k8s.io/api/resource/v1beta1,DeviceAttribute,VersionValue
API rule violation: names_match,k8s.io/api/resource/v1beta1,NetworkDeviceData,IPs
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,JSONSchemaProps,Ref
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,JSONSchemaProps,Schema
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1,JSONSchemaProps,XEmbeddedResource

View File

@@ -14521,6 +14521,48 @@
"type": "object",
"x-kubernetes-map-type": "atomic"
},
"io.k8s.api.resource.v1alpha3.AllocatedDeviceStatus": {
"description": "AllocatedDeviceStatus contains the status of an allocated device, if the driver chooses to report it. This may include driver-specific information.",
"properties": {
"conditions": {
"description": "Conditions contains the latest observation of the device's state. If the device has been configured according to the class and claim config references, the `Ready` condition should be True.",
"items": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Condition"
},
"type": "array",
"x-kubernetes-list-map-keys": [
"type"
],
"x-kubernetes-list-type": "map"
},
"data": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.runtime.RawExtension",
"description": "Data contains arbitrary driver-specific data.\n\nThe length of the raw data must be smaller or equal to 10 Ki."
},
"device": {
"description": "Device references one device instance via its name in the driver's resource pool. It must be a DNS label.",
"type": "string"
},
"driver": {
"description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.",
"type": "string"
},
"networkData": {
"$ref": "#/definitions/io.k8s.api.resource.v1alpha3.NetworkDeviceData",
"description": "NetworkData contains network-related information specific to the device."
},
"pool": {
"description": "This name together with the driver name and the device name field identify which device was allocated (`<driver name>/<pool name>/<device name>`).\n\nMust not be longer than 253 characters and may contain one or more DNS sub-domains separated by slashes.",
"type": "string"
}
},
"required": [
"driver",
"pool",
"device"
],
"type": "object"
},
"io.k8s.api.resource.v1alpha3.AllocationResult": {
"description": "AllocationResult contains attributes of an allocated resource.",
"properties": {
@@ -14901,6 +14943,28 @@
},
"type": "object"
},
"io.k8s.api.resource.v1alpha3.NetworkDeviceData": {
"description": "NetworkDeviceData provides network-related details for the allocated device. This information may be filled by drivers or other components to configure or identify the device within a network context.",
"properties": {
"hardwareAddress": {
"description": "HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface.\n\nMust not be longer than 128 characters.",
"type": "string"
},
"interfaceName": {
"description": "InterfaceName specifies the name of the network interface associated with the allocated device. This might be the name of a physical or virtual network interface being configured in the pod.\n\nMust not be longer than 256 characters.",
"type": "string"
},
"ips": {
"description": "IPs lists the network addresses assigned to the device's network interface. This can include both IPv4 and IPv6 addresses. The IPs are in the CIDR notation, which includes both the address and the associated subnet mask. e.g.: \"192.0.2.5/24\" for IPv4 and \"2001:db8::5/64\" for IPv6.",
"items": {
"type": "string"
},
"type": "array",
"x-kubernetes-list-type": "atomic"
}
},
"type": "object"
},
"io.k8s.api.resource.v1alpha3.OpaqueDeviceConfiguration": {
"description": "OpaqueDeviceConfiguration contains configuration parameters for a driver in a format defined by the driver vendor.",
"properties": {
@@ -15034,6 +15098,19 @@
"$ref": "#/definitions/io.k8s.api.resource.v1alpha3.AllocationResult",
"description": "Allocation is set once the claim has been allocated successfully."
},
"devices": {
"description": "Devices contains the status of each device allocated for this claim, as reported by the driver. This can include driver-specific information. Entries are owned by their respective drivers.",
"items": {
"$ref": "#/definitions/io.k8s.api.resource.v1alpha3.AllocatedDeviceStatus"
},
"type": "array",
"x-kubernetes-list-map-keys": [
"driver",
"device",
"pool"
],
"x-kubernetes-list-type": "map"
},
"reservedFor": {
"description": "ReservedFor indicates which entities are currently allowed to use the claim. A Pod which references a ResourceClaim which is not reserved for that Pod will not be started. A claim that is in use or might be in use because it has been reserved must not get deallocated.\n\nIn a cluster with multiple scheduler instances, two pods might get scheduled concurrently by different schedulers. When they reference the same ResourceClaim which already has reached its maximum number of consumers, only one pod can be scheduled.\n\nBoth schedulers try to add their pod to the claim.status.reservedFor field, but only the update that reaches the API server first gets stored. The other one fails with an error and the scheduler which issued it knows that it must put the pod back into the queue, waiting for the ResourceClaim to become usable again.\n\nThere can be at most 32 such reservations. This may get increased in the future, but not reduced.",
"items": {
@@ -15264,6 +15341,48 @@
],
"type": "object"
},
"io.k8s.api.resource.v1beta1.AllocatedDeviceStatus": {
"description": "AllocatedDeviceStatus contains the status of an allocated device, if the driver chooses to report it. This may include driver-specific information.",
"properties": {
"conditions": {
"description": "Conditions contains the latest observation of the device's state. If the device has been configured according to the class and claim config references, the `Ready` condition should be True.",
"items": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Condition"
},
"type": "array",
"x-kubernetes-list-map-keys": [
"type"
],
"x-kubernetes-list-type": "map"
},
"data": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.runtime.RawExtension",
"description": "Data contains arbitrary driver-specific data.\n\nThe length of the raw data must be smaller or equal to 10 Ki."
},
"device": {
"description": "Device references one device instance via its name in the driver's resource pool. It must be a DNS label.",
"type": "string"
},
"driver": {
"description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.",
"type": "string"
},
"networkData": {
"$ref": "#/definitions/io.k8s.api.resource.v1beta1.NetworkDeviceData",
"description": "NetworkData contains network-related information specific to the device."
},
"pool": {
"description": "This name together with the driver name and the device name field identify which device was allocated (`<driver name>/<pool name>/<device name>`).\n\nMust not be longer than 253 characters and may contain one or more DNS sub-domains separated by slashes.",
"type": "string"
}
},
"required": [
"driver",
"pool",
"device"
],
"type": "object"
},
"io.k8s.api.resource.v1beta1.AllocationResult": {
"description": "AllocationResult contains attributes of an allocated resource.",
"properties": {
@@ -15657,6 +15776,28 @@
},
"type": "object"
},
"io.k8s.api.resource.v1beta1.NetworkDeviceData": {
"description": "NetworkDeviceData provides network-related details for the allocated device. This information may be filled by drivers or other components to configure or identify the device within a network context.",
"properties": {
"hardwareAddress": {
"description": "HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface.\n\nMust not be longer than 128 characters.",
"type": "string"
},
"interfaceName": {
"description": "InterfaceName specifies the name of the network interface associated with the allocated device. This might be the name of a physical or virtual network interface being configured in the pod.\n\nMust not be longer than 256 characters.",
"type": "string"
},
"ips": {
"description": "IPs lists the network addresses assigned to the device's network interface. This can include both IPv4 and IPv6 addresses. The IPs are in the CIDR notation, which includes both the address and the associated subnet mask. e.g.: \"192.0.2.5/24\" for IPv4 and \"2001:db8::5/64\" for IPv6.",
"items": {
"type": "string"
},
"type": "array",
"x-kubernetes-list-type": "atomic"
}
},
"type": "object"
},
"io.k8s.api.resource.v1beta1.OpaqueDeviceConfiguration": {
"description": "OpaqueDeviceConfiguration contains configuration parameters for a driver in a format defined by the driver vendor.",
"properties": {
@@ -15790,6 +15931,19 @@
"$ref": "#/definitions/io.k8s.api.resource.v1beta1.AllocationResult",
"description": "Allocation is set once the claim has been allocated successfully."
},
"devices": {
"description": "Devices contains the status of each device allocated for this claim, as reported by the driver. This can include driver-specific information. Entries are owned by their respective drivers.",
"items": {
"$ref": "#/definitions/io.k8s.api.resource.v1beta1.AllocatedDeviceStatus"
},
"type": "array",
"x-kubernetes-list-map-keys": [
"driver",
"device",
"pool"
],
"x-kubernetes-list-type": "map"
},
"reservedFor": {
"description": "ReservedFor indicates which entities are currently allowed to use the claim. A Pod which references a ResourceClaim which is not reserved for that Pod will not be started. A claim that is in use or might be in use because it has been reserved must not get deallocated.\n\nIn a cluster with multiple scheduler instances, two pods might get scheduled concurrently by different schedulers. When they reference the same ResourceClaim which already has reached its maximum number of consumers, only one pod can be scheduled.\n\nBoth schedulers try to add their pod to the claim.status.reservedFor field, but only the update that reaches the API server first gets stored. The other one fails with an error and the scheduler which issued it knows that it must put the pod back into the queue, waiting for the ResourceClaim to become usable again.\n\nThere can be at most 32 such reservations. This may get increased in the future, but not reduced.",
"items": {

View File

@@ -86,6 +86,64 @@
"type": "object",
"x-kubernetes-map-type": "atomic"
},
"io.k8s.api.resource.v1alpha3.AllocatedDeviceStatus": {
"description": "AllocatedDeviceStatus contains the status of an allocated device, if the driver chooses to report it. This may include driver-specific information.",
"properties": {
"conditions": {
"description": "Conditions contains the latest observation of the device's state. If the device has been configured according to the class and claim config references, the `Ready` condition should be True.",
"items": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Condition"
}
],
"default": {}
},
"type": "array",
"x-kubernetes-list-map-keys": [
"type"
],
"x-kubernetes-list-type": "map"
},
"data": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.runtime.RawExtension"
}
],
"description": "Data contains arbitrary driver-specific data.\n\nThe length of the raw data must be smaller or equal to 10 Ki."
},
"device": {
"default": "",
"description": "Device references one device instance via its name in the driver's resource pool. It must be a DNS label.",
"type": "string"
},
"driver": {
"default": "",
"description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.",
"type": "string"
},
"networkData": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.NetworkDeviceData"
}
],
"description": "NetworkData contains network-related information specific to the device."
},
"pool": {
"default": "",
"description": "This name together with the driver name and the device name field identify which device was allocated (`<driver name>/<pool name>/<device name>`).\n\nMust not be longer than 253 characters and may contain one or more DNS sub-domains separated by slashes.",
"type": "string"
}
},
"required": [
"driver",
"pool",
"device"
],
"type": "object"
},
"io.k8s.api.resource.v1alpha3.AllocationResult": {
"description": "AllocationResult contains attributes of an allocated resource.",
"properties": {
@@ -572,6 +630,29 @@
},
"type": "object"
},
"io.k8s.api.resource.v1alpha3.NetworkDeviceData": {
"description": "NetworkDeviceData provides network-related details for the allocated device. This information may be filled by drivers or other components to configure or identify the device within a network context.",
"properties": {
"hardwareAddress": {
"description": "HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface.\n\nMust not be longer than 128 characters.",
"type": "string"
},
"interfaceName": {
"description": "InterfaceName specifies the name of the network interface associated with the allocated device. This might be the name of a physical or virtual network interface being configured in the pod.\n\nMust not be longer than 256 characters.",
"type": "string"
},
"ips": {
"description": "IPs lists the network addresses assigned to the device's network interface. This can include both IPv4 and IPv6 addresses. The IPs are in the CIDR notation, which includes both the address and the associated subnet mask. e.g.: \"192.0.2.5/24\" for IPv4 and \"2001:db8::5/64\" for IPv6.",
"items": {
"default": "",
"type": "string"
},
"type": "array",
"x-kubernetes-list-type": "atomic"
}
},
"type": "object"
},
"io.k8s.api.resource.v1alpha3.OpaqueDeviceConfiguration": {
"description": "OpaqueDeviceConfiguration contains configuration parameters for a driver in a format defined by the driver vendor.",
"properties": {
@@ -747,6 +828,24 @@
],
"description": "Allocation is set once the claim has been allocated successfully."
},
"devices": {
"description": "Devices contains the status of each device allocated for this claim, as reported by the driver. This can include driver-specific information. Entries are owned by their respective drivers.",
"items": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.AllocatedDeviceStatus"
}
],
"default": {}
},
"type": "array",
"x-kubernetes-list-map-keys": [
"driver",
"device",
"pool"
],
"x-kubernetes-list-type": "map"
},
"reservedFor": {
"description": "ReservedFor indicates which entities are currently allowed to use the claim. A Pod which references a ResourceClaim which is not reserved for that Pod will not be started. A claim that is in use or might be in use because it has been reserved must not get deallocated.\n\nIn a cluster with multiple scheduler instances, two pods might get scheduled concurrently by different schedulers. When they reference the same ResourceClaim which already has reached its maximum number of consumers, only one pod can be scheduled.\n\nBoth schedulers try to add their pod to the claim.status.reservedFor field, but only the update that reaches the API server first gets stored. The other one fails with an error and the scheduler which issued it knows that it must put the pod back into the queue, waiting for the ResourceClaim to become usable again.\n\nThere can be at most 32 such reservations. This may get increased in the future, but not reduced.",
"items": {
@@ -1175,6 +1274,52 @@
}
]
},
"io.k8s.apimachinery.pkg.apis.meta.v1.Condition": {
"description": "Condition contains details for one aspect of the current state of this API Resource.",
"properties": {
"lastTransitionTime": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
}
],
"description": "lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable."
},
"message": {
"default": "",
"description": "message is a human readable message indicating details about the transition. This may be an empty string.",
"type": "string"
},
"observedGeneration": {
"description": "observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.",
"format": "int64",
"type": "integer"
},
"reason": {
"default": "",
"description": "reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.",
"type": "string"
},
"status": {
"default": "",
"description": "status of the condition, one of True, False, Unknown.",
"type": "string"
},
"type": {
"default": "",
"description": "type of condition in CamelCase or in foo.example.com/CamelCase.",
"type": "string"
}
},
"required": [
"type",
"status",
"lastTransitionTime",
"reason",
"message"
],
"type": "object"
},
"io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": {
"description": "DeleteOptions may be provided when deleting an API object.",
"properties": {

View File

@@ -86,6 +86,64 @@
"type": "object",
"x-kubernetes-map-type": "atomic"
},
"io.k8s.api.resource.v1beta1.AllocatedDeviceStatus": {
"description": "AllocatedDeviceStatus contains the status of an allocated device, if the driver chooses to report it. This may include driver-specific information.",
"properties": {
"conditions": {
"description": "Conditions contains the latest observation of the device's state. If the device has been configured according to the class and claim config references, the `Ready` condition should be True.",
"items": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Condition"
}
],
"default": {}
},
"type": "array",
"x-kubernetes-list-map-keys": [
"type"
],
"x-kubernetes-list-type": "map"
},
"data": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.runtime.RawExtension"
}
],
"description": "Data contains arbitrary driver-specific data.\n\nThe length of the raw data must be smaller or equal to 10 Ki."
},
"device": {
"default": "",
"description": "Device references one device instance via its name in the driver's resource pool. It must be a DNS label.",
"type": "string"
},
"driver": {
"default": "",
"description": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.",
"type": "string"
},
"networkData": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.api.resource.v1beta1.NetworkDeviceData"
}
],
"description": "NetworkData contains network-related information specific to the device."
},
"pool": {
"default": "",
"description": "This name together with the driver name and the device name field identify which device was allocated (`<driver name>/<pool name>/<device name>`).\n\nMust not be longer than 253 characters and may contain one or more DNS sub-domains separated by slashes.",
"type": "string"
}
},
"required": [
"driver",
"pool",
"device"
],
"type": "object"
},
"io.k8s.api.resource.v1beta1.AllocationResult": {
"description": "AllocationResult contains attributes of an allocated resource.",
"properties": {
@@ -594,6 +652,29 @@
},
"type": "object"
},
"io.k8s.api.resource.v1beta1.NetworkDeviceData": {
"description": "NetworkDeviceData provides network-related details for the allocated device. This information may be filled by drivers or other components to configure or identify the device within a network context.",
"properties": {
"hardwareAddress": {
"description": "HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface.\n\nMust not be longer than 128 characters.",
"type": "string"
},
"interfaceName": {
"description": "InterfaceName specifies the name of the network interface associated with the allocated device. This might be the name of a physical or virtual network interface being configured in the pod.\n\nMust not be longer than 256 characters.",
"type": "string"
},
"ips": {
"description": "IPs lists the network addresses assigned to the device's network interface. This can include both IPv4 and IPv6 addresses. The IPs are in the CIDR notation, which includes both the address and the associated subnet mask. e.g.: \"192.0.2.5/24\" for IPv4 and \"2001:db8::5/64\" for IPv6.",
"items": {
"default": "",
"type": "string"
},
"type": "array",
"x-kubernetes-list-type": "atomic"
}
},
"type": "object"
},
"io.k8s.api.resource.v1beta1.OpaqueDeviceConfiguration": {
"description": "OpaqueDeviceConfiguration contains configuration parameters for a driver in a format defined by the driver vendor.",
"properties": {
@@ -769,6 +850,24 @@
],
"description": "Allocation is set once the claim has been allocated successfully."
},
"devices": {
"description": "Devices contains the status of each device allocated for this claim, as reported by the driver. This can include driver-specific information. Entries are owned by their respective drivers.",
"items": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.api.resource.v1beta1.AllocatedDeviceStatus"
}
],
"default": {}
},
"type": "array",
"x-kubernetes-list-map-keys": [
"driver",
"device",
"pool"
],
"x-kubernetes-list-type": "map"
},
"reservedFor": {
"description": "ReservedFor indicates which entities are currently allowed to use the claim. A Pod which references a ResourceClaim which is not reserved for that Pod will not be started. A claim that is in use or might be in use because it has been reserved must not get deallocated.\n\nIn a cluster with multiple scheduler instances, two pods might get scheduled concurrently by different schedulers. When they reference the same ResourceClaim which already has reached its maximum number of consumers, only one pod can be scheduled.\n\nBoth schedulers try to add their pod to the claim.status.reservedFor field, but only the update that reaches the API server first gets stored. The other one fails with an error and the scheduler which issued it knows that it must put the pod back into the queue, waiting for the ResourceClaim to become usable again.\n\nThere can be at most 32 such reservations. This may get increased in the future, but not reduced.",
"items": {
@@ -1197,6 +1296,52 @@
}
]
},
"io.k8s.apimachinery.pkg.apis.meta.v1.Condition": {
"description": "Condition contains details for one aspect of the current state of this API Resource.",
"properties": {
"lastTransitionTime": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Time"
}
],
"description": "lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable."
},
"message": {
"default": "",
"description": "message is a human readable message indicating details about the transition. This may be an empty string.",
"type": "string"
},
"observedGeneration": {
"description": "observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.",
"format": "int64",
"type": "integer"
},
"reason": {
"default": "",
"description": "reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.",
"type": "string"
},
"status": {
"default": "",
"description": "status of the condition, one of True, False, Unknown.",
"type": "string"
},
"type": {
"default": "",
"description": "type of condition in CamelCase or in foo.example.com/CamelCase.",
"type": "string"
}
},
"required": [
"type",
"status",
"lastTransitionTime",
"reason",
"message"
],
"type": "object"
},
"io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions": {
"description": "DeleteOptions may be provided when deleting an API object.",
"properties": {

View File

@@ -56,5 +56,13 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
// might be valid JSON which changes during re-encoding.
r.Parameters = runtime.RawExtension{Raw: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`)}
},
func(r *resource.AllocatedDeviceStatus, c fuzz.Continue) {
c.FuzzNoCustom(r)
// Match the fuzzer default content for runtime.Object.
//
// This is necessary because randomly generated content
// might be valid JSON which changes during re-encoding.
r.Data = runtime.RawExtension{Raw: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`)}
},
}
}

View File

@@ -703,6 +703,18 @@ type ResourceClaimStatus struct {
// it got removed. May be reused once decoding v1alpha3 is no longer
// supported.
// DeallocationRequested bool
// Devices contains the status of each device allocated for this
// claim, as reported by the driver. This can include driver-specific
// information. Entries are owned by their respective drivers.
//
// +optional
// +listType=map
// +listMapKey=driver
// +listMapKey=device
// +listMapKey=pool
// +featureGate=DRAResourceClaimDeviceStatus
Devices []AllocatedDeviceStatus
}
// ReservedForMaxSize is the maximum number of entries in
@@ -975,3 +987,88 @@ type ResourceClaimTemplateList struct {
// Items is the list of resource claim templates.
Items []ResourceClaimTemplate
}
// AllocatedDeviceStatus contains the status of an allocated device, if the
// driver chooses to report it. This may include driver-specific information.
type AllocatedDeviceStatus struct {
// Driver specifies the name of the DRA driver whose kubelet
// plugin should be invoked to process the allocation once the claim is
// needed on a node.
//
// Must be a DNS subdomain and should end with a DNS domain owned by the
// vendor of the driver.
//
// +required
Driver string
// This name together with the driver name and the device name field
// identify which device was allocated (`<driver name>/<pool name>/<device name>`).
//
// Must not be longer than 253 characters and may contain one or more
// DNS sub-domains separated by slashes.
//
// +required
Pool string
// Device references one device instance via its name in the driver's
// resource pool. It must be a DNS label.
//
// +required
Device string
// Conditions contains the latest observation of the device's state.
// If the device has been configured according to the class and claim
// config references, the `Ready` condition should be True.
//
// Must not contain more than 8 entries.
//
// +optional
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition
// Data contains arbitrary driver-specific data.
//
// The length of the raw data must be smaller or equal to 10 Ki.
//
// +optional
Data runtime.RawExtension
// NetworkData contains network-related information specific to the device.
//
// +optional
NetworkData *NetworkDeviceData
}
// NetworkDeviceData provides network-related details for the allocated device.
// This information may be filled by drivers or other components to configure
// or identify the device within a network context.
type NetworkDeviceData struct {
// InterfaceName specifies the name of the network interface associated with
// the allocated device. This might be the name of a physical or virtual
// network interface being configured in the pod.
//
// Must not be longer than 256 characters.
//
// +optional
InterfaceName string
// IPs lists the network addresses assigned to the device's network interface.
// This can include both IPv4 and IPv6 addresses.
// The IPs are in the CIDR notation, which includes both the address and the
// associated subnet mask.
// e.g.: "192.0.2.5/24" for IPv4 and "2001:db8::5/64" for IPv6.
//
// Must not contain more than 16 entries.
//
// +optional
// +listType=atomic
IPs []string
// HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface.
//
// Must not be longer than 128 characters.
//
// +optional
HardwareAddress string
}

View File

@@ -24,9 +24,10 @@ package v1alpha3
import (
unsafe "unsafe"
v1 "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
resourcev1alpha3 "k8s.io/api/resource/v1alpha3"
apiresource "k8s.io/apimachinery/pkg/api/resource"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
types "k8s.io/apimachinery/pkg/types"
@@ -41,6 +42,16 @@ func init() {
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*resourcev1alpha3.AllocatedDeviceStatus)(nil), (*resource.AllocatedDeviceStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha3_AllocatedDeviceStatus_To_resource_AllocatedDeviceStatus(a.(*resourcev1alpha3.AllocatedDeviceStatus), b.(*resource.AllocatedDeviceStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resource.AllocatedDeviceStatus)(nil), (*resourcev1alpha3.AllocatedDeviceStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resource_AllocatedDeviceStatus_To_v1alpha3_AllocatedDeviceStatus(a.(*resource.AllocatedDeviceStatus), b.(*resourcev1alpha3.AllocatedDeviceStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resourcev1alpha3.AllocationResult)(nil), (*resource.AllocationResult)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha3_AllocationResult_To_resource_AllocationResult(a.(*resourcev1alpha3.AllocationResult), b.(*resource.AllocationResult), scope)
}); err != nil {
@@ -221,6 +232,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resourcev1alpha3.NetworkDeviceData)(nil), (*resource.NetworkDeviceData)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha3_NetworkDeviceData_To_resource_NetworkDeviceData(a.(*resourcev1alpha3.NetworkDeviceData), b.(*resource.NetworkDeviceData), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resource.NetworkDeviceData)(nil), (*resourcev1alpha3.NetworkDeviceData)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resource_NetworkDeviceData_To_v1alpha3_NetworkDeviceData(a.(*resource.NetworkDeviceData), b.(*resourcev1alpha3.NetworkDeviceData), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resourcev1alpha3.OpaqueDeviceConfiguration)(nil), (*resource.OpaqueDeviceConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha3_OpaqueDeviceConfiguration_To_resource_OpaqueDeviceConfiguration(a.(*resourcev1alpha3.OpaqueDeviceConfiguration), b.(*resource.OpaqueDeviceConfiguration), scope)
}); err != nil {
@@ -364,6 +385,36 @@ func RegisterConversions(s *runtime.Scheme) error {
return nil
}
func autoConvert_v1alpha3_AllocatedDeviceStatus_To_resource_AllocatedDeviceStatus(in *resourcev1alpha3.AllocatedDeviceStatus, out *resource.AllocatedDeviceStatus, s conversion.Scope) error {
out.Driver = in.Driver
out.Pool = in.Pool
out.Device = in.Device
out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions))
out.Data = in.Data
out.NetworkData = (*resource.NetworkDeviceData)(unsafe.Pointer(in.NetworkData))
return nil
}
// Convert_v1alpha3_AllocatedDeviceStatus_To_resource_AllocatedDeviceStatus is an autogenerated conversion function.
func Convert_v1alpha3_AllocatedDeviceStatus_To_resource_AllocatedDeviceStatus(in *resourcev1alpha3.AllocatedDeviceStatus, out *resource.AllocatedDeviceStatus, s conversion.Scope) error {
return autoConvert_v1alpha3_AllocatedDeviceStatus_To_resource_AllocatedDeviceStatus(in, out, s)
}
func autoConvert_resource_AllocatedDeviceStatus_To_v1alpha3_AllocatedDeviceStatus(in *resource.AllocatedDeviceStatus, out *resourcev1alpha3.AllocatedDeviceStatus, s conversion.Scope) error {
out.Driver = in.Driver
out.Pool = in.Pool
out.Device = in.Device
out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions))
out.Data = in.Data
out.NetworkData = (*resourcev1alpha3.NetworkDeviceData)(unsafe.Pointer(in.NetworkData))
return nil
}
// Convert_resource_AllocatedDeviceStatus_To_v1alpha3_AllocatedDeviceStatus is an autogenerated conversion function.
func Convert_resource_AllocatedDeviceStatus_To_v1alpha3_AllocatedDeviceStatus(in *resource.AllocatedDeviceStatus, out *resourcev1alpha3.AllocatedDeviceStatus, s conversion.Scope) error {
return autoConvert_resource_AllocatedDeviceStatus_To_v1alpha3_AllocatedDeviceStatus(in, out, s)
}
func autoConvert_v1alpha3_AllocationResult_To_resource_AllocationResult(in *resourcev1alpha3.AllocationResult, out *resource.AllocationResult, s conversion.Scope) error {
if err := Convert_v1alpha3_DeviceAllocationResult_To_resource_DeviceAllocationResult(&in.Devices, &out.Devices, s); err != nil {
return err
@@ -381,7 +432,7 @@ func autoConvert_resource_AllocationResult_To_v1alpha3_AllocationResult(in *reso
if err := Convert_resource_DeviceAllocationResult_To_v1alpha3_DeviceAllocationResult(&in.Devices, &out.Devices, s); err != nil {
return err
}
out.NodeSelector = (*v1.NodeSelector)(unsafe.Pointer(in.NodeSelector))
out.NodeSelector = (*corev1.NodeSelector)(unsafe.Pointer(in.NodeSelector))
return nil
}
@@ -834,6 +885,30 @@ func Convert_resource_DeviceSelector_To_v1alpha3_DeviceSelector(in *resource.Dev
return autoConvert_resource_DeviceSelector_To_v1alpha3_DeviceSelector(in, out, s)
}
func autoConvert_v1alpha3_NetworkDeviceData_To_resource_NetworkDeviceData(in *resourcev1alpha3.NetworkDeviceData, out *resource.NetworkDeviceData, s conversion.Scope) error {
out.InterfaceName = in.InterfaceName
out.IPs = *(*[]string)(unsafe.Pointer(&in.IPs))
out.HardwareAddress = in.HardwareAddress
return nil
}
// Convert_v1alpha3_NetworkDeviceData_To_resource_NetworkDeviceData is an autogenerated conversion function.
func Convert_v1alpha3_NetworkDeviceData_To_resource_NetworkDeviceData(in *resourcev1alpha3.NetworkDeviceData, out *resource.NetworkDeviceData, s conversion.Scope) error {
return autoConvert_v1alpha3_NetworkDeviceData_To_resource_NetworkDeviceData(in, out, s)
}
func autoConvert_resource_NetworkDeviceData_To_v1alpha3_NetworkDeviceData(in *resource.NetworkDeviceData, out *resourcev1alpha3.NetworkDeviceData, s conversion.Scope) error {
out.InterfaceName = in.InterfaceName
out.IPs = *(*[]string)(unsafe.Pointer(&in.IPs))
out.HardwareAddress = in.HardwareAddress
return nil
}
// Convert_resource_NetworkDeviceData_To_v1alpha3_NetworkDeviceData is an autogenerated conversion function.
func Convert_resource_NetworkDeviceData_To_v1alpha3_NetworkDeviceData(in *resource.NetworkDeviceData, out *resourcev1alpha3.NetworkDeviceData, s conversion.Scope) error {
return autoConvert_resource_NetworkDeviceData_To_v1alpha3_NetworkDeviceData(in, out, s)
}
func autoConvert_v1alpha3_OpaqueDeviceConfiguration_To_resource_OpaqueDeviceConfiguration(in *resourcev1alpha3.OpaqueDeviceConfiguration, out *resource.OpaqueDeviceConfiguration, s conversion.Scope) error {
out.Driver = in.Driver
out.Parameters = in.Parameters
@@ -963,6 +1038,7 @@ func Convert_resource_ResourceClaimSpec_To_v1alpha3_ResourceClaimSpec(in *resour
func autoConvert_v1alpha3_ResourceClaimStatus_To_resource_ResourceClaimStatus(in *resourcev1alpha3.ResourceClaimStatus, out *resource.ResourceClaimStatus, s conversion.Scope) error {
out.Allocation = (*resource.AllocationResult)(unsafe.Pointer(in.Allocation))
out.ReservedFor = *(*[]resource.ResourceClaimConsumerReference)(unsafe.Pointer(&in.ReservedFor))
out.Devices = *(*[]resource.AllocatedDeviceStatus)(unsafe.Pointer(&in.Devices))
return nil
}
@@ -974,6 +1050,7 @@ func Convert_v1alpha3_ResourceClaimStatus_To_resource_ResourceClaimStatus(in *re
func autoConvert_resource_ResourceClaimStatus_To_v1alpha3_ResourceClaimStatus(in *resource.ResourceClaimStatus, out *resourcev1alpha3.ResourceClaimStatus, s conversion.Scope) error {
out.Allocation = (*resourcev1alpha3.AllocationResult)(unsafe.Pointer(in.Allocation))
out.ReservedFor = *(*[]resourcev1alpha3.ResourceClaimConsumerReference)(unsafe.Pointer(&in.ReservedFor))
out.Devices = *(*[]resourcev1alpha3.AllocatedDeviceStatus)(unsafe.Pointer(&in.Devices))
return nil
}
@@ -1181,7 +1258,7 @@ func autoConvert_resource_ResourceSliceSpec_To_v1alpha3_ResourceSliceSpec(in *re
return err
}
out.NodeName = in.NodeName
out.NodeSelector = (*v1.NodeSelector)(unsafe.Pointer(in.NodeSelector))
out.NodeSelector = (*corev1.NodeSelector)(unsafe.Pointer(in.NodeSelector))
out.AllNodes = in.AllNodes
if in.Devices != nil {
in, out := &in.Devices, &out.Devices

View File

@@ -24,8 +24,9 @@ package v1beta1
import (
unsafe "unsafe"
v1 "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
resourcev1beta1 "k8s.io/api/resource/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
types "k8s.io/apimachinery/pkg/types"
@@ -40,6 +41,16 @@ func init() {
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*resourcev1beta1.AllocatedDeviceStatus)(nil), (*resource.AllocatedDeviceStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_AllocatedDeviceStatus_To_resource_AllocatedDeviceStatus(a.(*resourcev1beta1.AllocatedDeviceStatus), b.(*resource.AllocatedDeviceStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resource.AllocatedDeviceStatus)(nil), (*resourcev1beta1.AllocatedDeviceStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resource_AllocatedDeviceStatus_To_v1beta1_AllocatedDeviceStatus(a.(*resource.AllocatedDeviceStatus), b.(*resourcev1beta1.AllocatedDeviceStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resourcev1beta1.AllocationResult)(nil), (*resource.AllocationResult)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_AllocationResult_To_resource_AllocationResult(a.(*resourcev1beta1.AllocationResult), b.(*resource.AllocationResult), scope)
}); err != nil {
@@ -230,6 +241,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resourcev1beta1.NetworkDeviceData)(nil), (*resource.NetworkDeviceData)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_NetworkDeviceData_To_resource_NetworkDeviceData(a.(*resourcev1beta1.NetworkDeviceData), b.(*resource.NetworkDeviceData), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resource.NetworkDeviceData)(nil), (*resourcev1beta1.NetworkDeviceData)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resource_NetworkDeviceData_To_v1beta1_NetworkDeviceData(a.(*resource.NetworkDeviceData), b.(*resourcev1beta1.NetworkDeviceData), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resourcev1beta1.OpaqueDeviceConfiguration)(nil), (*resource.OpaqueDeviceConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1beta1_OpaqueDeviceConfiguration_To_resource_OpaqueDeviceConfiguration(a.(*resourcev1beta1.OpaqueDeviceConfiguration), b.(*resource.OpaqueDeviceConfiguration), scope)
}); err != nil {
@@ -363,6 +384,36 @@ func RegisterConversions(s *runtime.Scheme) error {
return nil
}
func autoConvert_v1beta1_AllocatedDeviceStatus_To_resource_AllocatedDeviceStatus(in *resourcev1beta1.AllocatedDeviceStatus, out *resource.AllocatedDeviceStatus, s conversion.Scope) error {
out.Driver = in.Driver
out.Pool = in.Pool
out.Device = in.Device
out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions))
out.Data = in.Data
out.NetworkData = (*resource.NetworkDeviceData)(unsafe.Pointer(in.NetworkData))
return nil
}
// Convert_v1beta1_AllocatedDeviceStatus_To_resource_AllocatedDeviceStatus is an autogenerated conversion function.
func Convert_v1beta1_AllocatedDeviceStatus_To_resource_AllocatedDeviceStatus(in *resourcev1beta1.AllocatedDeviceStatus, out *resource.AllocatedDeviceStatus, s conversion.Scope) error {
return autoConvert_v1beta1_AllocatedDeviceStatus_To_resource_AllocatedDeviceStatus(in, out, s)
}
func autoConvert_resource_AllocatedDeviceStatus_To_v1beta1_AllocatedDeviceStatus(in *resource.AllocatedDeviceStatus, out *resourcev1beta1.AllocatedDeviceStatus, s conversion.Scope) error {
out.Driver = in.Driver
out.Pool = in.Pool
out.Device = in.Device
out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions))
out.Data = in.Data
out.NetworkData = (*resourcev1beta1.NetworkDeviceData)(unsafe.Pointer(in.NetworkData))
return nil
}
// Convert_resource_AllocatedDeviceStatus_To_v1beta1_AllocatedDeviceStatus is an autogenerated conversion function.
func Convert_resource_AllocatedDeviceStatus_To_v1beta1_AllocatedDeviceStatus(in *resource.AllocatedDeviceStatus, out *resourcev1beta1.AllocatedDeviceStatus, s conversion.Scope) error {
return autoConvert_resource_AllocatedDeviceStatus_To_v1beta1_AllocatedDeviceStatus(in, out, s)
}
func autoConvert_v1beta1_AllocationResult_To_resource_AllocationResult(in *resourcev1beta1.AllocationResult, out *resource.AllocationResult, s conversion.Scope) error {
if err := Convert_v1beta1_DeviceAllocationResult_To_resource_DeviceAllocationResult(&in.Devices, &out.Devices, s); err != nil {
return err
@@ -380,7 +431,7 @@ func autoConvert_resource_AllocationResult_To_v1beta1_AllocationResult(in *resou
if err := Convert_resource_DeviceAllocationResult_To_v1beta1_DeviceAllocationResult(&in.Devices, &out.Devices, s); err != nil {
return err
}
out.NodeSelector = (*v1.NodeSelector)(unsafe.Pointer(in.NodeSelector))
out.NodeSelector = (*corev1.NodeSelector)(unsafe.Pointer(in.NodeSelector))
return nil
}
@@ -813,6 +864,30 @@ func Convert_resource_DeviceSelector_To_v1beta1_DeviceSelector(in *resource.Devi
return autoConvert_resource_DeviceSelector_To_v1beta1_DeviceSelector(in, out, s)
}
func autoConvert_v1beta1_NetworkDeviceData_To_resource_NetworkDeviceData(in *resourcev1beta1.NetworkDeviceData, out *resource.NetworkDeviceData, s conversion.Scope) error {
out.InterfaceName = in.InterfaceName
out.IPs = *(*[]string)(unsafe.Pointer(&in.IPs))
out.HardwareAddress = in.HardwareAddress
return nil
}
// Convert_v1beta1_NetworkDeviceData_To_resource_NetworkDeviceData is an autogenerated conversion function.
func Convert_v1beta1_NetworkDeviceData_To_resource_NetworkDeviceData(in *resourcev1beta1.NetworkDeviceData, out *resource.NetworkDeviceData, s conversion.Scope) error {
return autoConvert_v1beta1_NetworkDeviceData_To_resource_NetworkDeviceData(in, out, s)
}
func autoConvert_resource_NetworkDeviceData_To_v1beta1_NetworkDeviceData(in *resource.NetworkDeviceData, out *resourcev1beta1.NetworkDeviceData, s conversion.Scope) error {
out.InterfaceName = in.InterfaceName
out.IPs = *(*[]string)(unsafe.Pointer(&in.IPs))
out.HardwareAddress = in.HardwareAddress
return nil
}
// Convert_resource_NetworkDeviceData_To_v1beta1_NetworkDeviceData is an autogenerated conversion function.
func Convert_resource_NetworkDeviceData_To_v1beta1_NetworkDeviceData(in *resource.NetworkDeviceData, out *resourcev1beta1.NetworkDeviceData, s conversion.Scope) error {
return autoConvert_resource_NetworkDeviceData_To_v1beta1_NetworkDeviceData(in, out, s)
}
func autoConvert_v1beta1_OpaqueDeviceConfiguration_To_resource_OpaqueDeviceConfiguration(in *resourcev1beta1.OpaqueDeviceConfiguration, out *resource.OpaqueDeviceConfiguration, s conversion.Scope) error {
out.Driver = in.Driver
out.Parameters = in.Parameters
@@ -942,6 +1017,7 @@ func Convert_resource_ResourceClaimSpec_To_v1beta1_ResourceClaimSpec(in *resourc
func autoConvert_v1beta1_ResourceClaimStatus_To_resource_ResourceClaimStatus(in *resourcev1beta1.ResourceClaimStatus, out *resource.ResourceClaimStatus, s conversion.Scope) error {
out.Allocation = (*resource.AllocationResult)(unsafe.Pointer(in.Allocation))
out.ReservedFor = *(*[]resource.ResourceClaimConsumerReference)(unsafe.Pointer(&in.ReservedFor))
out.Devices = *(*[]resource.AllocatedDeviceStatus)(unsafe.Pointer(&in.Devices))
return nil
}
@@ -953,6 +1029,7 @@ func Convert_v1beta1_ResourceClaimStatus_To_resource_ResourceClaimStatus(in *res
func autoConvert_resource_ResourceClaimStatus_To_v1beta1_ResourceClaimStatus(in *resource.ResourceClaimStatus, out *resourcev1beta1.ResourceClaimStatus, s conversion.Scope) error {
out.Allocation = (*resourcev1beta1.AllocationResult)(unsafe.Pointer(in.Allocation))
out.ReservedFor = *(*[]resourcev1beta1.ResourceClaimConsumerReference)(unsafe.Pointer(&in.ReservedFor))
out.Devices = *(*[]resourcev1beta1.AllocatedDeviceStatus)(unsafe.Pointer(&in.Devices))
return nil
}
@@ -1130,7 +1207,7 @@ func autoConvert_resource_ResourceSliceSpec_To_v1beta1_ResourceSliceSpec(in *res
return err
}
out.NodeName = in.NodeName
out.NodeSelector = (*v1.NodeSelector)(unsafe.Pointer(in.NodeSelector))
out.NodeSelector = (*corev1.NodeSelector)(unsafe.Pointer(in.NodeSelector))
out.AllNodes = in.AllNodes
out.Devices = *(*[]resourcev1beta1.Device)(unsafe.Pointer(&in.Devices))
return nil

View File

@@ -25,6 +25,8 @@ import (
apiequality "k8s.io/apimachinery/pkg/api/equality"
apimachineryvalidation "k8s.io/apimachinery/pkg/api/validation"
metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation"
@@ -32,8 +34,10 @@ import (
"k8s.io/apiserver/pkg/cel"
"k8s.io/apiserver/pkg/cel/environment"
dracel "k8s.io/dynamic-resource-allocation/cel"
"k8s.io/dynamic-resource-allocation/structured"
corevalidation "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/apis/resource"
netutils "k8s.io/utils/net"
)
var (
@@ -123,6 +127,15 @@ func gatherRequestNames(deviceClaim *resource.DeviceClaim) sets.Set[string] {
return requestNames
}
func gatherAllocatedDevices(allocationResult *resource.DeviceAllocationResult) sets.Set[structured.DeviceID] {
allocatedDevices := sets.New[structured.DeviceID]()
for _, result := range allocationResult.Results {
deviceID := structured.MakeDeviceID(result.Driver, result.Pool, result.Device)
allocatedDevices.Insert(deviceID)
}
return allocatedDevices
}
func validateDeviceRequest(request resource.DeviceRequest, fldPath *field.Path, stored bool) field.ErrorList {
allErrs := validateRequestName(request.Name, fldPath.Child("name"))
if request.DeviceClassName == "" {
@@ -243,24 +256,7 @@ func validateDeviceConfiguration(config resource.DeviceConfiguration, fldPath *f
func validateOpaqueConfiguration(config resource.OpaqueDeviceConfiguration, fldPath *field.Path, stored bool) field.ErrorList {
var allErrs field.ErrorList
allErrs = append(allErrs, validateDriverName(config.Driver, fldPath.Child("driver"))...)
// Validation of RawExtension as in https://github.com/kubernetes/kubernetes/pull/125549/
var v any
if len(config.Parameters.Raw) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("parameters"), ""))
} else if !stored && len(config.Parameters.Raw) > resource.OpaqueParametersMaxLength {
// Don't even bother with parsing when too large.
// Only applies on create. Existing parameters are grand-fathered in
// because the limit was introduced in 1.32. This also means that it
// can be changed in the future.
allErrs = append(allErrs, field.TooLong(fldPath.Child("parameters"), "" /* unused */, resource.OpaqueParametersMaxLength))
} else if err := json.Unmarshal(config.Parameters.Raw, &v); err != nil {
allErrs = append(allErrs, field.Invalid(fldPath.Child("parameters"), "<value omitted>", fmt.Sprintf("error parsing data as JSON: %v", err.Error())))
} else if v == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("parameters"), ""))
} else if _, isObject := v.(map[string]any); !isObject {
allErrs = append(allErrs, field.Invalid(fldPath.Child("parameters"), "<value omitted>", "parameters must be a valid JSON object"))
}
allErrs = append(allErrs, validateRawExtension(config.Parameters, fldPath.Child("parameters"), stored)...)
return allErrs
}
@@ -271,6 +267,19 @@ func validateResourceClaimStatusUpdate(status, oldStatus *resource.ResourceClaim
func(consumer resource.ResourceClaimConsumerReference) (types.UID, string) { return consumer.UID, "uid" },
fldPath.Child("reservedFor"))...)
var allocatedDevices sets.Set[structured.DeviceID]
if status.Allocation != nil {
allocatedDevices = gatherAllocatedDevices(&status.Allocation.Devices)
}
allErrs = append(allErrs, validateSet(status.Devices, -1,
func(device resource.AllocatedDeviceStatus, fldPath *field.Path) field.ErrorList {
return validateDeviceStatus(device, fldPath, allocatedDevices)
},
func(device resource.AllocatedDeviceStatus) (structured.DeviceID, string) {
return structured.MakeDeviceID(device.Driver, device.Pool, device.Device), "deviceID"
},
fldPath.Child("devices"))...)
// Now check for invariants that must be valid for a ResourceClaim.
if len(status.ReservedFor) > 0 {
if status.Allocation == nil {
@@ -729,3 +738,82 @@ func truncateIfTooLong(str string, maxLen int) string {
remaining := maxLen - len(ellipsis)
return str[0:(remaining+1)/2] + ellipsis + str[len(str)-remaining/2:]
}
func validateDeviceStatus(device resource.AllocatedDeviceStatus, fldPath *field.Path, allocatedDevices sets.Set[structured.DeviceID]) field.ErrorList {
var allErrs field.ErrorList
allErrs = append(allErrs, validateDriverName(device.Driver, fldPath.Child("driver"))...)
allErrs = append(allErrs, validatePoolName(device.Pool, fldPath.Child("pool"))...)
allErrs = append(allErrs, validateDeviceName(device.Device, fldPath.Child("device"))...)
deviceID := structured.MakeDeviceID(device.Driver, device.Pool, device.Device)
if !allocatedDevices.Has(deviceID) {
allErrs = append(allErrs, field.Invalid(fldPath, deviceID, "must be an allocated device in the claim"))
}
if len(device.Conditions) > maxConditions {
allErrs = append(allErrs, field.TooMany(fldPath.Child("conditions"), len(device.Conditions), maxConditions))
}
allErrs = append(allErrs, metav1validation.ValidateConditions(device.Conditions, fldPath.Child("conditions"))...)
if len(device.Data.Raw) > 0 { // Data is an optional field.
allErrs = append(allErrs, validateRawExtension(device.Data, fldPath.Child("data"), false)...)
}
allErrs = append(allErrs, validateNetworkDeviceData(device.NetworkData, fldPath.Child("networkData"))...)
return allErrs
}
// validateRawExtension validates RawExtension as in https://github.com/kubernetes/kubernetes/pull/125549/
func validateRawExtension(rawExtension runtime.RawExtension, fldPath *field.Path, stored bool) field.ErrorList {
var allErrs field.ErrorList
var v any
if len(rawExtension.Raw) == 0 {
allErrs = append(allErrs, field.Required(fldPath, ""))
} else if !stored && len(rawExtension.Raw) > resource.OpaqueParametersMaxLength {
// Don't even bother with parsing when too large.
// Only applies on create. Existing parameters are grand-fathered in
// because the limit was introduced in 1.32. This also means that it
// can be changed in the future.
allErrs = append(allErrs, field.TooLong(fldPath, "" /* unused */, resource.OpaqueParametersMaxLength))
} else if err := json.Unmarshal(rawExtension.Raw, &v); err != nil {
allErrs = append(allErrs, field.Invalid(fldPath, "<value omitted>", fmt.Sprintf("error parsing data as JSON: %v", err.Error())))
} else if v == nil {
allErrs = append(allErrs, field.Required(fldPath, ""))
} else if _, isObject := v.(map[string]any); !isObject {
allErrs = append(allErrs, field.Invalid(fldPath, "<value omitted>", "parameters must be a valid JSON object"))
}
return allErrs
}
const maxConditions int = 8
const maxIPs int = 16
const interfaceNameMaxLength int = 256
const hardwareAddressMaxLength int = 128
func validateNetworkDeviceData(networkDeviceData *resource.NetworkDeviceData, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList
if networkDeviceData == nil {
return allErrs
}
if len(networkDeviceData.InterfaceName) > interfaceNameMaxLength {
allErrs = append(allErrs, field.TooLong(fldPath.Child("interfaceName"), "" /* unused */, interfaceNameMaxLength))
}
if len(networkDeviceData.HardwareAddress) > hardwareAddressMaxLength {
allErrs = append(allErrs, field.TooLong(fldPath.Child("hardwareAddress"), "" /* unused */, hardwareAddressMaxLength))
}
allErrs = append(allErrs, validateSet(networkDeviceData.IPs, maxIPs,
func(address string, fldPath *field.Path) field.ErrorList {
return validation.IsValidCIDR(fldPath, address)
},
func(address string) (string, string) {
// reformat CIDR to handle different ways IPs can be written
// (e.g. 2001:db8::1/64 == 2001:0db8::1/64)
ip, ipNet, err := netutils.ParseCIDRSloppy(address)
if err != nil {
return "", "" // will fail at IsValidCIDR
}
maskSize, _ := ipNet.Mask.Size()
return fmt.Sprintf("%s/%d", ip.String(), maskSize), ""
},
fldPath.Child("ips"))...)
return allErrs
}

View File

@@ -27,6 +27,7 @@ import (
"k8s.io/apimachinery/pkg/util/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature"
featuregatetesting "k8s.io/component-base/featuregate/testing"
"k8s.io/dynamic-resource-allocation/structured"
"k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/resource"
"k8s.io/kubernetes/pkg/features"
@@ -615,10 +616,11 @@ func TestValidateClaimStatusUpdate(t *testing.T) {
validAllocatedClaimOld.Status.Allocation.Devices.Results[0].AdminAccess = nil // Not required in 1.31.
scenarios := map[string]struct {
adminAccess bool
oldClaim *resource.ResourceClaim
update func(claim *resource.ResourceClaim) *resource.ResourceClaim
wantFailures field.ErrorList
adminAccess bool
deviceStatusFeatureGate bool
oldClaim *resource.ResourceClaim
update func(claim *resource.ResourceClaim) *resource.ResourceClaim
wantFailures field.ErrorList
}{
"valid-no-op-update": {
oldClaim: validClaim,
@@ -983,13 +985,317 @@ func TestValidateClaimStatusUpdate(t *testing.T) {
return claim
},
},
"valid-network-device-status": {
oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(),
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
claim.Status.Devices = []resource.AllocatedDeviceStatus{
{
Driver: goodName,
Pool: goodName,
Device: goodName,
Conditions: []metav1.Condition{
{Type: "test-0", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-1", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-2", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-3", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-4", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-5", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-6", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-7", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
},
Data: runtime.RawExtension{
Raw: []byte(`{"kind": "foo", "apiVersion": "dra.example.com/v1"}`),
},
NetworkData: &resource.NetworkDeviceData{
InterfaceName: strings.Repeat("x", 256),
HardwareAddress: strings.Repeat("x", 128),
IPs: []string{
"10.9.8.0/24",
"2001:db8::/64",
"10.9.8.1/24",
"2001:db8::1/64",
"10.9.8.2/24", "10.9.8.3/24", "10.9.8.4/24", "10.9.8.5/24", "10.9.8.6/24", "10.9.8.7/24",
"10.9.8.8/24", "10.9.8.9/24", "10.9.8.10/24", "10.9.8.11/24", "10.9.8.12/24", "10.9.8.13/24",
},
},
},
}
return claim
},
deviceStatusFeatureGate: true,
},
"invalid-device-status-duplicate": {
wantFailures: field.ErrorList{
field.Duplicate(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(1), "2001:db8::1/64"),
field.Duplicate(field.NewPath("status", "devices").Index(1).Child("deviceID"), structured.MakeDeviceID(goodName, goodName, goodName)),
},
oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(),
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
claim.Status.Devices = []resource.AllocatedDeviceStatus{
{
Driver: goodName,
Pool: goodName,
Device: goodName,
NetworkData: &resource.NetworkDeviceData{
IPs: []string{
"2001:db8::1/64",
"2001:0db8::1/64",
},
},
},
{
Driver: goodName,
Pool: goodName,
Device: goodName,
},
}
return claim
},
deviceStatusFeatureGate: true,
},
"invalid-network-device-status": {
wantFailures: field.ErrorList{
field.TooLong(field.NewPath("status", "devices").Index(0).Child("networkData", "interfaceName"), "", interfaceNameMaxLength),
field.TooLong(field.NewPath("status", "devices").Index(0).Child("networkData", "hardwareAddress"), "", hardwareAddressMaxLength),
field.Invalid(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(0), "300.9.8.0/24", "must be a valid CIDR value, (e.g. 10.9.8.0/24 or 2001:db8::/64)"),
},
oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(),
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
claim.Status.Devices = []resource.AllocatedDeviceStatus{
{
Driver: goodName,
Pool: goodName,
Device: goodName,
NetworkData: &resource.NetworkDeviceData{
InterfaceName: strings.Repeat("x", interfaceNameMaxLength+1),
HardwareAddress: strings.Repeat("x", hardwareAddressMaxLength+1),
IPs: []string{
"300.9.8.0/24",
},
},
},
}
return claim
},
deviceStatusFeatureGate: true,
},
"invalid-data-device-status": {
wantFailures: field.ErrorList{
field.Invalid(field.NewPath("status", "devices").Index(0).Child("data"), "<value omitted>", "error parsing data as JSON: invalid character 'o' in literal false (expecting 'a')"),
},
oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(),
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
claim.Status.Devices = []resource.AllocatedDeviceStatus{
{
Driver: goodName,
Pool: goodName,
Device: goodName,
Data: runtime.RawExtension{
Raw: []byte(`foo`),
},
},
}
return claim
},
deviceStatusFeatureGate: true,
},
"invalid-data-device-status-limits": {
wantFailures: field.ErrorList{
field.TooMany(field.NewPath("status", "devices").Index(0).Child("conditions"), maxConditions+1, maxConditions),
field.TooLong(field.NewPath("status", "devices").Index(0).Child("data"), "" /* unused */, resource.OpaqueParametersMaxLength),
field.TooMany(field.NewPath("status", "devices").Index(0).Child("networkData", "ips"), maxIPs+1, maxIPs),
},
oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(),
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
claim.Status.Devices = []resource.AllocatedDeviceStatus{
{
Driver: goodName,
Pool: goodName,
Device: goodName,
Data: runtime.RawExtension{Raw: []byte(`{"str": "` + strings.Repeat("x", resource.OpaqueParametersMaxLength-9-2+1 /* too large by one */) + `"}`)},
Conditions: []metav1.Condition{
{Type: "test-0", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-1", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-2", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-3", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-4", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-5", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-6", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-7", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-8", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
},
NetworkData: &resource.NetworkDeviceData{
IPs: []string{
"10.9.8.0/24", "10.9.8.1/24", "10.9.8.2/24", "10.9.8.3/24", "10.9.8.4/24", "10.9.8.5/24", "10.9.8.6/24", "10.9.8.7/24", "10.9.8.8/24",
"10.9.8.9/24", "10.9.8.10/24", "10.9.8.11/24", "10.9.8.12/24", "10.9.8.13/24", "10.9.8.14/24", "10.9.8.15/24", "10.9.8.16/24",
},
},
},
}
return claim
},
deviceStatusFeatureGate: true,
},
"invalid-device-status-no-device": {
wantFailures: field.ErrorList{
field.Invalid(field.NewPath("status", "devices").Index(0), structured.MakeDeviceID("b", "a", "r"), "must be an allocated device in the claim"),
},
oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(),
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
claim.Status.Devices = []resource.AllocatedDeviceStatus{
{
Driver: "b",
Pool: "a",
Device: "r",
},
}
return claim
},
deviceStatusFeatureGate: true,
},
"invalid-device-status-duplicate-disabled-feature-gate": {
wantFailures: field.ErrorList{
field.Duplicate(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(1), "2001:db8::1/64"),
field.Duplicate(field.NewPath("status", "devices").Index(1).Child("deviceID"), structured.MakeDeviceID(goodName, goodName, goodName)),
},
oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(),
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
claim.Status.Devices = []resource.AllocatedDeviceStatus{
{
Driver: goodName,
Pool: goodName,
Device: goodName,
NetworkData: &resource.NetworkDeviceData{
IPs: []string{
"2001:db8::1/64",
"2001:0db8::1/64",
},
},
},
{
Driver: goodName,
Pool: goodName,
Device: goodName,
},
}
return claim
},
deviceStatusFeatureGate: false,
},
"invalid-network-device-status-disabled-feature-gate": {
wantFailures: field.ErrorList{
field.TooLong(field.NewPath("status", "devices").Index(0).Child("networkData", "interfaceName"), "", interfaceNameMaxLength),
field.TooLong(field.NewPath("status", "devices").Index(0).Child("networkData", "hardwareAddress"), "", hardwareAddressMaxLength),
field.Invalid(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(0), "300.9.8.0/24", "must be a valid CIDR value, (e.g. 10.9.8.0/24 or 2001:db8::/64)"),
},
oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(),
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
claim.Status.Devices = []resource.AllocatedDeviceStatus{
{
Driver: goodName,
Pool: goodName,
Device: goodName,
NetworkData: &resource.NetworkDeviceData{
InterfaceName: strings.Repeat("x", interfaceNameMaxLength+1),
HardwareAddress: strings.Repeat("x", hardwareAddressMaxLength+1),
IPs: []string{
"300.9.8.0/24",
},
},
},
}
return claim
},
deviceStatusFeatureGate: false,
},
"invalid-data-device-status-disabled-feature-gate": {
wantFailures: field.ErrorList{
field.Invalid(field.NewPath("status", "devices").Index(0).Child("data"), "<value omitted>", "error parsing data as JSON: invalid character 'o' in literal false (expecting 'a')"),
},
oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(),
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
claim.Status.Devices = []resource.AllocatedDeviceStatus{
{
Driver: goodName,
Pool: goodName,
Device: goodName,
Data: runtime.RawExtension{
Raw: []byte(`foo`),
},
},
}
return claim
},
deviceStatusFeatureGate: false,
},
"invalid-data-device-status-limits-feature-gate": {
wantFailures: field.ErrorList{
field.TooMany(field.NewPath("status", "devices").Index(0).Child("conditions"), maxConditions+1, maxConditions),
field.TooLong(field.NewPath("status", "devices").Index(0).Child("data"), "" /* unused */, resource.OpaqueParametersMaxLength),
field.TooMany(field.NewPath("status", "devices").Index(0).Child("networkData", "ips"), maxIPs+1, maxIPs),
},
oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(),
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
claim.Status.Devices = []resource.AllocatedDeviceStatus{
{
Driver: goodName,
Pool: goodName,
Device: goodName,
Data: runtime.RawExtension{Raw: []byte(`{"str": "` + strings.Repeat("x", resource.OpaqueParametersMaxLength-9-2+1 /* too large by one */) + `"}`)},
Conditions: []metav1.Condition{
{Type: "test-0", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-1", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-2", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-3", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-4", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-5", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-6", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-7", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
{Type: "test-8", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0},
},
NetworkData: &resource.NetworkDeviceData{
IPs: []string{
"10.9.8.0/24", "10.9.8.1/24", "10.9.8.2/24", "10.9.8.3/24", "10.9.8.4/24", "10.9.8.5/24", "10.9.8.6/24", "10.9.8.7/24", "10.9.8.8/24",
"10.9.8.9/24", "10.9.8.10/24", "10.9.8.11/24", "10.9.8.12/24", "10.9.8.13/24", "10.9.8.14/24", "10.9.8.15/24", "10.9.8.16/24",
},
},
},
}
return claim
},
deviceStatusFeatureGate: false,
},
"invalid-device-status-no-device-disabled-feature-gate": {
wantFailures: field.ErrorList{
field.Invalid(field.NewPath("status", "devices").Index(0), structured.MakeDeviceID("b", "a", "r"), "must be an allocated device in the claim"),
},
oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(),
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
claim.Status.Devices = []resource.AllocatedDeviceStatus{
{
Driver: "b",
Pool: "a",
Device: "r",
},
}
return claim
},
deviceStatusFeatureGate: false,
},
}
for name, scenario := range scenarios {
t.Run(name, func(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAAdminAccess, scenario.adminAccess)
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAResourceClaimDeviceStatus, scenario.deviceStatusFeatureGate)
scenario.oldClaim.ResourceVersion = "1"
errs := ValidateResourceClaimStatusUpdate(scenario.update(scenario.oldClaim.DeepCopy()), scenario.oldClaim)
if name == "invalid-data-device-status-limits-feature-gate" {
fmt.Println(errs)
fmt.Println(scenario.wantFailures)
}
assertFailures(t, scenario.wantFailures, errs)
})
}

View File

@@ -22,10 +22,40 @@ limitations under the License.
package resource
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
core "k8s.io/kubernetes/pkg/apis/core"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllocatedDeviceStatus) DeepCopyInto(out *AllocatedDeviceStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
in.Data.DeepCopyInto(&out.Data)
if in.NetworkData != nil {
in, out := &in.NetworkData, &out.NetworkData
*out = new(NetworkDeviceData)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AllocatedDeviceStatus.
func (in *AllocatedDeviceStatus) DeepCopy() *AllocatedDeviceStatus {
if in == nil {
return nil
}
out := new(AllocatedDeviceStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllocationResult) DeepCopyInto(out *AllocationResult) {
*out = *in
@@ -503,6 +533,27 @@ func (in *DeviceSelector) DeepCopy() *DeviceSelector {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NetworkDeviceData) DeepCopyInto(out *NetworkDeviceData) {
*out = *in
if in.IPs != nil {
in, out := &in.IPs, &out.IPs
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkDeviceData.
func (in *NetworkDeviceData) DeepCopy() *NetworkDeviceData {
if in == nil {
return nil
}
out := new(NetworkDeviceData)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OpaqueDeviceConfiguration) DeepCopyInto(out *OpaqueDeviceConfiguration) {
*out = *in
@@ -627,6 +678,13 @@ func (in *ResourceClaimStatus) DeepCopyInto(out *ResourceClaimStatus) {
*out = make([]ResourceClaimConsumerReference, len(*in))
copy(*out, *in)
}
if in.Devices != nil {
in, out := &in.Devices, &out.Devices
*out = make([]AllocatedDeviceStatus, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}

View File

@@ -224,6 +224,14 @@ const (
// based on "structured parameters".
DynamicResourceAllocation featuregate.Feature = "DynamicResourceAllocation"
// owner: @LionelJouin
// kep: http://kep.k8s.io/4817
// alpha: v1.32
//
// Enables support the ResourceClaim.status.devices field and for setting this
// status from DRA drivers.
DRAResourceClaimDeviceStatus featuregate.Feature = "DRAResourceClaimDeviceStatus"
// owner: @harche
// kep: http://kep.k8s.io/3386
//

View File

@@ -183,6 +183,10 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate
{Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Beta},
},
DRAResourceClaimDeviceStatus: {
{Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha},
},
ElasticIndexedJob: {
{Version: version.MustParse("1.27"), Default: true, PreRelease: featuregate.Beta},
{Version: version.MustParse("1.31"), Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // GA in 1.31, remove in 1.32

View File

@@ -900,6 +900,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"k8s.io/api/rbac/v1beta1.RoleList": schema_k8sio_api_rbac_v1beta1_RoleList(ref),
"k8s.io/api/rbac/v1beta1.RoleRef": schema_k8sio_api_rbac_v1beta1_RoleRef(ref),
"k8s.io/api/rbac/v1beta1.Subject": schema_k8sio_api_rbac_v1beta1_Subject(ref),
"k8s.io/api/resource/v1alpha3.AllocatedDeviceStatus": schema_k8sio_api_resource_v1alpha3_AllocatedDeviceStatus(ref),
"k8s.io/api/resource/v1alpha3.AllocationResult": schema_k8sio_api_resource_v1alpha3_AllocationResult(ref),
"k8s.io/api/resource/v1alpha3.BasicDevice": schema_k8sio_api_resource_v1alpha3_BasicDevice(ref),
"k8s.io/api/resource/v1alpha3.CELDeviceSelector": schema_k8sio_api_resource_v1alpha3_CELDeviceSelector(ref),
@@ -918,6 +919,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"k8s.io/api/resource/v1alpha3.DeviceRequest": schema_k8sio_api_resource_v1alpha3_DeviceRequest(ref),
"k8s.io/api/resource/v1alpha3.DeviceRequestAllocationResult": schema_k8sio_api_resource_v1alpha3_DeviceRequestAllocationResult(ref),
"k8s.io/api/resource/v1alpha3.DeviceSelector": schema_k8sio_api_resource_v1alpha3_DeviceSelector(ref),
"k8s.io/api/resource/v1alpha3.NetworkDeviceData": schema_k8sio_api_resource_v1alpha3_NetworkDeviceData(ref),
"k8s.io/api/resource/v1alpha3.OpaqueDeviceConfiguration": schema_k8sio_api_resource_v1alpha3_OpaqueDeviceConfiguration(ref),
"k8s.io/api/resource/v1alpha3.ResourceClaim": schema_k8sio_api_resource_v1alpha3_ResourceClaim(ref),
"k8s.io/api/resource/v1alpha3.ResourceClaimConsumerReference": schema_k8sio_api_resource_v1alpha3_ResourceClaimConsumerReference(ref),
@@ -931,6 +933,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"k8s.io/api/resource/v1alpha3.ResourceSlice": schema_k8sio_api_resource_v1alpha3_ResourceSlice(ref),
"k8s.io/api/resource/v1alpha3.ResourceSliceList": schema_k8sio_api_resource_v1alpha3_ResourceSliceList(ref),
"k8s.io/api/resource/v1alpha3.ResourceSliceSpec": schema_k8sio_api_resource_v1alpha3_ResourceSliceSpec(ref),
"k8s.io/api/resource/v1beta1.AllocatedDeviceStatus": schema_k8sio_api_resource_v1beta1_AllocatedDeviceStatus(ref),
"k8s.io/api/resource/v1beta1.AllocationResult": schema_k8sio_api_resource_v1beta1_AllocationResult(ref),
"k8s.io/api/resource/v1beta1.BasicDevice": schema_k8sio_api_resource_v1beta1_BasicDevice(ref),
"k8s.io/api/resource/v1beta1.CELDeviceSelector": schema_k8sio_api_resource_v1beta1_CELDeviceSelector(ref),
@@ -950,6 +953,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"k8s.io/api/resource/v1beta1.DeviceRequest": schema_k8sio_api_resource_v1beta1_DeviceRequest(ref),
"k8s.io/api/resource/v1beta1.DeviceRequestAllocationResult": schema_k8sio_api_resource_v1beta1_DeviceRequestAllocationResult(ref),
"k8s.io/api/resource/v1beta1.DeviceSelector": schema_k8sio_api_resource_v1beta1_DeviceSelector(ref),
"k8s.io/api/resource/v1beta1.NetworkDeviceData": schema_k8sio_api_resource_v1beta1_NetworkDeviceData(ref),
"k8s.io/api/resource/v1beta1.OpaqueDeviceConfiguration": schema_k8sio_api_resource_v1beta1_OpaqueDeviceConfiguration(ref),
"k8s.io/api/resource/v1beta1.ResourceClaim": schema_k8sio_api_resource_v1beta1_ResourceClaim(ref),
"k8s.io/api/resource/v1beta1.ResourceClaimConsumerReference": schema_k8sio_api_resource_v1beta1_ResourceClaimConsumerReference(ref),
@@ -46294,6 +46298,80 @@ func schema_k8sio_api_rbac_v1beta1_Subject(ref common.ReferenceCallback) common.
}
}
func schema_k8sio_api_resource_v1alpha3_AllocatedDeviceStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "AllocatedDeviceStatus contains the status of an allocated device, if the driver chooses to report it. This may include driver-specific information.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"driver": {
SchemaProps: spec.SchemaProps{
Description: "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"pool": {
SchemaProps: spec.SchemaProps{
Description: "This name together with the driver name and the device name field identify which device was allocated (`<driver name>/<pool name>/<device name>`).\n\nMust not be longer than 253 characters and may contain one or more DNS sub-domains separated by slashes.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"device": {
SchemaProps: spec.SchemaProps{
Description: "Device references one device instance via its name in the driver's resource pool. It must be a DNS label.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"conditions": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-map-keys": []interface{}{
"type",
},
"x-kubernetes-list-type": "map",
},
},
SchemaProps: spec.SchemaProps{
Description: "Conditions contains the latest observation of the device's state. If the device has been configured according to the class and claim config references, the `Ready` condition should be True.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"),
},
},
},
},
},
"data": {
SchemaProps: spec.SchemaProps{
Description: "Data contains arbitrary driver-specific data.\n\nThe length of the raw data must be smaller or equal to 10 Ki.",
Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"),
},
},
"networkData": {
SchemaProps: spec.SchemaProps{
Description: "NetworkData contains network-related information specific to the device.",
Ref: ref("k8s.io/api/resource/v1alpha3.NetworkDeviceData"),
},
},
},
Required: []string{"driver", "pool", "device"},
},
},
Dependencies: []string{
"k8s.io/api/resource/v1alpha3.NetworkDeviceData", "k8s.io/apimachinery/pkg/apis/meta/v1.Condition", "k8s.io/apimachinery/pkg/runtime.RawExtension"},
}
}
func schema_k8sio_api_resource_v1alpha3_AllocationResult(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -47051,6 +47129,53 @@ func schema_k8sio_api_resource_v1alpha3_DeviceSelector(ref common.ReferenceCallb
}
}
func schema_k8sio_api_resource_v1alpha3_NetworkDeviceData(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "NetworkDeviceData provides network-related details for the allocated device. This information may be filled by drivers or other components to configure or identify the device within a network context.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"interfaceName": {
SchemaProps: spec.SchemaProps{
Description: "InterfaceName specifies the name of the network interface associated with the allocated device. This might be the name of a physical or virtual network interface being configured in the pod.\n\nMust not be longer than 256 characters.",
Type: []string{"string"},
Format: "",
},
},
"ips": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-type": "atomic",
},
},
SchemaProps: spec.SchemaProps{
Description: "IPs lists the network addresses assigned to the device's network interface. This can include both IPv4 and IPv6 addresses. The IPs are in the CIDR notation, which includes both the address and the associated subnet mask. e.g.: \"192.0.2.5/24\" for IPv4 and \"2001:db8::5/64\" for IPv6.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
"hardwareAddress": {
SchemaProps: spec.SchemaProps{
Description: "HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface.\n\nMust not be longer than 128 characters.",
Type: []string{"string"},
Format: "",
},
},
},
},
},
}
}
func schema_k8sio_api_resource_v1alpha3_OpaqueDeviceConfiguration(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -47287,11 +47412,35 @@ func schema_k8sio_api_resource_v1alpha3_ResourceClaimStatus(ref common.Reference
},
},
},
"devices": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-map-keys": []interface{}{
"driver",
"device",
"pool",
},
"x-kubernetes-list-type": "map",
},
},
SchemaProps: spec.SchemaProps{
Description: "Devices contains the status of each device allocated for this claim, as reported by the driver. This can include driver-specific information. Entries are owned by their respective drivers.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/api/resource/v1alpha3.AllocatedDeviceStatus"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/resource/v1alpha3.AllocationResult", "k8s.io/api/resource/v1alpha3.ResourceClaimConsumerReference"},
"k8s.io/api/resource/v1alpha3.AllocatedDeviceStatus", "k8s.io/api/resource/v1alpha3.AllocationResult", "k8s.io/api/resource/v1alpha3.ResourceClaimConsumerReference"},
}
}
@@ -47623,6 +47772,80 @@ func schema_k8sio_api_resource_v1alpha3_ResourceSliceSpec(ref common.ReferenceCa
}
}
func schema_k8sio_api_resource_v1beta1_AllocatedDeviceStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "AllocatedDeviceStatus contains the status of an allocated device, if the driver chooses to report it. This may include driver-specific information.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"driver": {
SchemaProps: spec.SchemaProps{
Description: "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"pool": {
SchemaProps: spec.SchemaProps{
Description: "This name together with the driver name and the device name field identify which device was allocated (`<driver name>/<pool name>/<device name>`).\n\nMust not be longer than 253 characters and may contain one or more DNS sub-domains separated by slashes.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"device": {
SchemaProps: spec.SchemaProps{
Description: "Device references one device instance via its name in the driver's resource pool. It must be a DNS label.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"conditions": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-map-keys": []interface{}{
"type",
},
"x-kubernetes-list-type": "map",
},
},
SchemaProps: spec.SchemaProps{
Description: "Conditions contains the latest observation of the device's state. If the device has been configured according to the class and claim config references, the `Ready` condition should be True.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.Condition"),
},
},
},
},
},
"data": {
SchemaProps: spec.SchemaProps{
Description: "Data contains arbitrary driver-specific data.\n\nThe length of the raw data must be smaller or equal to 10 Ki.",
Ref: ref("k8s.io/apimachinery/pkg/runtime.RawExtension"),
},
},
"networkData": {
SchemaProps: spec.SchemaProps{
Description: "NetworkData contains network-related information specific to the device.",
Ref: ref("k8s.io/api/resource/v1beta1.NetworkDeviceData"),
},
},
},
Required: []string{"driver", "pool", "device"},
},
},
Dependencies: []string{
"k8s.io/api/resource/v1beta1.NetworkDeviceData", "k8s.io/apimachinery/pkg/apis/meta/v1.Condition", "k8s.io/apimachinery/pkg/runtime.RawExtension"},
}
}
func schema_k8sio_api_resource_v1beta1_AllocationResult(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -48403,6 +48626,53 @@ func schema_k8sio_api_resource_v1beta1_DeviceSelector(ref common.ReferenceCallba
}
}
func schema_k8sio_api_resource_v1beta1_NetworkDeviceData(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "NetworkDeviceData provides network-related details for the allocated device. This information may be filled by drivers or other components to configure or identify the device within a network context.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"interfaceName": {
SchemaProps: spec.SchemaProps{
Description: "InterfaceName specifies the name of the network interface associated with the allocated device. This might be the name of a physical or virtual network interface being configured in the pod.\n\nMust not be longer than 256 characters.",
Type: []string{"string"},
Format: "",
},
},
"ips": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-type": "atomic",
},
},
SchemaProps: spec.SchemaProps{
Description: "IPs lists the network addresses assigned to the device's network interface. This can include both IPv4 and IPv6 addresses. The IPs are in the CIDR notation, which includes both the address and the associated subnet mask. e.g.: \"192.0.2.5/24\" for IPv4 and \"2001:db8::5/64\" for IPv6.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
"hardwareAddress": {
SchemaProps: spec.SchemaProps{
Description: "HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface.\n\nMust not be longer than 128 characters.",
Type: []string{"string"},
Format: "",
},
},
},
},
},
}
}
func schema_k8sio_api_resource_v1beta1_OpaqueDeviceConfiguration(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -48639,11 +48909,35 @@ func schema_k8sio_api_resource_v1beta1_ResourceClaimStatus(ref common.ReferenceC
},
},
},
"devices": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-map-keys": []interface{}{
"driver",
"device",
"pool",
},
"x-kubernetes-list-type": "map",
},
},
SchemaProps: spec.SchemaProps{
Description: "Devices contains the status of each device allocated for this claim, as reported by the driver. This can include driver-specific information. Entries are owned by their respective drivers.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/api/resource/v1beta1.AllocatedDeviceStatus"),
},
},
},
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/resource/v1beta1.AllocationResult", "k8s.io/api/resource/v1beta1.ResourceClaimConsumerReference"},
"k8s.io/api/resource/v1beta1.AllocatedDeviceStatus", "k8s.io/api/resource/v1beta1.AllocationResult", "k8s.io/api/resource/v1beta1.ResourceClaimConsumerReference"},
}
}

View File

@@ -24,11 +24,13 @@ import (
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/storage"
"k8s.io/apiserver/pkg/storage/names"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/dynamic-resource-allocation/structured"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/resource"
"k8s.io/kubernetes/pkg/apis/resource/validation"
@@ -140,6 +142,7 @@ func (resourceclaimStatusStrategy) PrepareForUpdate(ctx context.Context, obj, ol
newClaim.Spec = oldClaim.Spec
metav1.ResetObjectMetaForStatus(&newClaim.ObjectMeta, &oldClaim.ObjectMeta)
dropDeallocatedStatusDevices(newClaim, oldClaim)
dropDisabledFields(newClaim, oldClaim)
}
@@ -181,6 +184,7 @@ func toSelectableFields(claim *resource.ResourceClaim) fields.Set {
// dropDisabledFields removes fields which are covered by a feature gate.
func dropDisabledFields(newClaim, oldClaim *resource.ResourceClaim) {
dropDisabledDRAAdminAccessFields(newClaim, oldClaim)
dropDisabledDRAResourceClaimDeviceStatusFields(newClaim, oldClaim)
}
func dropDisabledDRAAdminAccessFields(newClaim, oldClaim *resource.ResourceClaim) {
@@ -231,3 +235,53 @@ func draAdminAccessFeatureInUse(claim *resource.ResourceClaim) bool {
return false
}
func dropDisabledDRAResourceClaimDeviceStatusFields(newClaim, oldClaim *resource.ResourceClaim) {
isDRAResourceClaimDeviceStatusInUse := (oldClaim != nil && len(oldClaim.Status.Devices) > 0)
// drop resourceClaim.Status.Devices field if feature gate is not enabled and it was not in use
if !utilfeature.DefaultFeatureGate.Enabled(features.DRAResourceClaimDeviceStatus) && !isDRAResourceClaimDeviceStatusInUse {
newClaim.Status.Devices = nil
}
}
// dropDeallocatedStatusDevices removes the status.devices that were allocated
// in the oldClaim and that have been removed in the newClaim.
func dropDeallocatedStatusDevices(newClaim, oldClaim *resource.ResourceClaim) {
isDRAResourceClaimDeviceStatusInUse := (oldClaim != nil && len(oldClaim.Status.Devices) > 0)
if !utilfeature.DefaultFeatureGate.Enabled(features.DRAResourceClaimDeviceStatus) && !isDRAResourceClaimDeviceStatusInUse {
return
}
deallocatedDevices := sets.New[structured.DeviceID]()
if oldClaim.Status.Allocation != nil {
// Get all devices in the oldClaim.
for _, result := range oldClaim.Status.Allocation.Devices.Results {
deviceID := structured.MakeDeviceID(result.Driver, result.Pool, result.Device)
deallocatedDevices.Insert(deviceID)
}
}
// Remove devices from deallocatedDevices that are still in newClaim.
if newClaim.Status.Allocation != nil {
for _, result := range newClaim.Status.Allocation.Devices.Results {
deviceID := structured.MakeDeviceID(result.Driver, result.Pool, result.Device)
deallocatedDevices.Delete(deviceID)
}
}
// Remove from newClaim.Status.Devices.
n := 0
for _, device := range newClaim.Status.Devices {
deviceID := structured.MakeDeviceID(device.Driver, device.Pool, device.Device)
if !deallocatedDevices.Has(deviceID) {
newClaim.Status.Devices[n] = device
n++
}
}
newClaim.Status.Devices = newClaim.Status.Devices[:n]
if len(newClaim.Status.Devices) == 0 {
newClaim.Status.Devices = nil
}
}

View File

@@ -133,6 +133,13 @@ var objWithAdminAccessStatus = &resource.ResourceClaim{
},
}
const (
testRequest = "test-request"
testDriver = "test-driver"
testPool = "test-pool"
testDevice = "test-device"
)
func TestStrategy(t *testing.T) {
if !Strategy.NamespaceScoped() {
t.Errorf("ResourceClaim must be namespace scoped")
@@ -275,11 +282,12 @@ func TestStatusStrategyUpdate(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
testcases := map[string]struct {
oldObj *resource.ResourceClaim
newObj *resource.ResourceClaim
adminAccess bool
expectValidationError bool
expectObj *resource.ResourceClaim
oldObj *resource.ResourceClaim
newObj *resource.ResourceClaim
adminAccess bool
deviceStatusFeatureGate bool
expectValidationError bool
expectObj *resource.ResourceClaim
}{
"no-changes-okay": {
oldObj: obj,
@@ -347,11 +355,123 @@ func TestStatusStrategyUpdate(t *testing.T) {
return oldObj
}(),
},
"drop-fields-devices-status": {
oldObj: func() *resource.ResourceClaim {
obj := obj.DeepCopy()
addSpecDevicesRequest(obj, testRequest)
addStatusAllocationDevicesResults(obj, testDriver, testPool, testDevice, testRequest)
return obj
}(),
newObj: func() *resource.ResourceClaim { // Status is added
obj := obj.DeepCopy()
addSpecDevicesRequest(obj, testRequest)
addStatusAllocationDevicesResults(obj, testDriver, testPool, testDevice, testRequest)
addStatusDevices(obj, testDriver, testPool, testDevice)
return obj
}(),
deviceStatusFeatureGate: false,
expectObj: func() *resource.ResourceClaim { // Status is no longer there
obj := obj.DeepCopy()
addSpecDevicesRequest(obj, testRequest)
addStatusAllocationDevicesResults(obj, testDriver, testPool, testDevice, testRequest)
return obj
}(),
},
"keep-fields-devices-status-disable-feature-gate": {
oldObj: func() *resource.ResourceClaim {
obj := obj.DeepCopy()
addSpecDevicesRequest(obj, testRequest)
addStatusAllocationDevicesResults(obj, testDriver, testPool, testDevice, testRequest)
addStatusDevices(obj, testDriver, testPool, testDevice)
return obj
}(),
newObj: func() *resource.ResourceClaim { // Status is added
obj := obj.DeepCopy()
addSpecDevicesRequest(obj, testRequest)
addStatusAllocationDevicesResults(obj, testDriver, testPool, testDevice, testRequest)
addStatusDevices(obj, testDriver, testPool, testDevice)
return obj
}(),
deviceStatusFeatureGate: false,
expectObj: func() *resource.ResourceClaim { // Status is still there (as the status was set in the old object)
obj := obj.DeepCopy()
addSpecDevicesRequest(obj, testRequest)
addStatusAllocationDevicesResults(obj, testDriver, testPool, testDevice, testRequest)
addStatusDevices(obj, testDriver, testPool, testDevice)
return obj
}(),
},
"keep-fields-devices-status": {
oldObj: func() *resource.ResourceClaim {
obj := obj.DeepCopy()
addSpecDevicesRequest(obj, testRequest)
addStatusAllocationDevicesResults(obj, testDriver, testPool, testDevice, testRequest)
return obj
}(),
newObj: func() *resource.ResourceClaim { // Status is added
obj := obj.DeepCopy()
addSpecDevicesRequest(obj, testRequest)
addStatusAllocationDevicesResults(obj, testDriver, testPool, testDevice, testRequest)
addStatusDevices(obj, testDriver, testPool, testDevice)
return obj
}(),
deviceStatusFeatureGate: true,
expectObj: func() *resource.ResourceClaim { // Status is still there
obj := obj.DeepCopy()
addSpecDevicesRequest(obj, testRequest)
addStatusAllocationDevicesResults(obj, testDriver, testPool, testDevice, testRequest)
addStatusDevices(obj, testDriver, testPool, testDevice)
return obj
}(),
},
"drop-status-deallocated-device": {
oldObj: func() *resource.ResourceClaim {
obj := obj.DeepCopy()
addSpecDevicesRequest(obj, testRequest)
addStatusAllocationDevicesResults(obj, testDriver, testPool, testDevice, testRequest)
addStatusDevices(obj, testDriver, testPool, testDevice)
return obj
}(),
newObj: func() *resource.ResourceClaim { // device is deallocated
obj := obj.DeepCopy()
addSpecDevicesRequest(obj, testRequest)
addStatusDevices(obj, testDriver, testPool, testDevice)
return obj
}(),
deviceStatusFeatureGate: true,
expectObj: func() *resource.ResourceClaim { // Status is no longer there
obj := obj.DeepCopy()
addSpecDevicesRequest(obj, testRequest)
return obj
}(),
},
"drop-status-deallocated-device-disable-feature-gate": {
oldObj: func() *resource.ResourceClaim {
obj := obj.DeepCopy()
addSpecDevicesRequest(obj, testRequest)
addStatusAllocationDevicesResults(obj, testDriver, testPool, testDevice, testRequest)
addStatusDevices(obj, testDriver, testPool, testDevice)
return obj
}(),
newObj: func() *resource.ResourceClaim { // device is deallocated
obj := obj.DeepCopy()
addSpecDevicesRequest(obj, testRequest)
addStatusDevices(obj, testDriver, testPool, testDevice)
return obj
}(),
deviceStatusFeatureGate: false,
expectObj: func() *resource.ResourceClaim { // Status is no longer there
obj := obj.DeepCopy()
addSpecDevicesRequest(obj, testRequest)
return obj
}(),
},
}
for name, tc := range testcases {
t.Run(name, func(t *testing.T) {
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAAdminAccess, tc.adminAccess)
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAResourceClaimDeviceStatus, tc.deviceStatusFeatureGate)
oldObj := tc.oldObj.DeepCopy()
newObj := tc.newObj.DeepCopy()
@@ -377,3 +497,29 @@ func TestStatusStrategyUpdate(t *testing.T) {
})
}
}
func addSpecDevicesRequest(resourceClaim *resource.ResourceClaim, request string) {
resourceClaim.Spec.Devices.Requests = append(resourceClaim.Spec.Devices.Requests, resource.DeviceRequest{
Name: request,
})
}
func addStatusAllocationDevicesResults(resourceClaim *resource.ResourceClaim, driver string, pool string, device string, request string) {
if resourceClaim.Status.Allocation == nil {
resourceClaim.Status.Allocation = &resource.AllocationResult{}
}
resourceClaim.Status.Allocation.Devices.Results = append(resourceClaim.Status.Allocation.Devices.Results, resource.DeviceRequestAllocationResult{
Request: request,
Driver: driver,
Pool: pool,
Device: device,
})
}
func addStatusDevices(resourceClaim *resource.ResourceClaim, driver string, pool string, device string) {
resourceClaim.Status.Devices = append(resourceClaim.Status.Devices, resource.AllocatedDeviceStatus{
Driver: driver,
Pool: pool,
Device: device,
})
}

File diff suppressed because it is too large Load Diff

View File

@@ -30,6 +30,56 @@ import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "k8s.io/api/resource/v1alpha3";
// AllocatedDeviceStatus contains the status of an allocated device, if the
// driver chooses to report it. This may include driver-specific information.
message AllocatedDeviceStatus {
// Driver specifies the name of the DRA driver whose kubelet
// plugin should be invoked to process the allocation once the claim is
// needed on a node.
//
// Must be a DNS subdomain and should end with a DNS domain owned by the
// vendor of the driver.
//
// +required
optional string driver = 1;
// This name together with the driver name and the device name field
// identify which device was allocated (`<driver name>/<pool name>/<device name>`).
//
// Must not be longer than 253 characters and may contain one or more
// DNS sub-domains separated by slashes.
//
// +required
optional string pool = 2;
// Device references one device instance via its name in the driver's
// resource pool. It must be a DNS label.
//
// +required
optional string device = 3;
// Conditions contains the latest observation of the device's state.
// If the device has been configured according to the class and claim
// config references, the `Ready` condition should be True.
//
// +optional
// +listType=map
// +listMapKey=type
repeated .k8s.io.apimachinery.pkg.apis.meta.v1.Condition conditions = 4;
// Data contains arbitrary driver-specific data.
//
// The length of the raw data must be smaller or equal to 10 Ki.
//
// +optional
optional .k8s.io.apimachinery.pkg.runtime.RawExtension data = 5;
// NetworkData contains network-related information specific to the device.
//
// +optional
optional NetworkDeviceData networkData = 6;
}
// AllocationResult contains attributes of an allocated resource.
message AllocationResult {
// Devices is the result of allocating devices.
@@ -480,6 +530,37 @@ message DeviceSelector {
optional CELDeviceSelector cel = 1;
}
// NetworkDeviceData provides network-related details for the allocated device.
// This information may be filled by drivers or other components to configure
// or identify the device within a network context.
message NetworkDeviceData {
// InterfaceName specifies the name of the network interface associated with
// the allocated device. This might be the name of a physical or virtual
// network interface being configured in the pod.
//
// Must not be longer than 256 characters.
//
// +optional
optional string interfaceName = 1;
// IPs lists the network addresses assigned to the device's network interface.
// This can include both IPv4 and IPv6 addresses.
// The IPs are in the CIDR notation, which includes both the address and the
// associated subnet mask.
// e.g.: "192.0.2.5/24" for IPv4 and "2001:db8::5/64" for IPv6.
//
// +optional
// +listType=atomic
repeated string ips = 2;
// HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface.
//
// Must not be longer than 128 characters.
//
// +optional
optional string hardwareAddress = 3;
}
// OpaqueDeviceConfiguration contains configuration parameters for a driver
// in a format defined by the driver vendor.
message OpaqueDeviceConfiguration {
@@ -603,6 +684,18 @@ message ResourceClaimStatus {
// +patchStrategy=merge
// +patchMergeKey=uid
repeated ResourceClaimConsumerReference reservedFor = 2;
// Devices contains the status of each device allocated for this
// claim, as reported by the driver. This can include driver-specific
// information. Entries are owned by their respective drivers.
//
// +optional
// +listType=map
// +listMapKey=driver
// +listMapKey=device
// +listMapKey=pool
// +featureGate=DRAResourceClaimDeviceStatus
repeated AllocatedDeviceStatus devices = 4;
}
// ResourceClaimTemplate is used to produce ResourceClaim objects.

View File

@@ -701,6 +701,18 @@ type ResourceClaimStatus struct {
// it got removed. May be reused once decoding v1alpha3 is no longer
// supported.
// DeallocationRequested bool `json:"deallocationRequested,omitempty" protobuf:"bytes,3,opt,name=deallocationRequested"`
// Devices contains the status of each device allocated for this
// claim, as reported by the driver. This can include driver-specific
// information. Entries are owned by their respective drivers.
//
// +optional
// +listType=map
// +listMapKey=driver
// +listMapKey=device
// +listMapKey=pool
// +featureGate=DRAResourceClaimDeviceStatus
Devices []AllocatedDeviceStatus `json:"devices,omitempty" protobuf:"bytes,4,opt,name=devices"`
}
// ReservedForMaxSize is the maximum number of entries in
@@ -986,3 +998,84 @@ type ResourceClaimTemplateList struct {
// Items is the list of resource claim templates.
Items []ResourceClaimTemplate `json:"items" protobuf:"bytes,2,rep,name=items"`
}
// AllocatedDeviceStatus contains the status of an allocated device, if the
// driver chooses to report it. This may include driver-specific information.
type AllocatedDeviceStatus struct {
// Driver specifies the name of the DRA driver whose kubelet
// plugin should be invoked to process the allocation once the claim is
// needed on a node.
//
// Must be a DNS subdomain and should end with a DNS domain owned by the
// vendor of the driver.
//
// +required
Driver string `json:"driver" protobuf:"bytes,1,rep,name=driver"`
// This name together with the driver name and the device name field
// identify which device was allocated (`<driver name>/<pool name>/<device name>`).
//
// Must not be longer than 253 characters and may contain one or more
// DNS sub-domains separated by slashes.
//
// +required
Pool string `json:"pool" protobuf:"bytes,2,rep,name=pool"`
// Device references one device instance via its name in the driver's
// resource pool. It must be a DNS label.
//
// +required
Device string `json:"device" protobuf:"bytes,3,rep,name=device"`
// Conditions contains the latest observation of the device's state.
// If the device has been configured according to the class and claim
// config references, the `Ready` condition should be True.
//
// +optional
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions" protobuf:"bytes,4,opt,name=conditions"`
// Data contains arbitrary driver-specific data.
//
// The length of the raw data must be smaller or equal to 10 Ki.
//
// +optional
Data runtime.RawExtension `json:"data,omitempty" protobuf:"bytes,5,opt,name=data"`
// NetworkData contains network-related information specific to the device.
//
// +optional
NetworkData *NetworkDeviceData `json:"networkData,omitempty" protobuf:"bytes,6,opt,name=networkData"`
}
// NetworkDeviceData provides network-related details for the allocated device.
// This information may be filled by drivers or other components to configure
// or identify the device within a network context.
type NetworkDeviceData struct {
// InterfaceName specifies the name of the network interface associated with
// the allocated device. This might be the name of a physical or virtual
// network interface being configured in the pod.
//
// Must not be longer than 256 characters.
//
// +optional
InterfaceName string `json:"interfaceName,omitempty" protobuf:"bytes,1,opt,name=interfaceName"`
// IPs lists the network addresses assigned to the device's network interface.
// This can include both IPv4 and IPv6 addresses.
// The IPs are in the CIDR notation, which includes both the address and the
// associated subnet mask.
// e.g.: "192.0.2.5/24" for IPv4 and "2001:db8::5/64" for IPv6.
//
// +optional
// +listType=atomic
IPs []string `json:"ips,omitempty" protobuf:"bytes,2,opt,name=ips"`
// HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface.
//
// Must not be longer than 128 characters.
//
// +optional
HardwareAddress string `json:"hardwareAddress,omitempty" protobuf:"bytes,3,opt,name=hardwareAddress"`
}

View File

@@ -27,6 +27,20 @@ package v1alpha3
// Those methods can be generated by using hack/update-codegen.sh
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT.
var map_AllocatedDeviceStatus = map[string]string{
"": "AllocatedDeviceStatus contains the status of an allocated device, if the driver chooses to report it. This may include driver-specific information.",
"driver": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.",
"pool": "This name together with the driver name and the device name field identify which device was allocated (`<driver name>/<pool name>/<device name>`).\n\nMust not be longer than 253 characters and may contain one or more DNS sub-domains separated by slashes.",
"device": "Device references one device instance via its name in the driver's resource pool. It must be a DNS label.",
"conditions": "Conditions contains the latest observation of the device's state. If the device has been configured according to the class and claim config references, the `Ready` condition should be True.",
"data": "Data contains arbitrary driver-specific data.\n\nThe length of the raw data must be smaller or equal to 10 Ki.",
"networkData": "NetworkData contains network-related information specific to the device.",
}
func (AllocatedDeviceStatus) SwaggerDoc() map[string]string {
return map_AllocatedDeviceStatus
}
var map_AllocationResult = map[string]string{
"": "AllocationResult contains attributes of an allocated resource.",
"devices": "Devices is the result of allocating devices.",
@@ -211,6 +225,17 @@ func (DeviceSelector) SwaggerDoc() map[string]string {
return map_DeviceSelector
}
var map_NetworkDeviceData = map[string]string{
"": "NetworkDeviceData provides network-related details for the allocated device. This information may be filled by drivers or other components to configure or identify the device within a network context.",
"interfaceName": "InterfaceName specifies the name of the network interface associated with the allocated device. This might be the name of a physical or virtual network interface being configured in the pod.\n\nMust not be longer than 256 characters.",
"ips": "IPs lists the network addresses assigned to the device's network interface. This can include both IPv4 and IPv6 addresses. The IPs are in the CIDR notation, which includes both the address and the associated subnet mask. e.g.: \"192.0.2.5/24\" for IPv4 and \"2001:db8::5/64\" for IPv6.",
"hardwareAddress": "HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface.\n\nMust not be longer than 128 characters.",
}
func (NetworkDeviceData) SwaggerDoc() map[string]string {
return map_NetworkDeviceData
}
var map_OpaqueDeviceConfiguration = map[string]string{
"": "OpaqueDeviceConfiguration contains configuration parameters for a driver in a format defined by the driver vendor.",
"driver": "Driver is used to determine which kubelet plugin needs to be passed these configuration parameters.\n\nAn admission policy provided by the driver developer could use this to decide whether it needs to validate them.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.",
@@ -267,6 +292,7 @@ var map_ResourceClaimStatus = map[string]string{
"": "ResourceClaimStatus tracks whether the resource has been allocated and what the result of that was.",
"allocation": "Allocation is set once the claim has been allocated successfully.",
"reservedFor": "ReservedFor indicates which entities are currently allowed to use the claim. A Pod which references a ResourceClaim which is not reserved for that Pod will not be started. A claim that is in use or might be in use because it has been reserved must not get deallocated.\n\nIn a cluster with multiple scheduler instances, two pods might get scheduled concurrently by different schedulers. When they reference the same ResourceClaim which already has reached its maximum number of consumers, only one pod can be scheduled.\n\nBoth schedulers try to add their pod to the claim.status.reservedFor field, but only the update that reaches the API server first gets stored. The other one fails with an error and the scheduler which issued it knows that it must put the pod back into the queue, waiting for the ResourceClaim to become usable again.\n\nThere can be at most 32 such reservations. This may get increased in the future, but not reduced.",
"devices": "Devices contains the status of each device allocated for this claim, as reported by the driver. This can include driver-specific information. Entries are owned by their respective drivers.",
}
func (ResourceClaimStatus) SwaggerDoc() map[string]string {

View File

@@ -22,18 +22,48 @@ limitations under the License.
package v1alpha3
import (
v1 "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
resource "k8s.io/apimachinery/pkg/api/resource"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllocatedDeviceStatus) DeepCopyInto(out *AllocatedDeviceStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
in.Data.DeepCopyInto(&out.Data)
if in.NetworkData != nil {
in, out := &in.NetworkData, &out.NetworkData
*out = new(NetworkDeviceData)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AllocatedDeviceStatus.
func (in *AllocatedDeviceStatus) DeepCopy() *AllocatedDeviceStatus {
if in == nil {
return nil
}
out := new(AllocatedDeviceStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllocationResult) DeepCopyInto(out *AllocationResult) {
*out = *in
in.Devices.DeepCopyInto(&out.Devices)
if in.NodeSelector != nil {
in, out := &in.NodeSelector, &out.NodeSelector
*out = new(v1.NodeSelector)
*out = new(corev1.NodeSelector)
(*in).DeepCopyInto(*out)
}
return
@@ -487,6 +517,27 @@ func (in *DeviceSelector) DeepCopy() *DeviceSelector {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NetworkDeviceData) DeepCopyInto(out *NetworkDeviceData) {
*out = *in
if in.IPs != nil {
in, out := &in.IPs, &out.IPs
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkDeviceData.
func (in *NetworkDeviceData) DeepCopy() *NetworkDeviceData {
if in == nil {
return nil
}
out := new(NetworkDeviceData)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OpaqueDeviceConfiguration) DeepCopyInto(out *OpaqueDeviceConfiguration) {
*out = *in
@@ -611,6 +662,13 @@ func (in *ResourceClaimStatus) DeepCopyInto(out *ResourceClaimStatus) {
*out = make([]ResourceClaimConsumerReference, len(*in))
copy(*out, *in)
}
if in.Devices != nil {
in, out := &in.Devices, &out.Devices
*out = make([]AllocatedDeviceStatus, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
@@ -784,7 +842,7 @@ func (in *ResourceSliceSpec) DeepCopyInto(out *ResourceSliceSpec) {
out.Pool = in.Pool
if in.NodeSelector != nil {
in, out := &in.NodeSelector, &out.NodeSelector
*out = new(v1.NodeSelector)
*out = new(corev1.NodeSelector)
(*in).DeepCopyInto(*out)
}
if in.Devices != nil {

File diff suppressed because it is too large Load Diff

View File

@@ -30,6 +30,56 @@ import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "k8s.io/api/resource/v1beta1";
// AllocatedDeviceStatus contains the status of an allocated device, if the
// driver chooses to report it. This may include driver-specific information.
message AllocatedDeviceStatus {
// Driver specifies the name of the DRA driver whose kubelet
// plugin should be invoked to process the allocation once the claim is
// needed on a node.
//
// Must be a DNS subdomain and should end with a DNS domain owned by the
// vendor of the driver.
//
// +required
optional string driver = 1;
// This name together with the driver name and the device name field
// identify which device was allocated (`<driver name>/<pool name>/<device name>`).
//
// Must not be longer than 253 characters and may contain one or more
// DNS sub-domains separated by slashes.
//
// +required
optional string pool = 2;
// Device references one device instance via its name in the driver's
// resource pool. It must be a DNS label.
//
// +required
optional string device = 3;
// Conditions contains the latest observation of the device's state.
// If the device has been configured according to the class and claim
// config references, the `Ready` condition should be True.
//
// +optional
// +listType=map
// +listMapKey=type
repeated .k8s.io.apimachinery.pkg.apis.meta.v1.Condition conditions = 4;
// Data contains arbitrary driver-specific data.
//
// The length of the raw data must be smaller or equal to 10 Ki.
//
// +optional
optional .k8s.io.apimachinery.pkg.runtime.RawExtension data = 5;
// NetworkData contains network-related information specific to the device.
//
// +optional
optional NetworkDeviceData networkData = 6;
}
// AllocationResult contains attributes of an allocated resource.
message AllocationResult {
// Devices is the result of allocating devices.
@@ -488,6 +538,37 @@ message DeviceSelector {
optional CELDeviceSelector cel = 1;
}
// NetworkDeviceData provides network-related details for the allocated device.
// This information may be filled by drivers or other components to configure
// or identify the device within a network context.
message NetworkDeviceData {
// InterfaceName specifies the name of the network interface associated with
// the allocated device. This might be the name of a physical or virtual
// network interface being configured in the pod.
//
// Must not be longer than 256 characters.
//
// +optional
optional string interfaceName = 1;
// IPs lists the network addresses assigned to the device's network interface.
// This can include both IPv4 and IPv6 addresses.
// The IPs are in the CIDR notation, which includes both the address and the
// associated subnet mask.
// e.g.: "192.0.2.5/24" for IPv4 and "2001:db8::5/64" for IPv6.
//
// +optional
// +listType=atomic
repeated string ips = 2;
// HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface.
//
// Must not be longer than 128 characters.
//
// +optional
optional string hardwareAddress = 3;
}
// OpaqueDeviceConfiguration contains configuration parameters for a driver
// in a format defined by the driver vendor.
message OpaqueDeviceConfiguration {
@@ -611,6 +692,18 @@ message ResourceClaimStatus {
// +patchStrategy=merge
// +patchMergeKey=uid
repeated ResourceClaimConsumerReference reservedFor = 2;
// Devices contains the status of each device allocated for this
// claim, as reported by the driver. This can include driver-specific
// information. Entries are owned by their respective drivers.
//
// +optional
// +listType=map
// +listMapKey=driver
// +listMapKey=device
// +listMapKey=pool
// +featureGate=DRAResourceClaimDeviceStatus
repeated AllocatedDeviceStatus devices = 4;
}
// ResourceClaimTemplate is used to produce ResourceClaim objects.

View File

@@ -709,6 +709,18 @@ type ResourceClaimStatus struct {
// it got removed. May be reused once decoding v1alpha3 is no longer
// supported.
// DeallocationRequested bool `json:"deallocationRequested,omitempty" protobuf:"bytes,3,opt,name=deallocationRequested"`
// Devices contains the status of each device allocated for this
// claim, as reported by the driver. This can include driver-specific
// information. Entries are owned by their respective drivers.
//
// +optional
// +listType=map
// +listMapKey=driver
// +listMapKey=device
// +listMapKey=pool
// +featureGate=DRAResourceClaimDeviceStatus
Devices []AllocatedDeviceStatus `json:"devices,omitempty" protobuf:"bytes,4,opt,name=devices"`
}
// ReservedForMaxSize is the maximum number of entries in
@@ -989,3 +1001,84 @@ type ResourceClaimTemplateList struct {
// Items is the list of resource claim templates.
Items []ResourceClaimTemplate `json:"items" protobuf:"bytes,2,rep,name=items"`
}
// AllocatedDeviceStatus contains the status of an allocated device, if the
// driver chooses to report it. This may include driver-specific information.
type AllocatedDeviceStatus struct {
// Driver specifies the name of the DRA driver whose kubelet
// plugin should be invoked to process the allocation once the claim is
// needed on a node.
//
// Must be a DNS subdomain and should end with a DNS domain owned by the
// vendor of the driver.
//
// +required
Driver string `json:"driver" protobuf:"bytes,1,rep,name=driver"`
// This name together with the driver name and the device name field
// identify which device was allocated (`<driver name>/<pool name>/<device name>`).
//
// Must not be longer than 253 characters and may contain one or more
// DNS sub-domains separated by slashes.
//
// +required
Pool string `json:"pool" protobuf:"bytes,2,rep,name=pool"`
// Device references one device instance via its name in the driver's
// resource pool. It must be a DNS label.
//
// +required
Device string `json:"device" protobuf:"bytes,3,rep,name=device"`
// Conditions contains the latest observation of the device's state.
// If the device has been configured according to the class and claim
// config references, the `Ready` condition should be True.
//
// +optional
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition `json:"conditions" protobuf:"bytes,4,opt,name=conditions"`
// Data contains arbitrary driver-specific data.
//
// The length of the raw data must be smaller or equal to 10 Ki.
//
// +optional
Data runtime.RawExtension `json:"data,omitempty" protobuf:"bytes,5,opt,name=data"`
// NetworkData contains network-related information specific to the device.
//
// +optional
NetworkData *NetworkDeviceData `json:"networkData,omitempty" protobuf:"bytes,6,opt,name=networkData"`
}
// NetworkDeviceData provides network-related details for the allocated device.
// This information may be filled by drivers or other components to configure
// or identify the device within a network context.
type NetworkDeviceData struct {
// InterfaceName specifies the name of the network interface associated with
// the allocated device. This might be the name of a physical or virtual
// network interface being configured in the pod.
//
// Must not be longer than 256 characters.
//
// +optional
InterfaceName string `json:"interfaceName,omitempty" protobuf:"bytes,1,opt,name=interfaceName"`
// IPs lists the network addresses assigned to the device's network interface.
// This can include both IPv4 and IPv6 addresses.
// The IPs are in the CIDR notation, which includes both the address and the
// associated subnet mask.
// e.g.: "192.0.2.5/24" for IPv4 and "2001:db8::5/64" for IPv6.
//
// +optional
// +listType=atomic
IPs []string `json:"ips,omitempty" protobuf:"bytes,2,opt,name=ips"`
// HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface.
//
// Must not be longer than 128 characters.
//
// +optional
HardwareAddress string `json:"hardwareAddress,omitempty" protobuf:"bytes,3,opt,name=hardwareAddress"`
}

View File

@@ -27,6 +27,20 @@ package v1beta1
// Those methods can be generated by using hack/update-codegen.sh
// AUTO-GENERATED FUNCTIONS START HERE. DO NOT EDIT.
var map_AllocatedDeviceStatus = map[string]string{
"": "AllocatedDeviceStatus contains the status of an allocated device, if the driver chooses to report it. This may include driver-specific information.",
"driver": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.",
"pool": "This name together with the driver name and the device name field identify which device was allocated (`<driver name>/<pool name>/<device name>`).\n\nMust not be longer than 253 characters and may contain one or more DNS sub-domains separated by slashes.",
"device": "Device references one device instance via its name in the driver's resource pool. It must be a DNS label.",
"conditions": "Conditions contains the latest observation of the device's state. If the device has been configured according to the class and claim config references, the `Ready` condition should be True.",
"data": "Data contains arbitrary driver-specific data.\n\nThe length of the raw data must be smaller or equal to 10 Ki.",
"networkData": "NetworkData contains network-related information specific to the device.",
}
func (AllocatedDeviceStatus) SwaggerDoc() map[string]string {
return map_AllocatedDeviceStatus
}
var map_AllocationResult = map[string]string{
"": "AllocationResult contains attributes of an allocated resource.",
"devices": "Devices is the result of allocating devices.",
@@ -220,6 +234,17 @@ func (DeviceSelector) SwaggerDoc() map[string]string {
return map_DeviceSelector
}
var map_NetworkDeviceData = map[string]string{
"": "NetworkDeviceData provides network-related details for the allocated device. This information may be filled by drivers or other components to configure or identify the device within a network context.",
"interfaceName": "InterfaceName specifies the name of the network interface associated with the allocated device. This might be the name of a physical or virtual network interface being configured in the pod.\n\nMust not be longer than 256 characters.",
"ips": "IPs lists the network addresses assigned to the device's network interface. This can include both IPv4 and IPv6 addresses. The IPs are in the CIDR notation, which includes both the address and the associated subnet mask. e.g.: \"192.0.2.5/24\" for IPv4 and \"2001:db8::5/64\" for IPv6.",
"hardwareAddress": "HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface.\n\nMust not be longer than 128 characters.",
}
func (NetworkDeviceData) SwaggerDoc() map[string]string {
return map_NetworkDeviceData
}
var map_OpaqueDeviceConfiguration = map[string]string{
"": "OpaqueDeviceConfiguration contains configuration parameters for a driver in a format defined by the driver vendor.",
"driver": "Driver is used to determine which kubelet plugin needs to be passed these configuration parameters.\n\nAn admission policy provided by the driver developer could use this to decide whether it needs to validate them.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.",
@@ -276,6 +301,7 @@ var map_ResourceClaimStatus = map[string]string{
"": "ResourceClaimStatus tracks whether the resource has been allocated and what the result of that was.",
"allocation": "Allocation is set once the claim has been allocated successfully.",
"reservedFor": "ReservedFor indicates which entities are currently allowed to use the claim. A Pod which references a ResourceClaim which is not reserved for that Pod will not be started. A claim that is in use or might be in use because it has been reserved must not get deallocated.\n\nIn a cluster with multiple scheduler instances, two pods might get scheduled concurrently by different schedulers. When they reference the same ResourceClaim which already has reached its maximum number of consumers, only one pod can be scheduled.\n\nBoth schedulers try to add their pod to the claim.status.reservedFor field, but only the update that reaches the API server first gets stored. The other one fails with an error and the scheduler which issued it knows that it must put the pod back into the queue, waiting for the ResourceClaim to become usable again.\n\nThere can be at most 32 such reservations. This may get increased in the future, but not reduced.",
"devices": "Devices contains the status of each device allocated for this claim, as reported by the driver. This can include driver-specific information. Entries are owned by their respective drivers.",
}
func (ResourceClaimStatus) SwaggerDoc() map[string]string {

View File

@@ -22,17 +22,47 @@ limitations under the License.
package v1beta1
import (
v1 "k8s.io/api/core/v1"
corev1 "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllocatedDeviceStatus) DeepCopyInto(out *AllocatedDeviceStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
in.Data.DeepCopyInto(&out.Data)
if in.NetworkData != nil {
in, out := &in.NetworkData, &out.NetworkData
*out = new(NetworkDeviceData)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AllocatedDeviceStatus.
func (in *AllocatedDeviceStatus) DeepCopy() *AllocatedDeviceStatus {
if in == nil {
return nil
}
out := new(AllocatedDeviceStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AllocationResult) DeepCopyInto(out *AllocationResult) {
*out = *in
in.Devices.DeepCopyInto(&out.Devices)
if in.NodeSelector != nil {
in, out := &in.NodeSelector, &out.NodeSelector
*out = new(v1.NodeSelector)
*out = new(corev1.NodeSelector)
(*in).DeepCopyInto(*out)
}
return
@@ -503,6 +533,27 @@ func (in *DeviceSelector) DeepCopy() *DeviceSelector {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NetworkDeviceData) DeepCopyInto(out *NetworkDeviceData) {
*out = *in
if in.IPs != nil {
in, out := &in.IPs, &out.IPs
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkDeviceData.
func (in *NetworkDeviceData) DeepCopy() *NetworkDeviceData {
if in == nil {
return nil
}
out := new(NetworkDeviceData)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OpaqueDeviceConfiguration) DeepCopyInto(out *OpaqueDeviceConfiguration) {
*out = *in
@@ -627,6 +678,13 @@ func (in *ResourceClaimStatus) DeepCopyInto(out *ResourceClaimStatus) {
*out = make([]ResourceClaimConsumerReference, len(*in))
copy(*out, *in)
}
if in.Devices != nil {
in, out := &in.Devices, &out.Devices
*out = make([]AllocatedDeviceStatus, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
@@ -800,7 +858,7 @@ func (in *ResourceSliceSpec) DeepCopyInto(out *ResourceSliceSpec) {
out.Pool = in.Pool
if in.NodeSelector != nil {
in, out := &in.NodeSelector, &out.NodeSelector
*out = new(v1.NodeSelector)
*out = new(corev1.NodeSelector)
(*in).DeepCopyInto(*out)
}
if in.Devices != nil {

View File

@@ -157,6 +157,40 @@
"name": "nameValue",
"uid": "uidValue"
}
],
"devices": [
{
"driver": "driverValue",
"pool": "poolValue",
"device": "deviceValue",
"conditions": [
{
"type": "typeValue",
"status": "statusValue",
"observedGeneration": 3,
"lastTransitionTime": "2004-01-01T01:01:01Z",
"reason": "reasonValue",
"message": "messageValue"
}
],
"data": {
"apiVersion": "example.com/v1",
"kind": "CustomType",
"spec": {
"replicas": 1
},
"status": {
"available": 1
}
},
"networkData": {
"interfaceName": "interfaceNameValue",
"ips": [
"ipsValue"
],
"hardwareAddress": "hardwareAddressValue"
}
}
]
}
}

View File

@@ -93,6 +93,29 @@ status:
operator: operatorValue
values:
- valuesValue
devices:
- conditions:
- lastTransitionTime: "2004-01-01T01:01:01Z"
message: messageValue
observedGeneration: 3
reason: reasonValue
status: statusValue
type: typeValue
data:
apiVersion: example.com/v1
kind: CustomType
spec:
replicas: 1
status:
available: 1
device: deviceValue
driver: driverValue
networkData:
hardwareAddress: hardwareAddressValue
interfaceName: interfaceNameValue
ips:
- ipsValue
pool: poolValue
reservedFor:
- apiGroup: apiGroupValue
name: nameValue

View File

@@ -157,6 +157,40 @@
"name": "nameValue",
"uid": "uidValue"
}
],
"devices": [
{
"driver": "driverValue",
"pool": "poolValue",
"device": "deviceValue",
"conditions": [
{
"type": "typeValue",
"status": "statusValue",
"observedGeneration": 3,
"lastTransitionTime": "2004-01-01T01:01:01Z",
"reason": "reasonValue",
"message": "messageValue"
}
],
"data": {
"apiVersion": "example.com/v1",
"kind": "CustomType",
"spec": {
"replicas": 1
},
"status": {
"available": 1
}
},
"networkData": {
"interfaceName": "interfaceNameValue",
"ips": [
"ipsValue"
],
"hardwareAddress": "hardwareAddressValue"
}
}
]
}
}

View File

@@ -93,6 +93,29 @@ status:
operator: operatorValue
values:
- valuesValue
devices:
- conditions:
- lastTransitionTime: "2004-01-01T01:01:01Z"
message: messageValue
observedGeneration: 3
reason: reasonValue
status: statusValue
type: typeValue
data:
apiVersion: example.com/v1
kind: CustomType
spec:
replicas: 1
status:
available: 1
device: deviceValue
driver: driverValue
networkData:
hardwareAddress: hardwareAddressValue
interfaceName: interfaceNameValue
ips:
- ipsValue
pool: poolValue
reservedFor:
- apiGroup: apiGroupValue
name: nameValue

View File

@@ -12353,6 +12353,35 @@ var schemaYAML = typed.YAMLObject(`types:
- name: namespace
type:
scalar: string
- name: io.k8s.api.resource.v1alpha3.AllocatedDeviceStatus
map:
fields:
- name: conditions
type:
list:
elementType:
namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Condition
elementRelationship: associative
keys:
- type
- name: data
type:
namedType: __untyped_atomic_
- name: device
type:
scalar: string
default: ""
- name: driver
type:
scalar: string
default: ""
- name: networkData
type:
namedType: io.k8s.api.resource.v1alpha3.NetworkDeviceData
- name: pool
type:
scalar: string
default: ""
- name: io.k8s.api.resource.v1alpha3.AllocationResult
map:
fields:
@@ -12576,6 +12605,21 @@ var schemaYAML = typed.YAMLObject(`types:
- name: cel
type:
namedType: io.k8s.api.resource.v1alpha3.CELDeviceSelector
- name: io.k8s.api.resource.v1alpha3.NetworkDeviceData
map:
fields:
- name: hardwareAddress
type:
scalar: string
- name: interfaceName
type:
scalar: string
- name: ips
type:
list:
elementType:
scalar: string
elementRelationship: atomic
- name: io.k8s.api.resource.v1alpha3.OpaqueDeviceConfiguration
map:
fields:
@@ -12638,6 +12682,16 @@ var schemaYAML = typed.YAMLObject(`types:
- name: allocation
type:
namedType: io.k8s.api.resource.v1alpha3.AllocationResult
- name: devices
type:
list:
elementType:
namedType: io.k8s.api.resource.v1alpha3.AllocatedDeviceStatus
elementRelationship: associative
keys:
- driver
- device
- pool
- name: reservedFor
type:
list:
@@ -12732,6 +12786,35 @@ var schemaYAML = typed.YAMLObject(`types:
type:
namedType: io.k8s.api.resource.v1alpha3.ResourcePool
default: {}
- name: io.k8s.api.resource.v1beta1.AllocatedDeviceStatus
map:
fields:
- name: conditions
type:
list:
elementType:
namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Condition
elementRelationship: associative
keys:
- type
- name: data
type:
namedType: __untyped_atomic_
- name: device
type:
scalar: string
default: ""
- name: driver
type:
scalar: string
default: ""
- name: networkData
type:
namedType: io.k8s.api.resource.v1beta1.NetworkDeviceData
- name: pool
type:
scalar: string
default: ""
- name: io.k8s.api.resource.v1beta1.AllocationResult
map:
fields:
@@ -12961,6 +13044,21 @@ var schemaYAML = typed.YAMLObject(`types:
- name: cel
type:
namedType: io.k8s.api.resource.v1beta1.CELDeviceSelector
- name: io.k8s.api.resource.v1beta1.NetworkDeviceData
map:
fields:
- name: hardwareAddress
type:
scalar: string
- name: interfaceName
type:
scalar: string
- name: ips
type:
list:
elementType:
scalar: string
elementRelationship: atomic
- name: io.k8s.api.resource.v1beta1.OpaqueDeviceConfiguration
map:
fields:
@@ -13023,6 +13121,16 @@ var schemaYAML = typed.YAMLObject(`types:
- name: allocation
type:
namedType: io.k8s.api.resource.v1beta1.AllocationResult
- name: devices
type:
list:
elementType:
namedType: io.k8s.api.resource.v1beta1.AllocatedDeviceStatus
elementRelationship: associative
keys:
- driver
- device
- pool
- name: reservedFor
type:
list:

View File

@@ -0,0 +1,94 @@
/*
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 v1alpha3
import (
runtime "k8s.io/apimachinery/pkg/runtime"
v1 "k8s.io/client-go/applyconfigurations/meta/v1"
)
// AllocatedDeviceStatusApplyConfiguration represents a declarative configuration of the AllocatedDeviceStatus type for use
// with apply.
type AllocatedDeviceStatusApplyConfiguration struct {
Driver *string `json:"driver,omitempty"`
Pool *string `json:"pool,omitempty"`
Device *string `json:"device,omitempty"`
Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"`
Data *runtime.RawExtension `json:"data,omitempty"`
NetworkData *NetworkDeviceDataApplyConfiguration `json:"networkData,omitempty"`
}
// AllocatedDeviceStatusApplyConfiguration constructs a declarative configuration of the AllocatedDeviceStatus type for use with
// apply.
func AllocatedDeviceStatus() *AllocatedDeviceStatusApplyConfiguration {
return &AllocatedDeviceStatusApplyConfiguration{}
}
// WithDriver sets the Driver 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 Driver field is set to the value of the last call.
func (b *AllocatedDeviceStatusApplyConfiguration) WithDriver(value string) *AllocatedDeviceStatusApplyConfiguration {
b.Driver = &value
return b
}
// WithPool sets the Pool 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 Pool field is set to the value of the last call.
func (b *AllocatedDeviceStatusApplyConfiguration) WithPool(value string) *AllocatedDeviceStatusApplyConfiguration {
b.Pool = &value
return b
}
// WithDevice sets the Device 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 Device field is set to the value of the last call.
func (b *AllocatedDeviceStatusApplyConfiguration) WithDevice(value string) *AllocatedDeviceStatusApplyConfiguration {
b.Device = &value
return b
}
// WithConditions adds the given value to the Conditions field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the Conditions field.
func (b *AllocatedDeviceStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *AllocatedDeviceStatusApplyConfiguration {
for i := range values {
if values[i] == nil {
panic("nil value passed to WithConditions")
}
b.Conditions = append(b.Conditions, *values[i])
}
return b
}
// WithData sets the Data 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 Data field is set to the value of the last call.
func (b *AllocatedDeviceStatusApplyConfiguration) WithData(value runtime.RawExtension) *AllocatedDeviceStatusApplyConfiguration {
b.Data = &value
return b
}
// WithNetworkData sets the NetworkData 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 NetworkData field is set to the value of the last call.
func (b *AllocatedDeviceStatusApplyConfiguration) WithNetworkData(value *NetworkDeviceDataApplyConfiguration) *AllocatedDeviceStatusApplyConfiguration {
b.NetworkData = value
return b
}

View File

@@ -0,0 +1,59 @@
/*
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 v1alpha3
// NetworkDeviceDataApplyConfiguration represents a declarative configuration of the NetworkDeviceData type for use
// with apply.
type NetworkDeviceDataApplyConfiguration struct {
InterfaceName *string `json:"interfaceName,omitempty"`
IPs []string `json:"ips,omitempty"`
HardwareAddress *string `json:"hardwareAddress,omitempty"`
}
// NetworkDeviceDataApplyConfiguration constructs a declarative configuration of the NetworkDeviceData type for use with
// apply.
func NetworkDeviceData() *NetworkDeviceDataApplyConfiguration {
return &NetworkDeviceDataApplyConfiguration{}
}
// WithInterfaceName sets the InterfaceName 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 InterfaceName field is set to the value of the last call.
func (b *NetworkDeviceDataApplyConfiguration) WithInterfaceName(value string) *NetworkDeviceDataApplyConfiguration {
b.InterfaceName = &value
return b
}
// WithIPs adds the given value to the IPs field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the IPs field.
func (b *NetworkDeviceDataApplyConfiguration) WithIPs(values ...string) *NetworkDeviceDataApplyConfiguration {
for i := range values {
b.IPs = append(b.IPs, values[i])
}
return b
}
// WithHardwareAddress sets the HardwareAddress 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 HardwareAddress field is set to the value of the last call.
func (b *NetworkDeviceDataApplyConfiguration) WithHardwareAddress(value string) *NetworkDeviceDataApplyConfiguration {
b.HardwareAddress = &value
return b
}

View File

@@ -23,6 +23,7 @@ package v1alpha3
type ResourceClaimStatusApplyConfiguration struct {
Allocation *AllocationResultApplyConfiguration `json:"allocation,omitempty"`
ReservedFor []ResourceClaimConsumerReferenceApplyConfiguration `json:"reservedFor,omitempty"`
Devices []AllocatedDeviceStatusApplyConfiguration `json:"devices,omitempty"`
}
// ResourceClaimStatusApplyConfiguration constructs a declarative configuration of the ResourceClaimStatus type for use with
@@ -51,3 +52,16 @@ func (b *ResourceClaimStatusApplyConfiguration) WithReservedFor(values ...*Resou
}
return b
}
// WithDevices adds the given value to the Devices field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the Devices field.
func (b *ResourceClaimStatusApplyConfiguration) WithDevices(values ...*AllocatedDeviceStatusApplyConfiguration) *ResourceClaimStatusApplyConfiguration {
for i := range values {
if values[i] == nil {
panic("nil value passed to WithDevices")
}
b.Devices = append(b.Devices, *values[i])
}
return b
}

View File

@@ -0,0 +1,94 @@
/*
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 v1beta1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
v1 "k8s.io/client-go/applyconfigurations/meta/v1"
)
// AllocatedDeviceStatusApplyConfiguration represents a declarative configuration of the AllocatedDeviceStatus type for use
// with apply.
type AllocatedDeviceStatusApplyConfiguration struct {
Driver *string `json:"driver,omitempty"`
Pool *string `json:"pool,omitempty"`
Device *string `json:"device,omitempty"`
Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"`
Data *runtime.RawExtension `json:"data,omitempty"`
NetworkData *NetworkDeviceDataApplyConfiguration `json:"networkData,omitempty"`
}
// AllocatedDeviceStatusApplyConfiguration constructs a declarative configuration of the AllocatedDeviceStatus type for use with
// apply.
func AllocatedDeviceStatus() *AllocatedDeviceStatusApplyConfiguration {
return &AllocatedDeviceStatusApplyConfiguration{}
}
// WithDriver sets the Driver 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 Driver field is set to the value of the last call.
func (b *AllocatedDeviceStatusApplyConfiguration) WithDriver(value string) *AllocatedDeviceStatusApplyConfiguration {
b.Driver = &value
return b
}
// WithPool sets the Pool 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 Pool field is set to the value of the last call.
func (b *AllocatedDeviceStatusApplyConfiguration) WithPool(value string) *AllocatedDeviceStatusApplyConfiguration {
b.Pool = &value
return b
}
// WithDevice sets the Device 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 Device field is set to the value of the last call.
func (b *AllocatedDeviceStatusApplyConfiguration) WithDevice(value string) *AllocatedDeviceStatusApplyConfiguration {
b.Device = &value
return b
}
// WithConditions adds the given value to the Conditions field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the Conditions field.
func (b *AllocatedDeviceStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *AllocatedDeviceStatusApplyConfiguration {
for i := range values {
if values[i] == nil {
panic("nil value passed to WithConditions")
}
b.Conditions = append(b.Conditions, *values[i])
}
return b
}
// WithData sets the Data 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 Data field is set to the value of the last call.
func (b *AllocatedDeviceStatusApplyConfiguration) WithData(value runtime.RawExtension) *AllocatedDeviceStatusApplyConfiguration {
b.Data = &value
return b
}
// WithNetworkData sets the NetworkData 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 NetworkData field is set to the value of the last call.
func (b *AllocatedDeviceStatusApplyConfiguration) WithNetworkData(value *NetworkDeviceDataApplyConfiguration) *AllocatedDeviceStatusApplyConfiguration {
b.NetworkData = value
return b
}

View File

@@ -0,0 +1,59 @@
/*
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 v1beta1
// NetworkDeviceDataApplyConfiguration represents a declarative configuration of the NetworkDeviceData type for use
// with apply.
type NetworkDeviceDataApplyConfiguration struct {
InterfaceName *string `json:"interfaceName,omitempty"`
IPs []string `json:"ips,omitempty"`
HardwareAddress *string `json:"hardwareAddress,omitempty"`
}
// NetworkDeviceDataApplyConfiguration constructs a declarative configuration of the NetworkDeviceData type for use with
// apply.
func NetworkDeviceData() *NetworkDeviceDataApplyConfiguration {
return &NetworkDeviceDataApplyConfiguration{}
}
// WithInterfaceName sets the InterfaceName 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 InterfaceName field is set to the value of the last call.
func (b *NetworkDeviceDataApplyConfiguration) WithInterfaceName(value string) *NetworkDeviceDataApplyConfiguration {
b.InterfaceName = &value
return b
}
// WithIPs adds the given value to the IPs field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the IPs field.
func (b *NetworkDeviceDataApplyConfiguration) WithIPs(values ...string) *NetworkDeviceDataApplyConfiguration {
for i := range values {
b.IPs = append(b.IPs, values[i])
}
return b
}
// WithHardwareAddress sets the HardwareAddress 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 HardwareAddress field is set to the value of the last call.
func (b *NetworkDeviceDataApplyConfiguration) WithHardwareAddress(value string) *NetworkDeviceDataApplyConfiguration {
b.HardwareAddress = &value
return b
}

View File

@@ -23,6 +23,7 @@ package v1beta1
type ResourceClaimStatusApplyConfiguration struct {
Allocation *AllocationResultApplyConfiguration `json:"allocation,omitempty"`
ReservedFor []ResourceClaimConsumerReferenceApplyConfiguration `json:"reservedFor,omitempty"`
Devices []AllocatedDeviceStatusApplyConfiguration `json:"devices,omitempty"`
}
// ResourceClaimStatusApplyConfiguration constructs a declarative configuration of the ResourceClaimStatus type for use with
@@ -51,3 +52,16 @@ func (b *ResourceClaimStatusApplyConfiguration) WithReservedFor(values ...*Resou
}
return b
}
// WithDevices adds the given value to the Devices field in the declarative configuration
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
// If called multiple times, values provided by each call will be appended to the Devices field.
func (b *ResourceClaimStatusApplyConfiguration) WithDevices(values ...*AllocatedDeviceStatusApplyConfiguration) *ResourceClaimStatusApplyConfiguration {
for i := range values {
if values[i] == nil {
panic("nil value passed to WithDevices")
}
b.Devices = append(b.Devices, *values[i])
}
return b
}

View File

@@ -1582,6 +1582,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &applyconfigurationsrbacv1beta1.SubjectApplyConfiguration{}
// Group=resource.k8s.io, Version=v1alpha3
case v1alpha3.SchemeGroupVersion.WithKind("AllocatedDeviceStatus"):
return &resourcev1alpha3.AllocatedDeviceStatusApplyConfiguration{}
case v1alpha3.SchemeGroupVersion.WithKind("AllocationResult"):
return &resourcev1alpha3.AllocationResultApplyConfiguration{}
case v1alpha3.SchemeGroupVersion.WithKind("BasicDevice"):
@@ -1616,6 +1618,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &resourcev1alpha3.DeviceRequestAllocationResultApplyConfiguration{}
case v1alpha3.SchemeGroupVersion.WithKind("DeviceSelector"):
return &resourcev1alpha3.DeviceSelectorApplyConfiguration{}
case v1alpha3.SchemeGroupVersion.WithKind("NetworkDeviceData"):
return &resourcev1alpha3.NetworkDeviceDataApplyConfiguration{}
case v1alpha3.SchemeGroupVersion.WithKind("OpaqueDeviceConfiguration"):
return &resourcev1alpha3.OpaqueDeviceConfigurationApplyConfiguration{}
case v1alpha3.SchemeGroupVersion.WithKind("ResourceClaim"):
@@ -1638,6 +1642,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &resourcev1alpha3.ResourceSliceSpecApplyConfiguration{}
// Group=resource.k8s.io, Version=v1beta1
case resourcev1beta1.SchemeGroupVersion.WithKind("AllocatedDeviceStatus"):
return &applyconfigurationsresourcev1beta1.AllocatedDeviceStatusApplyConfiguration{}
case resourcev1beta1.SchemeGroupVersion.WithKind("AllocationResult"):
return &applyconfigurationsresourcev1beta1.AllocationResultApplyConfiguration{}
case resourcev1beta1.SchemeGroupVersion.WithKind("BasicDevice"):
@@ -1674,6 +1680,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &applyconfigurationsresourcev1beta1.DeviceRequestAllocationResultApplyConfiguration{}
case resourcev1beta1.SchemeGroupVersion.WithKind("DeviceSelector"):
return &applyconfigurationsresourcev1beta1.DeviceSelectorApplyConfiguration{}
case resourcev1beta1.SchemeGroupVersion.WithKind("NetworkDeviceData"):
return &applyconfigurationsresourcev1beta1.NetworkDeviceDataApplyConfiguration{}
case resourcev1beta1.SchemeGroupVersion.WithKind("OpaqueDeviceConfiguration"):
return &applyconfigurationsresourcev1beta1.OpaqueDeviceConfigurationApplyConfiguration{}
case resourcev1beta1.SchemeGroupVersion.WithKind("ResourceClaim"):

View File

@@ -403,6 +403,77 @@ var _ = framework.SIGDescribe("node")("DRA", feature.DynamicResourceAllocation,
return b.f.ClientSet.ResourceV1beta1().ResourceClaims(b.f.Namespace.Name).Get(ctx, claim.Name, metav1.GetOptions{})
}).WithTimeout(f.Timeouts.PodDelete).Should(gomega.HaveField("Status.Allocation", (*resourceapi.AllocationResult)(nil)))
})
f.It("must be possible for the driver to update the ResourceClaim.Status.Devices once allocated", feature.DRAResourceClaimDeviceStatus, func(ctx context.Context) {
pod := b.podExternal()
claim := b.externalClaim()
b.create(ctx, claim, pod)
// Waits for the ResourceClaim to be allocated and the pod to be scheduled.
var allocatedResourceClaim *resourceapi.ResourceClaim
var scheduledPod *v1.Pod
gomega.Eventually(ctx, func(ctx context.Context) (*resourceapi.ResourceClaim, error) {
var err error
allocatedResourceClaim, err = b.f.ClientSet.ResourceV1beta1().ResourceClaims(b.f.Namespace.Name).Get(ctx, claim.Name, metav1.GetOptions{})
return allocatedResourceClaim, err
}).WithTimeout(f.Timeouts.PodDelete).ShouldNot(gomega.HaveField("Status.Allocation", (*resourceapi.AllocationResult)(nil)))
gomega.Eventually(ctx, func(ctx context.Context) error {
var err error
scheduledPod, err = b.f.ClientSet.CoreV1().Pods(pod.Namespace).Get(ctx, pod.Name, metav1.GetOptions{})
if err != nil && scheduledPod.Spec.NodeName != "" {
return fmt.Errorf("expected the test pod %s to exist and to be scheduled on a node: %w", pod.Name, err)
}
return nil
}).WithTimeout(f.Timeouts.PodDelete).Should(gomega.BeNil())
gomega.Expect(allocatedResourceClaim.Status.Allocation).ToNot(gomega.BeNil())
gomega.Expect(allocatedResourceClaim.Status.Allocation.Devices.Results).To(gomega.HaveLen(1))
ginkgo.By("Setting the device status a first time")
allocatedResourceClaim.Status.Devices = append(allocatedResourceClaim.Status.Devices,
resourceapi.AllocatedDeviceStatus{
Driver: allocatedResourceClaim.Status.Allocation.Devices.Results[0].Driver,
Pool: allocatedResourceClaim.Status.Allocation.Devices.Results[0].Pool,
Device: allocatedResourceClaim.Status.Allocation.Devices.Results[0].Device,
Conditions: []metav1.Condition{{Type: "a", Status: "True", Message: "c", Reason: "d", LastTransitionTime: metav1.NewTime(time.Now().Truncate(time.Second))}},
Data: runtime.RawExtension{Raw: []byte(`{"foo":"bar"}`)},
NetworkData: &resourceapi.NetworkDeviceData{
InterfaceName: "inf1",
IPs: []string{"10.9.8.0/24", "2001:db8::/64"},
HardwareAddress: "bc:1c:b6:3e:b8:25",
},
})
// Updates the ResourceClaim from the driver on the same node as the pod.
updatedResourceClaim, err := driver.Nodes[scheduledPod.Spec.NodeName].ExamplePlugin.UpdateStatus(ctx, allocatedResourceClaim)
framework.ExpectNoError(err)
gomega.Expect(updatedResourceClaim).ToNot(gomega.BeNil())
gomega.Expect(updatedResourceClaim.Status.Devices).To(gomega.Equal(allocatedResourceClaim.Status.Devices))
ginkgo.By("Updating the device status")
updatedResourceClaim.Status.Devices[0] = resourceapi.AllocatedDeviceStatus{
Driver: allocatedResourceClaim.Status.Allocation.Devices.Results[0].Driver,
Pool: allocatedResourceClaim.Status.Allocation.Devices.Results[0].Pool,
Device: allocatedResourceClaim.Status.Allocation.Devices.Results[0].Device,
Conditions: []metav1.Condition{{Type: "e", Status: "True", Message: "g", Reason: "h", LastTransitionTime: metav1.NewTime(time.Now().Truncate(time.Second))}},
Data: runtime.RawExtension{Raw: []byte(`{"bar":"foo"}`)},
NetworkData: &resourceapi.NetworkDeviceData{
InterfaceName: "inf2",
IPs: []string{"10.9.8.1/24", "2001:db8::1/64"},
HardwareAddress: "bc:1c:b6:3e:b8:26",
},
}
updatedResourceClaim2, err := driver.Nodes[scheduledPod.Spec.NodeName].ExamplePlugin.UpdateStatus(ctx, updatedResourceClaim)
framework.ExpectNoError(err)
gomega.Expect(updatedResourceClaim2).ToNot(gomega.BeNil())
gomega.Expect(updatedResourceClaim2.Status.Devices).To(gomega.Equal(updatedResourceClaim.Status.Devices))
getResourceClaim, err := b.f.ClientSet.ResourceV1beta1().ResourceClaims(b.f.Namespace.Name).Get(ctx, claim.Name, metav1.GetOptions{})
framework.ExpectNoError(err)
gomega.Expect(getResourceClaim).ToNot(gomega.BeNil())
gomega.Expect(getResourceClaim.Status.Devices).To(gomega.Equal(updatedResourceClaim.Status.Devices))
})
}
singleNodeTests := func() {

View File

@@ -569,3 +569,7 @@ func (ex *ExamplePlugin) CountCalls(methodSuffix string) int {
}
return count
}
func (ex *ExamplePlugin) UpdateStatus(ctx context.Context, resourceClaim *resourceapi.ResourceClaim) (*resourceapi.ResourceClaim, error) {
return ex.kubeClient.ResourceV1beta1().ResourceClaims(resourceClaim.Namespace).UpdateStatus(ctx, resourceClaim, metav1.UpdateOptions{})
}

View File

@@ -15,6 +15,9 @@ rules:
- apiGroups: ["resource.k8s.io"]
resources: ["resourceclaims"]
verbs: ["get"]
- apiGroups: ["resource.k8s.io"]
resources: ["resourceclaims/status"]
verbs: ["update"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get"]

View File

@@ -95,6 +95,15 @@ var (
// TODO: document the feature (owning SIG, when to use this feature for a test)
Downgrade = framework.WithFeature(framework.ValidFeatures.Add("Downgrade"))
// owning-sig: sig-node
// kep: https://kep.k8s.io/4817
// test-infra jobs:
// - "dra-alpha" in https://testgrid.k8s.io/sig-node-dynamic-resource-allocation
//
// This label is used for tests which need:
// - the DynamicResourceAllocation *and* DRAResourceClaimDeviceStatus feature gates
DRAResourceClaimDeviceStatus = framework.WithFeature(framework.ValidFeatures.Add("DRAResourceClaimDeviceStatus"))
// owning-sig: sig-node
// kep: https://kep.k8s.io/4381
// test-infra jobs:

View File

@@ -418,6 +418,12 @@
lockToDefault: false
preRelease: Alpha
version: "1.32"
- name: DRAResourceClaimDeviceStatus
versionedSpecs:
- default: false
lockToDefault: false
preRelease: Alpha
version: "1.32"
- name: DynamicResourceAllocation
versionedSpecs:
- default: false

View File

@@ -0,0 +1,221 @@
/*
Copyright 2024 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.
*/
package resourceclaim
import (
"context"
"fmt"
"testing"
"k8s.io/api/resource/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
clientset "k8s.io/client-go/kubernetes"
kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/test/integration/framework"
)
// TestEnableDisableDRAResourceClaimDeviceStatus first test the feature gate disabled
// by creating a ResourceClaim with an invalid device (not allocated device) and checks
// the object is not validated.
// Then the feature gate is created, and an attempt to create similar invalid ResourceClaim
// is done with no success.
func TestEnableDisableDRAResourceClaimDeviceStatus(t *testing.T) {
// start etcd instance
etcdOptions := framework.SharedEtcd()
apiServerOptions := kubeapiservertesting.NewDefaultTestServerOptions()
// apiserver with the feature disabled
server1 := kubeapiservertesting.StartTestServerOrDie(t, apiServerOptions,
[]string{
fmt.Sprintf("--feature-gates=%s=true,%s=false", features.DynamicResourceAllocation, features.DRAResourceClaimDeviceStatus),
},
etcdOptions)
client1, err := clientset.NewForConfig(server1.ClientConfig)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
ns := framework.CreateNamespaceOrDie(client1, "test-enable-dra-resourceclaim-device-status", t)
rcDisabledName := "test-enable-dra-resourceclaim-device-status-rc-disabled"
rcDisabled := &v1beta1.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{
Name: rcDisabledName,
},
Spec: v1beta1.ResourceClaimSpec{
Devices: v1beta1.DeviceClaim{
Requests: []v1beta1.DeviceRequest{
{
Name: "foo",
DeviceClassName: "foo",
Count: 1,
AllocationMode: v1beta1.DeviceAllocationModeExactCount,
},
},
},
},
}
if _, err := client1.ResourceV1beta1().ResourceClaims(ns.Name).Create(context.TODO(), rcDisabled, metav1.CreateOptions{}); err != nil {
t.Fatal(err)
}
rcDisabled.Status = v1beta1.ResourceClaimStatus{
Devices: []v1beta1.AllocatedDeviceStatus{
{
Driver: "foo",
Pool: "foo",
Device: "foo",
Data: runtime.RawExtension{
Raw: []byte(`{"kind": "foo", "apiVersion": "dra.example.com/v1"}`),
},
NetworkData: &v1beta1.NetworkDeviceData{
InterfaceName: "net-1",
IPs: []string{
"10.9.8.0/24",
"2001:db8::/64",
},
HardwareAddress: "ea:9f:cb:40:b1:7b",
},
},
},
}
if _, err := client1.ResourceV1beta1().ResourceClaims(ns.Name).UpdateStatus(context.TODO(), rcDisabled, metav1.UpdateOptions{}); err != nil {
t.Fatal(err)
}
rcDisabled, err = client1.ResourceV1beta1().ResourceClaims(ns.Name).Get(context.TODO(), rcDisabledName, metav1.GetOptions{})
if err != nil {
t.Fatal(err)
}
// No devices as the Kubernetes api-server dropped these fields since the feature is disabled.
if len(rcDisabled.Status.Devices) != 0 {
t.Fatalf("expected 0 Device in status got %d", len(rcDisabled.Status.Devices))
}
// shutdown apiserver with the feature disabled
server1.TearDownFn()
// apiserver with the feature enabled
server2 := kubeapiservertesting.StartTestServerOrDie(t, apiServerOptions,
[]string{
fmt.Sprintf("--feature-gates=%s=true,%s=true", features.DynamicResourceAllocation, features.DRAResourceClaimDeviceStatus),
},
etcdOptions)
client2, err := clientset.NewForConfig(server2.ClientConfig)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
rcEnabledName := "test-enable-dra-resourceclaim-device-status-rc-enabled"
rcEnabled := &v1beta1.ResourceClaim{
ObjectMeta: metav1.ObjectMeta{
Name: rcEnabledName,
},
Spec: v1beta1.ResourceClaimSpec{
Devices: v1beta1.DeviceClaim{
Requests: []v1beta1.DeviceRequest{
{
Name: "bar",
DeviceClassName: "bar",
Count: 1,
AllocationMode: v1beta1.DeviceAllocationModeExactCount,
},
},
},
},
}
if _, err := client2.ResourceV1beta1().ResourceClaims(ns.Name).Create(context.TODO(), rcEnabled, metav1.CreateOptions{}); err != nil {
t.Fatal(err)
}
// Tests the validation is enabled.
// validation will refuse this update as the device is not allocated.
rcEnabled.Status = v1beta1.ResourceClaimStatus{
Devices: []v1beta1.AllocatedDeviceStatus{
{
Driver: "bar",
Pool: "bar",
Device: "bar",
Data: runtime.RawExtension{
Raw: []byte(`{"kind": "foo", "apiVersion": "dra.example.com/v1"}`),
},
NetworkData: &v1beta1.NetworkDeviceData{
InterfaceName: "net-1",
IPs: []string{
"10.9.8.0/24",
"2001:db8::/64",
},
HardwareAddress: "ea:9f:cb:40:b1:7b",
},
},
},
}
if _, err := client2.ResourceV1beta1().ResourceClaims(ns.Name).UpdateStatus(context.TODO(), rcEnabled, metav1.UpdateOptions{}); err == nil {
t.Fatalf("Expected error (must be an allocated device in the claim)")
}
rcEnabled.Status = v1beta1.ResourceClaimStatus{
Allocation: &v1beta1.AllocationResult{
Devices: v1beta1.DeviceAllocationResult{
Results: []v1beta1.DeviceRequestAllocationResult{
{
Request: "bar",
Driver: "bar",
Pool: "bar",
Device: "bar",
},
},
},
},
Devices: []v1beta1.AllocatedDeviceStatus{
{
Driver: "bar",
Pool: "bar",
Device: "bar",
Data: runtime.RawExtension{
Raw: []byte(`{"kind": "foo", "apiVersion": "dra.example.com/v1"}`),
},
NetworkData: &v1beta1.NetworkDeviceData{
InterfaceName: "net-1",
IPs: []string{
"10.9.8.0/24",
"2001:db8::/64",
},
HardwareAddress: "ea:9f:cb:40:b1:7b",
},
},
},
}
if _, err := client2.ResourceV1beta1().ResourceClaims(ns.Name).UpdateStatus(context.TODO(), rcEnabled, metav1.UpdateOptions{}); err != nil {
t.Fatal(err)
}
// Tests the field is enabled.
rcEnabled, err = client2.ResourceV1beta1().ResourceClaims(ns.Name).Get(context.TODO(), rcEnabledName, metav1.GetOptions{})
if err != nil {
t.Fatal(err)
}
if len(rcEnabled.Status.Devices) != 1 {
t.Fatalf("expected 1 Device in status got %d", len(rcEnabled.Status.Devices))
}
// shutdown apiserver with the feature enabled
server2.TearDownFn()
}

View File

@@ -0,0 +1,27 @@
/*
Copyright 2023 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.
*/
package resourceclaim
import (
"testing"
"k8s.io/kubernetes/test/integration/framework"
)
func TestMain(m *testing.M) {
framework.EtcdMain(m.Run)
}