Merge pull request #134152 from pohly/dra-device-taints-1.35

DRA: device taints: new ResourceSlice API, new features
This commit is contained in:
Kubernetes Prow Robot 2025-11-04 15:32:07 -08:00 committed by GitHub
commit c1a6a3ca71
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
106 changed files with 5879 additions and 1693 deletions

View file

@ -172,6 +172,7 @@ API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,C
API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,CronJobControllerConfiguration,ConcurrentCronJobSyncs
API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,DaemonSetControllerConfiguration,ConcurrentDaemonSetSyncs
API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,DeploymentControllerConfiguration,ConcurrentDeploymentSyncs
API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,DeviceTaintEvictionControllerConfiguration,ConcurrentSyncs
API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,EndpointControllerConfiguration,ConcurrentEndpointSyncs
API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,EndpointControllerConfiguration,EndpointUpdatesBatchPeriod
API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,EndpointSliceControllerConfiguration,ConcurrentServiceEndpointSyncs
@ -199,6 +200,7 @@ API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,K
API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,KubeControllerManagerConfiguration,DaemonSetController
API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,KubeControllerManagerConfiguration,DeploymentController
API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,KubeControllerManagerConfiguration,DeprecatedController
API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,KubeControllerManagerConfiguration,DeviceTaintEvictionController
API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,KubeControllerManagerConfiguration,EndpointController
API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,KubeControllerManagerConfiguration,EndpointSliceController
API rule violation: names_match,k8s.io/kube-controller-manager/config/v1alpha1,KubeControllerManagerConfiguration,EndpointSliceMirroringController

View file

@ -2101,6 +2101,21 @@
},
"scope": "Cluster",
"singularResource": "devicetaintrule",
"subresources": [
{
"responseKind": {
"group": "",
"kind": "DeviceTaintRule",
"version": ""
},
"subresource": "status",
"verbs": [
"get",
"patch",
"update"
]
}
],
"verbs": [
"create",
"delete",

View file

@ -19,6 +19,17 @@
"update",
"watch"
]
},
{
"kind": "DeviceTaintRule",
"name": "devicetaintrules/status",
"namespaced": false,
"singularName": "",
"verbs": [
"get",
"patch",
"update"
]
}
]
}

View file

@ -15452,7 +15452,7 @@
"description": "NodeSelector defines the nodes where the device is available.\n\nMust use exactly one term.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set."
},
"taints": {
"description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"items": {
"$ref": "#/definitions/io.k8s.api.resource.v1.DeviceTaint"
},
@ -15899,7 +15899,7 @@
"description": "The device this taint is attached to has the \"effect\" on any claim which does not tolerate the taint and, through the claim, to pods using the claim.",
"properties": {
"effect": {
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.",
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.",
"type": "string"
},
"key": {
@ -16363,7 +16363,7 @@
"type": "boolean"
},
"devices": {
"description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.",
"description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints the limit is 64.",
"items": {
"$ref": "#/definitions/io.k8s.api.resource.v1.Device"
},
@ -16405,34 +16405,11 @@
],
"type": "object"
},
"io.k8s.api.resource.v1alpha3.CELDeviceSelector": {
"description": "CELDeviceSelector contains a CEL expression for selecting a device.",
"properties": {
"expression": {
"description": "Expression is a CEL expression which evaluates a single device. It must evaluate to true when the device under consideration satisfies the desired criteria, and false when it does not. Any other result is an error and causes allocation of devices to abort.\n\nThe expression's input is an object named \"device\", which carries the following properties:\n - driver (string): the name of the driver which defines this device.\n - attributes (map[string]object): the device's attributes, grouped by prefix\n (e.g. device.attributes[\"dra.example.com\"] evaluates to an object with all\n of the attributes which were prefixed by \"dra.example.com\".\n - capacity (map[string]object): the device's capacities, grouped by prefix.\n\nExample: Consider a device with driver=\"dra.example.com\", which exposes two attributes named \"model\" and \"ext.example.com/family\" and which exposes one capacity named \"modules\". This input to this expression would have the following fields:\n\n device.driver\n device.attributes[\"dra.example.com\"].model\n device.attributes[\"ext.example.com\"].family\n device.capacity[\"dra.example.com\"].modules\n\nThe device.driver field can be used to check for a specific driver, either as a high-level precondition (i.e. you only want to consider devices from this driver) or as part of a multi-clause expression that is meant to consider devices from different drivers.\n\nThe value type of each attribute is defined by the device definition, and users who write these expressions must consult the documentation for their specific drivers. The value type of each capacity is Quantity.\n\nIf an unknown prefix is used as a lookup in either device.attributes or device.capacity, an empty map will be returned. Any reference to an unknown field will cause an evaluation error and allocation to abort.\n\nA robust expression should check for the existence of attributes before referencing them.\n\nFor ease of use, the cel.bind() function is enabled, and can be used to simplify expressions that access multiple attributes with the same domain. For example:\n\n cel.bind(dra, device.attributes[\"dra.example.com\"], dra.someBool && dra.anotherBool)\n\nThe length of the expression must be smaller or equal to 10 Ki. The cost of evaluating it is also limited based on the estimated number of logical steps.",
"type": "string"
}
},
"required": [
"expression"
],
"type": "object"
},
"io.k8s.api.resource.v1alpha3.DeviceSelector": {
"description": "DeviceSelector must have exactly one field set.",
"properties": {
"cel": {
"$ref": "#/definitions/io.k8s.api.resource.v1alpha3.CELDeviceSelector",
"description": "CEL contains a CEL expression for selecting a device."
}
},
"type": "object"
},
"io.k8s.api.resource.v1alpha3.DeviceTaint": {
"description": "The device this taint is attached to has the \"effect\" on any claim which does not tolerate the taint and, through the claim, to pods using the claim.",
"properties": {
"effect": {
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.",
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.",
"type": "string"
},
"key": {
@ -16472,6 +16449,10 @@
"spec": {
"$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRuleSpec",
"description": "Spec specifies the selector and one taint.\n\nChanging the spec automatically increments the metadata.generation number."
},
"status": {
"$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRuleStatus",
"description": "Status provides information about what was requested in the spec."
}
},
"required": [
@ -16526,7 +16507,7 @@
"properties": {
"deviceSelector": {
"$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintSelector",
"description": "DeviceSelector defines which device(s) the taint is applied to. All selector criteria must be satified for a device to match. The empty selector matches all devices. Without a selector, no devices are matches."
"description": "DeviceSelector defines which device(s) the taint is applied to. All selector criteria must be satisfied for a device to match. The empty selector matches all devices. Without a selector, no devices are matches."
},
"taint": {
"$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaint",
@ -16538,6 +16519,25 @@
],
"type": "object"
},
"io.k8s.api.resource.v1alpha3.DeviceTaintRuleStatus": {
"description": "DeviceTaintRuleStatus provides information about an on-going pod eviction.",
"properties": {
"conditions": {
"description": "Conditions provide information about the state of the DeviceTaintRule and the cluster at some point in time, in a machine-readable and human-readable format.\n\nThe following condition is currently defined as part of this API, more may get added: - Type: EvictionInProgress - Status: True if there are currently pods which need to be evicted, False otherwise\n (includes the effects which don't cause eviction).\n- Reason: not specified, may change - Message: includes information about number of pending pods and already evicted pods\n in a human-readable format, updated periodically, may change\n\nFor `effect: None`, the condition above gets set once for each change to the spec, with the message containing information about what would happen if the effect was `NoExecute`. This feedback can be used to decide whether changing the effect to `NoExecute` will work as intended. It only gets set once to avoid having to constantly update the status.\n\nMust have 8 or fewer entries.",
"items": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Condition"
},
"type": "array",
"x-kubernetes-list-map-keys": [
"type"
],
"x-kubernetes-list-type": "map",
"x-kubernetes-patch-merge-key": "type",
"x-kubernetes-patch-strategy": "merge"
}
},
"type": "object"
},
"io.k8s.api.resource.v1alpha3.DeviceTaintSelector": {
"description": "DeviceTaintSelector defines which device(s) a DeviceTaintRule applies to. The empty selector matches all devices. Without a selector, no devices are matched.",
"properties": {
@ -16545,10 +16545,6 @@
"description": "If device is set, only devices with that name are selected. This field corresponds to slice.spec.devices[].name.\n\nSetting also driver and pool may be required to avoid ambiguity, but is not required.",
"type": "string"
},
"deviceClassName": {
"description": "If DeviceClassName is set, the selectors defined there must be satisfied by a device to be selected. This field corresponds to class.metadata.name.",
"type": "string"
},
"driver": {
"description": "If driver is set, only devices from that driver are selected. This fields corresponds to slice.spec.driver.",
"type": "string"
@ -16556,14 +16552,6 @@
"pool": {
"description": "If pool is set, only devices in that pool are selected.\n\nAlso setting the driver name may be useful to avoid ambiguity when different drivers use the same pool name, but this is not required because selecting pools from different drivers may also be useful, for example when drivers with node-local devices use the node name as their pool name.",
"type": "string"
},
"selectors": {
"description": "Selectors contains the same selection criteria as a ResourceClaim. Currently, CEL expressions are supported. All of these selectors must be satisfied.",
"items": {
"$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceSelector"
},
"type": "array",
"x-kubernetes-list-type": "atomic"
}
},
"type": "object"
@ -16694,7 +16682,7 @@
"description": "NodeSelector defines the nodes where the device is available.\n\nMust use exactly one term.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set."
},
"taints": {
"description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"items": {
"$ref": "#/definitions/io.k8s.api.resource.v1beta1.DeviceTaint"
},
@ -17291,7 +17279,7 @@
"description": "The device this taint is attached to has the \"effect\" on any claim which does not tolerate the taint and, through the claim, to pods using the claim.",
"properties": {
"effect": {
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.",
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.",
"type": "string"
},
"key": {
@ -17709,7 +17697,7 @@
"type": "boolean"
},
"devices": {
"description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.",
"description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints the limit is 64.",
"items": {
"$ref": "#/definitions/io.k8s.api.resource.v1beta1.Device"
},
@ -17984,7 +17972,7 @@
"description": "NodeSelector defines the nodes where the device is available.\n\nMust use exactly one term.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set."
},
"taints": {
"description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"items": {
"$ref": "#/definitions/io.k8s.api.resource.v1beta2.DeviceTaint"
},
@ -18431,7 +18419,7 @@
"description": "The device this taint is attached to has the \"effect\" on any claim which does not tolerate the taint and, through the claim, to pods using the claim.",
"properties": {
"effect": {
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.",
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.",
"type": "string"
},
"key": {
@ -18895,7 +18883,7 @@
"type": "boolean"
},
"devices": {
"description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.",
"description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints the limit is 64.",
"items": {
"$ref": "#/definitions/io.k8s.api.resource.v1beta2.Device"
},
@ -81370,6 +81358,197 @@
}
}
},
"/apis/resource.k8s.io/v1alpha3/devicetaintrules/{name}/status": {
"get": {
"consumes": [
"*/*"
],
"description": "read status of the specified DeviceTaintRule",
"operationId": "readResourceV1alpha3DeviceTaintRuleStatus",
"produces": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf",
"application/cbor"
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"401": {
"description": "Unauthorized"
}
},
"schemes": [
"https"
],
"tags": [
"resource_v1alpha3"
],
"x-kubernetes-action": "get",
"x-kubernetes-group-version-kind": {
"group": "resource.k8s.io",
"kind": "DeviceTaintRule",
"version": "v1alpha3"
}
},
"parameters": [
{
"description": "name of the DeviceTaintRule",
"in": "path",
"name": "name",
"required": true,
"type": "string",
"uniqueItems": true
},
{
"$ref": "#/parameters/pretty-tJGM1-ng"
}
],
"patch": {
"consumes": [
"application/json-patch+json",
"application/merge-patch+json",
"application/strategic-merge-patch+json",
"application/apply-patch+yaml",
"application/apply-patch+cbor"
],
"description": "partially update status of the specified DeviceTaintRule",
"operationId": "patchResourceV1alpha3DeviceTaintRuleStatus",
"parameters": [
{
"$ref": "#/parameters/body-78PwaGsr"
},
{
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"in": "query",
"name": "dryRun",
"type": "string",
"uniqueItems": true
},
{
"$ref": "#/parameters/fieldManager-7c6nTn1T"
},
{
"description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.",
"in": "query",
"name": "fieldValidation",
"type": "string",
"uniqueItems": true
},
{
"$ref": "#/parameters/force-tOGGb0Yi"
}
],
"produces": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf",
"application/cbor"
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"401": {
"description": "Unauthorized"
}
},
"schemes": [
"https"
],
"tags": [
"resource_v1alpha3"
],
"x-kubernetes-action": "patch",
"x-kubernetes-group-version-kind": {
"group": "resource.k8s.io",
"kind": "DeviceTaintRule",
"version": "v1alpha3"
}
},
"put": {
"consumes": [
"*/*"
],
"description": "replace status of the specified DeviceTaintRule",
"operationId": "replaceResourceV1alpha3DeviceTaintRuleStatus",
"parameters": [
{
"in": "body",
"name": "body",
"required": true,
"schema": {
"$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
{
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"in": "query",
"name": "dryRun",
"type": "string",
"uniqueItems": true
},
{
"$ref": "#/parameters/fieldManager-Qy4HdaTW"
},
{
"description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.",
"in": "query",
"name": "fieldValidation",
"type": "string",
"uniqueItems": true
}
],
"produces": [
"application/json",
"application/yaml",
"application/vnd.kubernetes.protobuf",
"application/cbor"
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"201": {
"description": "Created",
"schema": {
"$ref": "#/definitions/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"401": {
"description": "Unauthorized"
}
},
"schemes": [
"https"
],
"tags": [
"resource_v1alpha3"
],
"x-kubernetes-action": "put",
"x-kubernetes-group-version-kind": {
"group": "resource.k8s.io",
"kind": "DeviceTaintRule",
"version": "v1alpha3"
}
}
},
"/apis/resource.k8s.io/v1alpha3/watch/devicetaintrules": {
"get": {
"consumes": [

View file

@ -401,7 +401,7 @@
"description": "NodeSelector defines the nodes where the device is available.\n\nMust use exactly one term.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set."
},
"taints": {
"description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"items": {
"allOf": [
{
@ -980,7 +980,7 @@
"properties": {
"effect": {
"default": "",
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.",
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.",
"type": "string"
},
"key": {
@ -1571,7 +1571,7 @@
"type": "boolean"
},
"devices": {
"description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.",
"description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints the limit is 64.",
"items": {
"allOf": [
{

View file

@ -1,40 +1,12 @@
{
"components": {
"schemas": {
"io.k8s.api.resource.v1alpha3.CELDeviceSelector": {
"description": "CELDeviceSelector contains a CEL expression for selecting a device.",
"properties": {
"expression": {
"default": "",
"description": "Expression is a CEL expression which evaluates a single device. It must evaluate to true when the device under consideration satisfies the desired criteria, and false when it does not. Any other result is an error and causes allocation of devices to abort.\n\nThe expression's input is an object named \"device\", which carries the following properties:\n - driver (string): the name of the driver which defines this device.\n - attributes (map[string]object): the device's attributes, grouped by prefix\n (e.g. device.attributes[\"dra.example.com\"] evaluates to an object with all\n of the attributes which were prefixed by \"dra.example.com\".\n - capacity (map[string]object): the device's capacities, grouped by prefix.\n\nExample: Consider a device with driver=\"dra.example.com\", which exposes two attributes named \"model\" and \"ext.example.com/family\" and which exposes one capacity named \"modules\". This input to this expression would have the following fields:\n\n device.driver\n device.attributes[\"dra.example.com\"].model\n device.attributes[\"ext.example.com\"].family\n device.capacity[\"dra.example.com\"].modules\n\nThe device.driver field can be used to check for a specific driver, either as a high-level precondition (i.e. you only want to consider devices from this driver) or as part of a multi-clause expression that is meant to consider devices from different drivers.\n\nThe value type of each attribute is defined by the device definition, and users who write these expressions must consult the documentation for their specific drivers. The value type of each capacity is Quantity.\n\nIf an unknown prefix is used as a lookup in either device.attributes or device.capacity, an empty map will be returned. Any reference to an unknown field will cause an evaluation error and allocation to abort.\n\nA robust expression should check for the existence of attributes before referencing them.\n\nFor ease of use, the cel.bind() function is enabled, and can be used to simplify expressions that access multiple attributes with the same domain. For example:\n\n cel.bind(dra, device.attributes[\"dra.example.com\"], dra.someBool && dra.anotherBool)\n\nThe length of the expression must be smaller or equal to 10 Ki. The cost of evaluating it is also limited based on the estimated number of logical steps.",
"type": "string"
}
},
"required": [
"expression"
],
"type": "object"
},
"io.k8s.api.resource.v1alpha3.DeviceSelector": {
"description": "DeviceSelector must have exactly one field set.",
"properties": {
"cel": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.CELDeviceSelector"
}
],
"description": "CEL contains a CEL expression for selecting a device."
}
},
"type": "object"
},
"io.k8s.api.resource.v1alpha3.DeviceTaint": {
"description": "The device this taint is attached to has the \"effect\" on any claim which does not tolerate the taint and, through the claim, to pods using the claim.",
"properties": {
"effect": {
"default": "",
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.",
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.",
"type": "string"
},
"key": {
@ -89,6 +61,15 @@
],
"default": {},
"description": "Spec specifies the selector and one taint.\n\nChanging the spec automatically increments the metadata.generation number."
},
"status": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRuleStatus"
}
],
"default": {},
"description": "Status provides information about what was requested in the spec."
}
},
"required": [
@ -157,7 +138,7 @@
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintSelector"
}
],
"description": "DeviceSelector defines which device(s) the taint is applied to. All selector criteria must be satified for a device to match. The empty selector matches all devices. Without a selector, no devices are matches."
"description": "DeviceSelector defines which device(s) the taint is applied to. All selector criteria must be satisfied for a device to match. The empty selector matches all devices. Without a selector, no devices are matches."
},
"taint": {
"allOf": [
@ -174,6 +155,30 @@
],
"type": "object"
},
"io.k8s.api.resource.v1alpha3.DeviceTaintRuleStatus": {
"description": "DeviceTaintRuleStatus provides information about an on-going pod eviction.",
"properties": {
"conditions": {
"description": "Conditions provide information about the state of the DeviceTaintRule and the cluster at some point in time, in a machine-readable and human-readable format.\n\nThe following condition is currently defined as part of this API, more may get added: - Type: EvictionInProgress - Status: True if there are currently pods which need to be evicted, False otherwise\n (includes the effects which don't cause eviction).\n- Reason: not specified, may change - Message: includes information about number of pending pods and already evicted pods\n in a human-readable format, updated periodically, may change\n\nFor `effect: None`, the condition above gets set once for each change to the spec, with the message containing information about what would happen if the effect was `NoExecute`. This feedback can be used to decide whether changing the effect to `NoExecute` will work as intended. It only gets set once to avoid having to constantly update the status.\n\nMust have 8 or fewer entries.",
"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",
"x-kubernetes-patch-merge-key": "type",
"x-kubernetes-patch-strategy": "merge"
}
},
"type": "object"
},
"io.k8s.api.resource.v1alpha3.DeviceTaintSelector": {
"description": "DeviceTaintSelector defines which device(s) a DeviceTaintRule applies to. The empty selector matches all devices. Without a selector, no devices are matched.",
"properties": {
@ -181,10 +186,6 @@
"description": "If device is set, only devices with that name are selected. This field corresponds to slice.spec.devices[].name.\n\nSetting also driver and pool may be required to avoid ambiguity, but is not required.",
"type": "string"
},
"deviceClassName": {
"description": "If DeviceClassName is set, the selectors defined there must be satisfied by a device to be selected. This field corresponds to class.metadata.name.",
"type": "string"
},
"driver": {
"description": "If driver is set, only devices from that driver are selected. This fields corresponds to slice.spec.driver.",
"type": "string"
@ -192,19 +193,6 @@
"pool": {
"description": "If pool is set, only devices in that pool are selected.\n\nAlso setting the driver name may be useful to avoid ambiguity when different drivers use the same pool name, but this is not required because selecting pools from different drivers may also be useful, for example when drivers with node-local devices use the node name as their pool name.",
"type": "string"
},
"selectors": {
"description": "Selectors contains the same selection criteria as a ResourceClaim. Currently, CEL expressions are supported. All of these selectors must be satisfied.",
"items": {
"allOf": [
{
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceSelector"
}
],
"default": {}
},
"type": "array",
"x-kubernetes-list-type": "atomic"
}
},
"type": "object"
@ -323,6 +311,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": {
@ -2334,6 +2368,315 @@
}
}
},
"/apis/resource.k8s.io/v1alpha3/devicetaintrules/{name}/status": {
"get": {
"description": "read status of the specified DeviceTaintRule",
"operationId": "readResourceV1alpha3DeviceTaintRuleStatus",
"responses": {
"200": {
"content": {
"application/cbor": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
}
},
"description": "OK"
},
"401": {
"description": "Unauthorized"
}
},
"tags": [
"resource_v1alpha3"
],
"x-kubernetes-action": "get",
"x-kubernetes-group-version-kind": {
"group": "resource.k8s.io",
"kind": "DeviceTaintRule",
"version": "v1alpha3"
}
},
"parameters": [
{
"description": "name of the DeviceTaintRule",
"in": "path",
"name": "name",
"required": true,
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"description": "If 'true', then the output is pretty printed. Defaults to 'false' unless the user-agent indicates a browser or command-line HTTP tool (curl and wget).",
"in": "query",
"name": "pretty",
"schema": {
"type": "string",
"uniqueItems": true
}
}
],
"patch": {
"description": "partially update status of the specified DeviceTaintRule",
"operationId": "patchResourceV1alpha3DeviceTaintRuleStatus",
"parameters": [
{
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"in": "query",
"name": "dryRun",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint. This field is required for apply requests (application/apply-patch) but optional for non-apply patch types (JsonPatch, MergePatch, StrategicMergePatch).",
"in": "query",
"name": "fieldManager",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.",
"in": "query",
"name": "fieldValidation",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"description": "Force is going to \"force\" Apply requests. It means user will re-acquire conflicting fields owned by other people. Force flag must be unset for non-apply patch requests.",
"in": "query",
"name": "force",
"schema": {
"type": "boolean",
"uniqueItems": true
}
}
],
"requestBody": {
"content": {
"application/apply-patch+cbor": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
}
},
"application/apply-patch+yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
}
},
"application/json-patch+json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
}
},
"application/merge-patch+json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
}
},
"application/strategic-merge-patch+json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.Patch"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/cbor": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
}
},
"description": "OK"
},
"201": {
"content": {
"application/cbor": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
}
},
"description": "Created"
},
"401": {
"description": "Unauthorized"
}
},
"tags": [
"resource_v1alpha3"
],
"x-kubernetes-action": "patch",
"x-kubernetes-group-version-kind": {
"group": "resource.k8s.io",
"kind": "DeviceTaintRule",
"version": "v1alpha3"
}
},
"put": {
"description": "replace status of the specified DeviceTaintRule",
"operationId": "replaceResourceV1alpha3DeviceTaintRuleStatus",
"parameters": [
{
"description": "When present, indicates that modifications should not be persisted. An invalid or unrecognized dryRun directive will result in an error response and no further processing of the request. Valid values are: - All: all dry run stages will be processed",
"in": "query",
"name": "dryRun",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"description": "fieldManager is a name associated with the actor or entity that is making these changes. The value must be less than or 128 characters long, and only contain printable characters, as defined by https://golang.org/pkg/unicode/#IsPrint.",
"in": "query",
"name": "fieldManager",
"schema": {
"type": "string",
"uniqueItems": true
}
},
{
"description": "fieldValidation instructs the server on how to handle objects in the request (POST/PUT/PATCH) containing unknown or duplicate fields. Valid values are: - Ignore: This will ignore any unknown fields that are silently dropped from the object, and will ignore all but the last duplicate field that the decoder encounters. This is the default behavior prior to v1.23. - Warn: This will send a warning via the standard warning response header for each unknown field that is dropped from the object, and for each duplicate field that is encountered. The request will still succeed if there are no other errors, and will only persist the last of any duplicate fields. This is the default in v1.23+ - Strict: This will fail the request with a BadRequest error if any unknown fields would be dropped from the object, or if any duplicate fields are present. The error returned from the server will contain all unknown and duplicate fields encountered.",
"in": "query",
"name": "fieldValidation",
"schema": {
"type": "string",
"uniqueItems": true
}
}
],
"requestBody": {
"content": {
"*/*": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/cbor": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
}
},
"description": "OK"
},
"201": {
"content": {
"application/cbor": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"application/vnd.kubernetes.protobuf": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
},
"application/yaml": {
"schema": {
"$ref": "#/components/schemas/io.k8s.api.resource.v1alpha3.DeviceTaintRule"
}
}
},
"description": "Created"
},
"401": {
"description": "Unauthorized"
}
},
"tags": [
"resource_v1alpha3"
],
"x-kubernetes-action": "put",
"x-kubernetes-group-version-kind": {
"group": "resource.k8s.io",
"kind": "DeviceTaintRule",
"version": "v1alpha3"
}
}
},
"/apis/resource.k8s.io/v1alpha3/watch/devicetaintrules": {
"get": {
"description": "watch individual changes to a list of DeviceTaintRule. deprecated: use the 'watch' parameter with a list operation instead.",

View file

@ -262,7 +262,7 @@
"description": "NodeSelector defines the nodes where the device is available.\n\nMust use exactly one term.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set."
},
"taints": {
"description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"items": {
"allOf": [
{
@ -1038,7 +1038,7 @@
"properties": {
"effect": {
"default": "",
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.",
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.",
"type": "string"
},
"key": {
@ -1568,7 +1568,7 @@
"type": "boolean"
},
"devices": {
"description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.",
"description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints the limit is 64.",
"items": {
"allOf": [
{

View file

@ -401,7 +401,7 @@
"description": "NodeSelector defines the nodes where the device is available.\n\nMust use exactly one term.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set."
},
"taints": {
"description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"description": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"items": {
"allOf": [
{
@ -980,7 +980,7 @@
"properties": {
"effect": {
"default": "",
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.",
"description": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.",
"type": "string"
},
"key": {
@ -1571,7 +1571,7 @@
"type": "boolean"
},
"devices": {
"description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.",
"description": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints the limit is 64.",
"items": {
"allOf": [
{

View file

@ -276,7 +276,7 @@ func newDeviceTaintEvictionController(ctx context.Context, controllerContext Con
controllerName,
)
return newControllerLoop(func(ctx context.Context) {
if err := deviceTaintEvictionController.Run(ctx); err != nil {
if err := deviceTaintEvictionController.Run(ctx, int(controllerContext.ComponentConfig.DeviceTaintEvictionController.ConcurrentSyncs)); err != nil {
klog.FromContext(ctx).Error(err, "Device taint processing leading to Pod eviction failed and is now paused")
}
<-ctx.Done()

View file

@ -0,0 +1,63 @@
/*
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.
*/
package options
import (
"fmt"
"github.com/spf13/pflag"
devicetaintevictionconfig "k8s.io/kubernetes/pkg/controller/devicetainteviction/config"
)
// DeviceTaintEvictionControllerOptions holds the DeviceTaintEvictionController options.
type DeviceTaintEvictionControllerOptions struct {
*devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration
}
// AddFlags adds flags related to DeviceTaintEvictionController for controller manager to the specified FlagSet.
func (o *DeviceTaintEvictionControllerOptions) AddFlags(fs *pflag.FlagSet) {
if o == nil {
return
}
fs.Int32Var(&o.ConcurrentSyncs, "concurrent-device-taint-eviction-syncs", o.ConcurrentSyncs, "The number of operations (evicting pods, updating DeviceTaintRule status) allowed to run concurrently. Greater number = more responsive, but more CPU (and network) load")
}
// ApplyTo fills up DeviceTaintEvictionController config with options.
func (o *DeviceTaintEvictionControllerOptions) ApplyTo(cfg *devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration) error {
if o == nil {
return nil
}
cfg.ConcurrentSyncs = o.ConcurrentSyncs
return nil
}
// Validate checks validation of DeviceTaintEvictionControllerOptions.
func (o *DeviceTaintEvictionControllerOptions) Validate() []error {
if o == nil {
return nil
}
var errs []error
if o.ConcurrentSyncs <= 0 {
errs = append(errs, fmt.Errorf("concurrent-device-taint-eviction-syncs must be greater than zero, got %d", o.ConcurrentSyncs))
}
return errs
}

View file

@ -0,0 +1,219 @@
/*
Copyright 2025 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 options
import (
"reflect"
"strings"
"testing"
"github.com/spf13/pflag"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
devicetaintevictionconfig "k8s.io/kubernetes/pkg/controller/devicetainteviction/config"
)
func TestDeviceTaintEvictionControllerOptions_AddFlags(t *testing.T) {
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
opts := &DeviceTaintEvictionControllerOptions{
&devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{
ConcurrentSyncs: 50,
},
}
opts.AddFlags(fs)
// Test that the flag was added
flag := fs.Lookup("concurrent-device-taint-eviction-syncs")
if flag == nil {
t.Error("concurrent-device-taint-eviction-syncs flag was not added")
return
}
// Test that the flag has the correct default value
if flag.DefValue != "50" {
t.Errorf("expected default value 50, got %s", flag.DefValue)
}
// Test flag parsing
args := []string{"--concurrent-device-taint-eviction-syncs=25"}
if err := fs.Parse(args); err != nil {
t.Errorf("failed to parse flags: %v", err)
}
if opts.ConcurrentSyncs != 25 {
t.Errorf("expected ConcurrentSyncs to be 25, got %d", opts.ConcurrentSyncs)
}
}
func TestDeviceTaintEvictionControllerOptions_AddFlags_Nil(t *testing.T) {
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
var opts *DeviceTaintEvictionControllerOptions
// Should not panic when options is nil
opts.AddFlags(fs)
// Flag should not be added
flag := fs.Lookup("concurrent-device-taint-eviction-syncs")
if flag != nil {
t.Error("concurrent-device-taint-eviction-syncs flag should not be added when options is nil")
}
}
func TestDeviceTaintEvictionControllerOptions_ApplyTo(t *testing.T) {
opts := &DeviceTaintEvictionControllerOptions{
&devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{
ConcurrentSyncs: 75,
},
}
cfg := &devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{}
err := opts.ApplyTo(cfg)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if cfg.ConcurrentSyncs != 75 {
t.Errorf("expected ConcurrentSyncs to be 75, got %d", cfg.ConcurrentSyncs)
}
}
func TestDeviceTaintEvictionControllerOptions_ApplyTo_Nil(t *testing.T) {
var opts *DeviceTaintEvictionControllerOptions
cfg := &devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{
ConcurrentSyncs: 50,
}
err := opts.ApplyTo(cfg)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
// Configuration should remain unchanged
if cfg.ConcurrentSyncs != 50 {
t.Errorf("expected ConcurrentSyncs to remain 50, got %d", cfg.ConcurrentSyncs)
}
}
func TestDeviceTaintEvictionControllerOptions_Validate(t *testing.T) {
testCases := []struct {
name string
concurrentSyncs int32
expectErrors bool
expectedErrorSubString string
}{
{
name: "valid concurrent syncs",
concurrentSyncs: 50,
expectErrors: false,
},
{
name: "valid minimum concurrent syncs",
concurrentSyncs: 1,
expectErrors: false,
},
{
name: "invalid zero concurrent syncs",
concurrentSyncs: 0,
expectErrors: true,
expectedErrorSubString: "concurrent-device-taint-eviction-syncs must be greater than zero, got 0",
},
{
name: "invalid negative concurrent syncs",
concurrentSyncs: -5,
expectErrors: true,
expectedErrorSubString: "concurrent-device-taint-eviction-syncs must be greater than zero, got -5",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
opts := &DeviceTaintEvictionControllerOptions{
&devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{
ConcurrentSyncs: tc.concurrentSyncs,
},
}
errs := opts.Validate()
if tc.expectErrors && len(errs) == 0 {
t.Error("expected validation errors, but got none")
}
if !tc.expectErrors && len(errs) > 0 {
t.Errorf("expected no validation errors, but got: %v", errs)
}
if tc.expectErrors && len(errs) > 0 {
gotErr := utilerrors.NewAggregate(errs).Error()
if !strings.Contains(gotErr, tc.expectedErrorSubString) {
t.Errorf("expected error to contain %q, but got %q", tc.expectedErrorSubString, gotErr)
}
}
})
}
}
func TestDeviceTaintEvictionControllerOptions_Validate_Nil(t *testing.T) {
var opts *DeviceTaintEvictionControllerOptions
errs := opts.Validate()
if len(errs) != 0 {
t.Errorf("expected no validation errors for nil options, but got: %v", errs)
}
}
func TestDeviceTaintEvictionControllerOptions_Integration(t *testing.T) {
// Test the complete workflow: create options, set flags, apply to config
fs := pflag.NewFlagSet("test", pflag.ContinueOnError)
opts := &DeviceTaintEvictionControllerOptions{
&devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{
ConcurrentSyncs: 50,
},
}
// Add flags
opts.AddFlags(fs)
// Parse flags with custom value
args := []string{"--concurrent-device-taint-eviction-syncs=100"}
if err := fs.Parse(args); err != nil {
t.Fatalf("failed to parse flags: %v", err)
}
// Validate
errs := opts.Validate()
if len(errs) > 0 {
t.Fatalf("validation failed: %v", errs)
}
// Apply to config
cfg := &devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{}
if err := opts.ApplyTo(cfg); err != nil {
t.Fatalf("failed to apply options: %v", err)
}
// Verify final configuration
expected := &devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{
ConcurrentSyncs: 100,
}
if !reflect.DeepEqual(cfg, expected) {
t.Errorf("expected config %+v, got %+v", expected, cfg)
}
}

View file

@ -77,6 +77,7 @@ type KubeControllerManagerOptions struct {
CSRSigningController *CSRSigningControllerOptions
DaemonSetController *DaemonSetControllerOptions
DeploymentController *DeploymentControllerOptions
DeviceTaintEvictionController *DeviceTaintEvictionControllerOptions
StatefulSetController *StatefulSetControllerOptions
DeprecatedFlags *DeprecatedControllerOptions
EndpointController *EndpointControllerOptions
@ -151,6 +152,9 @@ func NewKubeControllerManagerOptions() (*KubeControllerManagerOptions, error) {
DeploymentController: &DeploymentControllerOptions{
&componentConfig.DeploymentController,
},
DeviceTaintEvictionController: &DeviceTaintEvictionControllerOptions{
&componentConfig.DeviceTaintEvictionController,
},
StatefulSetController: &StatefulSetControllerOptions{
&componentConfig.StatefulSetController,
},
@ -272,6 +276,7 @@ func (s *KubeControllerManagerOptions) Flags(allControllers []string, disabledBy
s.AttachDetachController.AddFlags(fss.FlagSet(names.PersistentVolumeAttachDetachController))
s.CSRSigningController.AddFlags(fss.FlagSet(names.CertificateSigningRequestSigningController))
s.DeploymentController.AddFlags(fss.FlagSet(names.DeploymentController))
s.DeviceTaintEvictionController.AddFlags(fss.FlagSet(names.DeviceTaintEvictionController))
s.StatefulSetController.AddFlags(fss.FlagSet(names.StatefulSetController))
s.DaemonSetController.AddFlags(fss.FlagSet(names.DaemonSetController))
s.DeprecatedFlags.AddFlags(fss.FlagSet("deprecated"))
@ -341,6 +346,9 @@ func (s *KubeControllerManagerOptions) ApplyTo(c *kubecontrollerconfig.Config, a
if err := s.DeploymentController.ApplyTo(&c.ComponentConfig.DeploymentController); err != nil {
return err
}
if err := s.DeviceTaintEvictionController.ApplyTo(&c.ComponentConfig.DeviceTaintEvictionController); err != nil {
return err
}
if err := s.StatefulSetController.ApplyTo(&c.ComponentConfig.StatefulSetController); err != nil {
return err
}
@ -440,6 +448,7 @@ func (s *KubeControllerManagerOptions) Validate(allControllers []string, disable
errs = append(errs, s.CSRSigningController.Validate()...)
errs = append(errs, s.DaemonSetController.Validate()...)
errs = append(errs, s.DeploymentController.Validate()...)
errs = append(errs, s.DeviceTaintEvictionController.Validate()...)
errs = append(errs, s.StatefulSetController.Validate()...)
errs = append(errs, s.DeprecatedFlags.Validate()...)
errs = append(errs, s.EndpointController.Validate()...)

View file

@ -56,6 +56,7 @@ import (
cronjobconfig "k8s.io/kubernetes/pkg/controller/cronjob/config"
daemonconfig "k8s.io/kubernetes/pkg/controller/daemon/config"
deploymentconfig "k8s.io/kubernetes/pkg/controller/deployment/config"
devicetaintevictionconfig "k8s.io/kubernetes/pkg/controller/devicetainteviction/config"
endpointconfig "k8s.io/kubernetes/pkg/controller/endpoint/config"
endpointsliceconfig "k8s.io/kubernetes/pkg/controller/endpointslice/config"
endpointslicemirroringconfig "k8s.io/kubernetes/pkg/controller/endpointslicemirroring/config"
@ -98,6 +99,7 @@ var args = []string{
"--cluster-signing-legacy-unknown-cert-file=/cluster-signing-legacy-unknown/cert-file",
"--cluster-signing-legacy-unknown-key-file=/cluster-signing-legacy-unknown/key-file",
"--concurrent-deployment-syncs=10",
"--concurrent-device-taint-eviction-syncs=10",
"--concurrent-daemonset-syncs=10",
"--concurrent-horizontal-pod-autoscaler-syncs=10",
"--concurrent-statefulset-syncs=15",
@ -273,6 +275,11 @@ func TestAddFlags(t *testing.T) {
ConcurrentDeploymentSyncs: 10,
},
},
DeviceTaintEvictionController: &DeviceTaintEvictionControllerOptions{
&devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{
ConcurrentSyncs: 10,
},
},
StatefulSetController: &StatefulSetControllerOptions{
&statefulsetconfig.StatefulSetControllerConfiguration{
ConcurrentStatefulSetSyncs: 15,
@ -624,6 +631,9 @@ func TestApplyTo(t *testing.T) {
DeploymentController: deploymentconfig.DeploymentControllerConfiguration{
ConcurrentDeploymentSyncs: 10,
},
DeviceTaintEvictionController: devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{
ConcurrentSyncs: 10,
},
StatefulSetController: statefulsetconfig.StatefulSetControllerConfiguration{
ConcurrentStatefulSetSyncs: 15,
},
@ -1262,6 +1272,15 @@ func TestValidateControllersOptions(t *testing.T) {
},
},
},
{
name: "DeviceTaintEvictionControllerOptions",
expectErrors: false,
options: &DeviceTaintEvictionControllerOptions{
&devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration{
ConcurrentSyncs: 10,
},
},
},
{
name: "DeprecatedControllerOptions",
expectErrors: false,

View file

@ -145,7 +145,7 @@ type ResourceSliceSpec struct {
// Devices lists some or all of the devices in this pool.
//
// Must not have more than 128 entries.
// Must not have more than 128 entries. If any device uses taints the limit is 64.
//
// +optional
// +listType=atomic
@ -243,6 +243,7 @@ type ResourcePool struct {
const ResourceSliceMaxSharedCapacity = 128
const ResourceSliceMaxDevices = 128
const ResourceSliceMaxDevicesWithTaints = 64
const PoolNameMaxLength = validation.DNS1123SubdomainMaxLength // Same as for a single node name.
const BindingConditionsMaxSize = 4
const BindingFailureConditionsMaxSize = 4
@ -326,7 +327,9 @@ type Device struct {
// If specified, these are the driver-defined taints.
//
// The maximum number of taints is 4.
// The maximum number of taints is 16. If taints are set for
// any device in a ResourceSlice, then the maximum number of
// allowed devices per ResourceSlice is 64 instead of 128.
//
// This is an alpha field and requires enabling the DRADeviceTaints
// feature gate.
@ -601,8 +604,8 @@ type DeviceAttribute struct {
// DeviceAttributeMaxValueLength is the maximum length of a string or version attribute value.
const DeviceAttributeMaxValueLength = 64
// DeviceTaintsMaxLength is the maximum number of taints per device.
const DeviceTaintsMaxLength = 4
// DeviceTaintsMaxLength is the maximum number of taints per Device.
const DeviceTaintsMaxLength = 16
// The device this taint is attached to has the "effect" on
// any claim which does not tolerate the taint and, through the claim,
@ -622,8 +625,10 @@ type DeviceTaint struct {
// The effect of the taint on claims that do not tolerate the taint
// and through such claims on the pods using them.
// Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here.
//
// Valid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here. More effects may get added in the future.
// Consumers must treat unknown effects like None.
//
// +required
Effect DeviceTaintEffect
@ -632,6 +637,14 @@ type DeviceTaint struct {
//
// Implementing PreferNoSchedule would depend on a scoring solution for DRA.
// It might get added as part of that.
//
// A possible future new effect is NoExecuteWithPodDisruptionBudget:
// honor the pod disruption budget instead of simply deleting pods.
// This is currently undecided, it could also be a separate field.
//
// Validation must be prepared to allow unknown enums in stored objects,
// which will enable adding new enums within a single release without
// ratcheting.
// TimeAdded represents the time at which the taint was added.
// Added automatically during create or update if not set.
@ -650,6 +663,9 @@ type DeviceTaint struct {
type DeviceTaintEffect string
const (
// No effect, the taint is purely informational.
DeviceTaintEffectNone DeviceTaintEffect = "None"
// Do not allow new pods to schedule which use a tainted device unless they tolerate the taint,
// but allow all pods submitted to Kubelet without going through the scheduler
// to start, and allow all already-running pods to continue running.
@ -1876,18 +1892,16 @@ type DeviceTaintRule struct {
// Changing the spec automatically increments the metadata.generation number.
Spec DeviceTaintRuleSpec
// ^^^
// A spec gets added because adding a status seems likely.
// Such a status could provide feedback on applying the
// eviction and/or statistics (number of matching devices,
// affected allocated claims, pods remaining to be evicted,
// etc.).
// Status provides information about what was requested in the spec.
//
// +optional
Status DeviceTaintRuleStatus
}
// DeviceTaintRuleSpec specifies the selector and one taint.
type DeviceTaintRuleSpec struct {
// DeviceSelector defines which device(s) the taint is applied to.
// All selector criteria must be satified for a device to
// All selector criteria must be satisfied for a device to
// match. The empty selector matches all devices. Without
// a selector, no devices are matches.
//
@ -1904,13 +1918,6 @@ type DeviceTaintRuleSpec struct {
// The empty selector matches all devices. Without a selector, no devices
// are matched.
type DeviceTaintSelector struct {
// If DeviceClassName is set, the selectors defined there must be
// satisfied by a device to be selected. This field corresponds
// to class.metadata.name.
//
// +optional
DeviceClassName *string
// If driver is set, only devices from that driver are selected.
// This fields corresponds to slice.spec.driver.
//
@ -1937,16 +1944,43 @@ type DeviceTaintSelector struct {
//
// +optional
Device *string
}
// Selectors contains the same selection criteria as a ResourceClaim.
// Currently, CEL expressions are supported. All of these selectors
// must be satisfied.
// DeviceTaintRuleStatus provides information about an on-going pod eviction.
type DeviceTaintRuleStatus struct {
// Conditions provide information about the state of the DeviceTaintRule
// and the cluster at some point in time,
// in a machine-readable and human-readable format.
//
// The following condition is currently defined as part of this API, more may
// get added:
// - Type: EvictionInProgress
// - Status: True if there are currently pods which need to be evicted, False otherwise
// (includes the effects which don't cause eviction).
// - Reason: not specified, may change
// - Message: includes information about number of pending pods and already evicted pods
// in a human-readable format, updated periodically, may change
//
// For `effect: None`, the condition above gets set once for each change to
// the spec, with the message containing information about what would happen
// if the effect was `NoExecute`. This feedback can be used to decide whether
// changing the effect to `NoExecute` will work as intended. It only gets
// set once to avoid having to constantly update the status.
//
// Must have 8 or less entries.
//
// +optional
// +listType=atomic
Selectors []DeviceSelector
// +listType=map
// +listMapKey=type
Conditions []metav1.Condition
}
// DeviceTaintRuleStatusMaxConditions is the maximum number of conditions in DeviceTaintRuleStatus.
const DeviceTaintRuleStatusMaxConditions = 8
// DeviceTaintConditionEvictionInProgress is the publicly documented condition type for the DeviceTaintRuleStatus.
const DeviceTaintConditionEvictionInProgress = "EvictionInProgress"
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// DeviceTaintRuleList is a collection of DeviceTaintRules.

View file

@ -1237,7 +1237,7 @@ func Validate_DeviceTaint(ctx context.Context, op operation.Operation, fldPath *
return errs
}
var symbolsForDeviceTaintEffect = sets.New(resourcev1.DeviceTaintEffectNoExecute, resourcev1.DeviceTaintEffectNoSchedule)
var symbolsForDeviceTaintEffect = sets.New(resourcev1.DeviceTaintEffectNoExecute, resourcev1.DeviceTaintEffectNoSchedule, resourcev1.DeviceTaintEffectNone)
// Validate_DeviceTaintEffect validates an instance of DeviceTaintEffect according
// to declarative validation rules in the API schema.

View file

@ -98,6 +98,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resourcev1alpha3.DeviceTaintRuleStatus)(nil), (*resource.DeviceTaintRuleStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha3_DeviceTaintRuleStatus_To_resource_DeviceTaintRuleStatus(a.(*resourcev1alpha3.DeviceTaintRuleStatus), b.(*resource.DeviceTaintRuleStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resource.DeviceTaintRuleStatus)(nil), (*resourcev1alpha3.DeviceTaintRuleStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resource_DeviceTaintRuleStatus_To_v1alpha3_DeviceTaintRuleStatus(a.(*resource.DeviceTaintRuleStatus), b.(*resourcev1alpha3.DeviceTaintRuleStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resourcev1alpha3.DeviceTaintSelector)(nil), (*resource.DeviceTaintSelector)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha3_DeviceTaintSelector_To_resource_DeviceTaintSelector(a.(*resourcev1alpha3.DeviceTaintSelector), b.(*resource.DeviceTaintSelector), scope)
}); err != nil {
@ -182,6 +192,9 @@ func autoConvert_v1alpha3_DeviceTaintRule_To_resource_DeviceTaintRule(in *resour
if err := Convert_v1alpha3_DeviceTaintRuleSpec_To_resource_DeviceTaintRuleSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_v1alpha3_DeviceTaintRuleStatus_To_resource_DeviceTaintRuleStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
@ -195,6 +208,9 @@ func autoConvert_resource_DeviceTaintRule_To_v1alpha3_DeviceTaintRule(in *resour
if err := Convert_resource_DeviceTaintRuleSpec_To_v1alpha3_DeviceTaintRuleSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_resource_DeviceTaintRuleStatus_To_v1alpha3_DeviceTaintRuleStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
@ -251,12 +267,30 @@ func Convert_resource_DeviceTaintRuleSpec_To_v1alpha3_DeviceTaintRuleSpec(in *re
return autoConvert_resource_DeviceTaintRuleSpec_To_v1alpha3_DeviceTaintRuleSpec(in, out, s)
}
func autoConvert_v1alpha3_DeviceTaintRuleStatus_To_resource_DeviceTaintRuleStatus(in *resourcev1alpha3.DeviceTaintRuleStatus, out *resource.DeviceTaintRuleStatus, s conversion.Scope) error {
out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions))
return nil
}
// Convert_v1alpha3_DeviceTaintRuleStatus_To_resource_DeviceTaintRuleStatus is an autogenerated conversion function.
func Convert_v1alpha3_DeviceTaintRuleStatus_To_resource_DeviceTaintRuleStatus(in *resourcev1alpha3.DeviceTaintRuleStatus, out *resource.DeviceTaintRuleStatus, s conversion.Scope) error {
return autoConvert_v1alpha3_DeviceTaintRuleStatus_To_resource_DeviceTaintRuleStatus(in, out, s)
}
func autoConvert_resource_DeviceTaintRuleStatus_To_v1alpha3_DeviceTaintRuleStatus(in *resource.DeviceTaintRuleStatus, out *resourcev1alpha3.DeviceTaintRuleStatus, s conversion.Scope) error {
out.Conditions = *(*[]v1.Condition)(unsafe.Pointer(&in.Conditions))
return nil
}
// Convert_resource_DeviceTaintRuleStatus_To_v1alpha3_DeviceTaintRuleStatus is an autogenerated conversion function.
func Convert_resource_DeviceTaintRuleStatus_To_v1alpha3_DeviceTaintRuleStatus(in *resource.DeviceTaintRuleStatus, out *resourcev1alpha3.DeviceTaintRuleStatus, s conversion.Scope) error {
return autoConvert_resource_DeviceTaintRuleStatus_To_v1alpha3_DeviceTaintRuleStatus(in, out, s)
}
func autoConvert_v1alpha3_DeviceTaintSelector_To_resource_DeviceTaintSelector(in *resourcev1alpha3.DeviceTaintSelector, out *resource.DeviceTaintSelector, s conversion.Scope) error {
out.DeviceClassName = (*string)(unsafe.Pointer(in.DeviceClassName))
out.Driver = (*string)(unsafe.Pointer(in.Driver))
out.Pool = (*string)(unsafe.Pointer(in.Pool))
out.Device = (*string)(unsafe.Pointer(in.Device))
out.Selectors = *(*[]resource.DeviceSelector)(unsafe.Pointer(&in.Selectors))
return nil
}
@ -266,11 +300,9 @@ func Convert_v1alpha3_DeviceTaintSelector_To_resource_DeviceTaintSelector(in *re
}
func autoConvert_resource_DeviceTaintSelector_To_v1alpha3_DeviceTaintSelector(in *resource.DeviceTaintSelector, out *resourcev1alpha3.DeviceTaintSelector, s conversion.Scope) error {
out.DeviceClassName = (*string)(unsafe.Pointer(in.DeviceClassName))
out.Driver = (*string)(unsafe.Pointer(in.Driver))
out.Pool = (*string)(unsafe.Pointer(in.Pool))
out.Device = (*string)(unsafe.Pointer(in.Device))
out.Selectors = *(*[]resourcev1alpha3.DeviceSelector)(unsafe.Pointer(&in.Selectors))
return nil
}

View file

@ -1331,7 +1331,7 @@ func Validate_DeviceTaint(ctx context.Context, op operation.Operation, fldPath *
return errs
}
var symbolsForDeviceTaintEffect = sets.New(resourcev1beta1.DeviceTaintEffectNoExecute, resourcev1beta1.DeviceTaintEffectNoSchedule)
var symbolsForDeviceTaintEffect = sets.New(resourcev1beta1.DeviceTaintEffectNoExecute, resourcev1beta1.DeviceTaintEffectNoSchedule, resourcev1beta1.DeviceTaintEffectNone)
// Validate_DeviceTaintEffect validates an instance of DeviceTaintEffect according
// to declarative validation rules in the API schema.

View file

@ -1261,7 +1261,7 @@ func Validate_DeviceTaint(ctx context.Context, op operation.Operation, fldPath *
return errs
}
var symbolsForDeviceTaintEffect = sets.New(resourcev1beta2.DeviceTaintEffectNoExecute, resourcev1beta2.DeviceTaintEffectNoSchedule)
var symbolsForDeviceTaintEffect = sets.New(resourcev1beta2.DeviceTaintEffectNoExecute, resourcev1beta2.DeviceTaintEffectNoSchedule, resourcev1beta2.DeviceTaintEffectNone)
// Validate_DeviceTaintEffect validates an instance of DeviceTaintEffect according
// to declarative validation rules in the API schema.

View file

@ -704,9 +704,14 @@ func validateResourceSliceSpec(spec, oldSpec *resource.ResourceSliceSpec, fldPat
}
sharedCounterToCounterNames := gatherSharedCounterCounterNames(spec.SharedCounters)
allErrs = append(allErrs, validateSet(spec.Devices, resource.ResourceSliceMaxDevices,
maxDevices := resource.ResourceSliceMaxDevices
if haveDeviceTaints(spec) {
maxDevices = resource.ResourceSliceMaxDevicesWithTaints
}
allErrs = append(allErrs, validateSet(spec.Devices, maxDevices,
func(device resource.Device, fldPath *field.Path) field.ErrorList {
return validateDevice(device, fldPath, sharedCounterToCounterNames, spec.PerDeviceNodeSelection)
oldDevice := lookupDevice(oldSpec, device.Name)
return validateDevice(device, oldDevice, fldPath, sharedCounterToCounterNames, spec.PerDeviceNodeSelection)
},
func(device resource.Device) string {
return device.Name
@ -740,6 +745,32 @@ func validateResourceSliceSpec(spec, oldSpec *resource.ResourceSliceSpec, fldPat
return allErrs
}
func haveDeviceTaints(spec *resource.ResourceSliceSpec) bool {
if spec == nil {
return false
}
for _, device := range spec.Devices {
if len(device.Taints) > 0 {
return true
}
}
return false
}
func lookupDevice(spec *resource.ResourceSliceSpec, deviceName string) *resource.Device {
if spec == nil {
return nil
}
for i := range spec.Devices {
device := &spec.Devices[i]
if device.Name == deviceName {
return device
}
}
return nil
}
func validateCounterSet(counterSet resource.CounterSet, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList
if counterSet.Name == "" {
@ -782,7 +813,7 @@ func validateResourcePool(pool resource.ResourcePool, fldPath *field.Path) field
return allErrs
}
func validateDevice(device resource.Device, fldPath *field.Path, sharedCounterToCounterNames map[string]sets.Set[string], perDeviceNodeSelection *bool) field.ErrorList {
func validateDevice(device resource.Device, oldDevice *resource.Device, fldPath *field.Path, sharedCounterToCounterNames map[string]sets.Set[string], perDeviceNodeSelection *bool) field.ErrorList {
var allErrs field.ErrorList
allowMultipleAllocations := device.AllowMultipleAllocations != nil && *device.AllowMultipleAllocations
allErrs = append(allErrs, validateDeviceName(device.Name, fldPath.Child("name"))...)
@ -799,7 +830,15 @@ func validateDevice(device resource.Device, fldPath *field.Path, sharedCounterTo
} else {
allErrs = append(allErrs, validateMap(device.Capacity, -1, attributeAndCapacityMaxKeyLength, validateQualifiedName, validateSingleAllocatableDeviceCapacity, fldPath.Child("capacity"))...)
}
allErrs = append(allErrs, validateSlice(device.Taints, resource.DeviceTaintsMaxLength, validateDeviceTaint, fldPath.Child("taints"))...)
// If the entire set is the same as before then validation can be skipped.
// We could also do the DeepEqual on the entire spec, but here it is a bit cheaper.
if oldDevice == nil || !apiequality.Semantic.DeepEqual(oldDevice.Taints, device.Taints) {
allErrs = append(allErrs, validateSlice(device.Taints, resource.DeviceTaintsMaxLength,
func(taint resource.DeviceTaint, fldPath *field.Path) field.ErrorList {
return validateDeviceTaint(taint, nil, fldPath)
},
fldPath.Child("taints"))...)
}
allErrs = append(allErrs, validateSet(device.ConsumesCounters, -1,
validateDeviceCounterConsumption,
@ -1362,7 +1401,11 @@ func validateDeviceTaintRuleSpec(spec, oldSpec *resource.DeviceTaintRuleSpec, fl
oldFilter = oldSpec.DeviceSelector // +k8s:verify-mutation:reason=clone
}
allErrs = append(allErrs, validateDeviceTaintSelector(spec.DeviceSelector, oldFilter, fldPath.Child("deviceSelector"))...)
allErrs = append(allErrs, validateDeviceTaint(spec.Taint, fldPath.Child("taint"))...)
var oldTaint *resource.DeviceTaint
if oldSpec != nil {
oldTaint = &oldSpec.Taint // +k8s:verify-mutation:reason=clone
}
allErrs = append(allErrs, validateDeviceTaint(spec.Taint, oldTaint, fldPath.Child("taint"))...)
return allErrs
}
@ -1372,9 +1415,6 @@ func validateDeviceTaintSelector(filter, oldFilter *resource.DeviceTaintSelector
if filter == nil {
return allErrs
}
if filter.DeviceClassName != nil {
allErrs = append(allErrs, validateDeviceClassName(*filter.DeviceClassName, fldPath.Child("deviceClassName"))...)
}
if filter.Driver != nil {
allErrs = append(allErrs, validateDriverName(*filter.Driver, fldPath.Child("driver"))...)
}
@ -1385,37 +1425,26 @@ func validateDeviceTaintSelector(filter, oldFilter *resource.DeviceTaintSelector
allErrs = append(allErrs, validateDeviceName(*filter.Device, fldPath.Child("device"))...)
}
// If the selectors are exactly as before, we treat the CEL expressions as "stored".
// Any change, including merely reordering selectors, triggers validation as new
// expressions.
stored := false
if oldFilter != nil {
stored = apiequality.Semantic.DeepEqual(filter.Selectors, oldFilter.Selectors)
}
allErrs = append(allErrs, validateSlice(filter.Selectors, resource.DeviceSelectorsMaxSize,
func(selector resource.DeviceSelector, fldPath *field.Path) field.ErrorList {
return validateSelector(selector, fldPath, stored)
},
fldPath.Child("selectors"))...)
return allErrs
}
var validDeviceTolerationOperators = []resource.DeviceTolerationOperator{resource.DeviceTolerationOpEqual, resource.DeviceTolerationOpExists}
var validDeviceTaintEffects = sets.New(resource.DeviceTaintEffectNoSchedule, resource.DeviceTaintEffectNoExecute)
var validDeviceTaintEffects = sets.New(resource.DeviceTaintEffectNoSchedule, resource.DeviceTaintEffectNoExecute, resource.DeviceTaintEffectNone)
func validateDeviceTaint(taint resource.DeviceTaint, fldPath *field.Path) field.ErrorList {
func validateDeviceTaint(taint resource.DeviceTaint, oldTaint *resource.DeviceTaint, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList
allErrs = append(allErrs, metav1validation.ValidateLabelName(taint.Key, fldPath.Child("key"))...) // Includes checking for non-empty.
if taint.Value != "" {
allErrs = append(allErrs, validateLabelValue(taint.Value, fldPath.Child("value"))...)
}
switch {
case taint.Effect == "":
allErrs = append(allErrs, field.Required(fldPath.Child("effect"), "").MarkCoveredByDeclarative()) // Required in a taint.
case !validDeviceTaintEffects.Has(taint.Effect):
allErrs = append(allErrs, field.NotSupported(fldPath.Child("effect"), taint.Effect, sets.List(validDeviceTaintEffects)).MarkCoveredByDeclarative())
if oldTaint == nil || oldTaint.Effect != taint.Effect {
switch {
case taint.Effect == "":
allErrs = append(allErrs, field.Required(fldPath.Child("effect"), "").MarkCoveredByDeclarative()) // Required in a taint.
case !validDeviceTaintEffects.Has(taint.Effect):
allErrs = append(allErrs, field.NotSupported(fldPath.Child("effect"), taint.Effect, sets.List(validDeviceTaintEffects)).MarkCoveredByDeclarative())
}
}
return allErrs
@ -1498,3 +1527,17 @@ func validateDeviceBindingParameters(bindingConditions, bindingFailureConditions
return allErrs
}
// ValidateDeviceTaintRuleStatusUpdate tests if a DeviceTaintRule status update is valid.
func ValidateDeviceTaintRuleStatusUpdate(rule, oldRule *resource.DeviceTaintRule) field.ErrorList {
var allErrs field.ErrorList
fldPath := field.NewPath("status")
allErrs = corevalidation.ValidateObjectMetaUpdate(&rule.ObjectMeta, &oldRule.ObjectMeta, field.NewPath("metadata")) // Covers invalid name changes.
allErrs = append(allErrs, metav1validation.ValidateConditions(rule.Status.Conditions, fldPath.Child("conditions"))...)
if len(rule.Status.Conditions) > resource.DeviceTaintRuleStatusMaxConditions {
allErrs = append(allErrs, field.TooMany(fldPath.Child("conditions"), len(rule.Status.Conditions), resource.DeviceTaintRuleStatusMaxConditions))
}
return allErrs
}

View file

@ -17,7 +17,6 @@ limitations under the License.
package validation
import (
"strings"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -37,10 +36,9 @@ func testDeviceTaintRule(name string, spec resourceapi.DeviceTaintRuleSpec) *res
var validDeviceTaintRuleSpec = resourceapi.DeviceTaintRuleSpec{
DeviceSelector: &resourceapi.DeviceTaintSelector{
DeviceClassName: ptr.To(goodName),
Driver: ptr.To("test.example.com"),
Pool: ptr.To(goodName),
Device: ptr.To(goodName),
Driver: ptr.To("test.example.com"),
Pool: ptr.To(goodName),
Device: ptr.To(goodName),
},
Taint: resourceapi.DeviceTaint{
Key: "example.com/taint",
@ -187,14 +185,6 @@ func TestValidateDeviceTaint(t *testing.T) {
return taintRule
}(),
},
"bad-class": {
wantFailures: field.ErrorList{field.Invalid(field.NewPath("spec", "deviceSelector", "deviceClassName"), badName, "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')")},
taintRule: func() *resourceapi.DeviceTaintRule {
taintRule := testDeviceTaintRule(goodName, validDeviceTaintRuleSpec)
taintRule.Spec.DeviceSelector.DeviceClassName = ptr.To(badName)
return taintRule
}(),
},
"bad-driver": {
wantFailures: field.ErrorList{field.Invalid(field.NewPath("spec", "deviceSelector", "driver"), badName, "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')")},
taintRule: func() *resourceapi.DeviceTaintRule {
@ -219,70 +209,7 @@ func TestValidateDeviceTaint(t *testing.T) {
return taintRule
}(),
},
"CEL-compile-errors": {
wantFailures: field.ErrorList{
field.Invalid(field.NewPath("spec", "deviceSelector", "selectors").Index(1).Child("cel", "expression"), `device.attributes[true].someBoolean`, "compilation failed: ERROR: <input>:1:18: found no matching overload for '_[_]' applied to '(map(string, map(string, any)), bool)'\n | device.attributes[true].someBoolean\n | .................^"),
},
taintRule: func() *resourceapi.DeviceTaintRule {
taintRule := testDeviceTaintRule(goodName, validDeviceTaintRuleSpec)
taintRule.Spec.DeviceSelector.Selectors = []resourceapi.DeviceSelector{
{
// Good selector.
CEL: &resourceapi.CELDeviceSelector{
Expression: `device.driver == "dra.example.com"`,
},
},
{
// Bad selector.
CEL: &resourceapi.CELDeviceSelector{
Expression: `device.attributes[true].someBoolean`,
},
},
}
return taintRule
}(),
},
"CEL-length": {
wantFailures: field.ErrorList{
field.TooLong(field.NewPath("spec", "deviceSelector", "selectors").Index(1).Child("cel", "expression"), "" /*unused*/, resourceapi.CELSelectorExpressionMaxLength),
},
taintRule: func() *resourceapi.DeviceTaintRule {
taintRule := testDeviceTaintRule(goodName, validDeviceTaintRuleSpec)
expression := `device.driver == ""`
taintRule.Spec.DeviceSelector.Selectors = []resourceapi.DeviceSelector{
{
// Good selector.
CEL: &resourceapi.CELDeviceSelector{
Expression: strings.ReplaceAll(expression, `""`, `"`+strings.Repeat("x", resourceapi.CELSelectorExpressionMaxLength-len(expression))+`"`),
},
},
{
// Too long by one selector.
CEL: &resourceapi.CELDeviceSelector{
Expression: strings.ReplaceAll(expression, `""`, `"`+strings.Repeat("x", resourceapi.CELSelectorExpressionMaxLength-len(expression)+1)+`"`),
},
},
}
return taintRule
}(),
},
"CEL-cost": {
wantFailures: field.ErrorList{
field.Forbidden(field.NewPath("spec", "deviceSelector", "selectors").Index(0).Child("cel", "expression"), "too complex, exceeds cost limit"),
},
taintRule: func() *resourceapi.DeviceTaintRule {
claim := testDeviceTaintRule(goodName, validDeviceTaintRuleSpec)
claim.Spec.DeviceSelector.Selectors = []resourceapi.DeviceSelector{
{
CEL: &resourceapi.CELDeviceSelector{
// From https://github.com/kubernetes/kubernetes/blob/50fc400f178d2078d0ca46aee955ee26375fc437/test/integration/apiserver/cel/validatingadmissionpolicy_test.go#L2150.
Expression: `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(x, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(y, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z5, int('1'.find('[0-9]*')) < 100)))))))`,
},
},
}
return claim
}(),
},
// Minimal tests for DeviceTaint. Full coverage of validateDeviceTaint is in ResourceSlice test.
"valid-taint": {
taintRule: func() *resourceapi.DeviceTaintRule {
claim := testDeviceTaintRule(goodName, validDeviceTaintRuleSpec)
@ -294,20 +221,33 @@ func TestValidateDeviceTaint(t *testing.T) {
return claim
}(),
},
"invalid-taint": {
"required-taint": {
wantFailures: field.ErrorList{
field.Required(field.NewPath("spec", "taint", "effect"), "").MarkCoveredByDeclarative(),
},
taintRule: func() *resourceapi.DeviceTaintRule {
claim := testDeviceTaintRule(goodName, validDeviceTaintRuleSpec)
claim.Spec.Taint = resourceapi.DeviceTaint{
// Minimal test. Full coverage of validateDeviceTaint is in ResourceSlice test.
Key: goodName,
Value: goodName,
}
return claim
}(),
},
"invalid-taint": {
wantFailures: field.ErrorList{
field.NotSupported(field.NewPath("spec", "taint", "effect"), resourceapi.DeviceTaintEffect("some-other-effect"), []resourceapi.DeviceTaintEffect{resourceapi.DeviceTaintEffectNoExecute, resourceapi.DeviceTaintEffectNoSchedule, resourceapi.DeviceTaintEffectNone}).MarkCoveredByDeclarative(),
},
taintRule: func() *resourceapi.DeviceTaintRule {
claim := testDeviceTaintRule(goodName, validDeviceTaintRuleSpec)
claim.Spec.Taint = resourceapi.DeviceTaint{
Effect: "some-other-effect",
Key: goodName,
Value: goodName,
}
return claim
}(),
},
}
for name, scenario := range scenarios {
@ -321,6 +261,8 @@ func TestValidateDeviceTaint(t *testing.T) {
func TestValidateDeviceTaintUpdate(t *testing.T) {
name := "valid"
validTaintRule := testDeviceTaintRule(name, validDeviceTaintRuleSpec)
invalidTaintEffectRule := validTaintRule.DeepCopy()
invalidTaintEffectRule.Spec.Taint.Effect = "some-other-effect"
scenarios := map[string]struct {
old *resourceapi.DeviceTaintRule
@ -339,6 +281,21 @@ func TestValidateDeviceTaintUpdate(t *testing.T) {
return taintRule
},
},
"valid-existing-unknown-effect": {
old: invalidTaintEffectRule,
update: func(taintRule *resourceapi.DeviceTaintRule) *resourceapi.DeviceTaintRule {
taintRule.Labels = map[string]string{"a": "b"}
return taintRule
},
},
"invalid-new-unknown-effect": {
wantFailures: field.ErrorList{field.NotSupported(field.NewPath("spec", "taint", "effect"), resourceapi.DeviceTaintEffect("some-other-effect"), []resourceapi.DeviceTaintEffect{resourceapi.DeviceTaintEffectNoExecute, resourceapi.DeviceTaintEffectNoSchedule, resourceapi.DeviceTaintEffectNone})}.MarkCoveredByDeclarative(),
old: validTaintRule,
update: func(taintRule *resourceapi.DeviceTaintRule) *resourceapi.DeviceTaintRule {
taintRule.Spec.Taint.Effect = "some-other-effect"
return taintRule
},
},
}
for name, scenario := range scenarios {

View file

@ -824,7 +824,7 @@ func TestValidateClaim(t *testing.T) {
field.Invalid(fldPath.Index(5).Child("key"), badName, "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')").MarkCoveredByDeclarative(),
field.Invalid(fldPath.Index(5).Child("value"), badName, "a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyValue', or 'my_value', or '12345', regex used for validation is '(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?')"),
field.NotSupported(fldPath.Index(5).Child("effect"), resource.DeviceTaintEffect("some-other-effect"), []resource.DeviceTaintEffect{resource.DeviceTaintEffectNoExecute, resource.DeviceTaintEffectNoSchedule}).MarkCoveredByDeclarative(),
field.NotSupported(fldPath.Index(5).Child("effect"), resource.DeviceTaintEffect("some-other-effect"), []resource.DeviceTaintEffect{resource.DeviceTaintEffectNoExecute, resource.DeviceTaintEffectNoSchedule, resource.DeviceTaintEffectNone}).MarkCoveredByDeclarative(),
)
return allErrs
}(),

View file

@ -108,6 +108,25 @@ func TestValidateResourceSlice(t *testing.T) {
wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevices+1, resourceapi.ResourceSliceMaxDevices)},
slice: testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevices+1),
},
"good-taints": {
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaints)
for i := range slice.Spec.Devices {
slice.Spec.Devices[i].Taints = []resourceapi.DeviceTaint{{Key: "example.com/taint", Effect: resourceapi.DeviceTaintEffectNoExecute}}
}
return slice
}(),
},
"too-large-taints": {
wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevicesWithTaints+1, resourceapi.ResourceSliceMaxDevicesWithTaints)},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaints+1)
for i := range slice.Spec.Devices {
slice.Spec.Devices[i].Taints = []resourceapi.DeviceTaint{{Key: "example.com/taint", Effect: resourceapi.DeviceTaintEffectNoExecute}}
}
return slice
}(),
},
"missing-name": {
wantFailures: field.ErrorList{field.Required(field.NewPath("metadata", "name"), "name or generateName is required")},
slice: testResourceSlice("", goodName, driverName, 1),
@ -497,7 +516,7 @@ func TestValidateResourceSlice(t *testing.T) {
field.Invalid(fldPath.Index(3).Child("key"), badName, "name part must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyName', or 'my.name', or '123-abc', regex used for validation is '([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]')"),
field.Invalid(fldPath.Index(3).Child("value"), badName, "a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character (e.g. 'MyValue', or 'my_value', or '12345', regex used for validation is '(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?')"),
field.NotSupported(fldPath.Index(3).Child("effect"), resourceapi.DeviceTaintEffect("some-other-op"), []resourceapi.DeviceTaintEffect{resourceapi.DeviceTaintEffectNoExecute, resourceapi.DeviceTaintEffectNoSchedule}).MarkCoveredByDeclarative(),
field.NotSupported(fldPath.Index(3).Child("effect"), resourceapi.DeviceTaintEffect("some-other-effect"), []resourceapi.DeviceTaintEffect{resourceapi.DeviceTaintEffectNoExecute, resourceapi.DeviceTaintEffectNoSchedule, resourceapi.DeviceTaintEffectNone}).MarkCoveredByDeclarative(),
}
}(),
slice: func() *resourceapi.ResourceSlice {
@ -522,7 +541,7 @@ func TestValidateResourceSlice(t *testing.T) {
// Invalid strings.
Key: badName,
Value: badName,
Effect: "some-other-op",
Effect: "some-other-effect",
},
}
return slice
@ -867,6 +886,17 @@ func TestValidateResourceSlice(t *testing.T) {
func TestValidateResourceSliceUpdate(t *testing.T) {
name := "valid"
validResourceSlice := testResourceSlice(name, name, name, 1)
invalidResourceSliceWithTaints := validResourceSlice.DeepCopy()
invalidResourceSliceWithTaints.Spec.Devices[0].Taints = []resourceapi.DeviceTaint{
{
Key: "unhealthy-power",
Effect: resourceapi.DeviceTaintEffectNoExecute,
},
{
Key: "unhealthy-mem",
Effect: "some-other-effect",
},
}
scenarios := map[string]struct {
oldResourceSlice *resourceapi.ResourceSlice
@ -938,6 +968,31 @@ func TestValidateResourceSliceUpdate(t *testing.T) {
return slice
},
},
"invalid-new-effect-in-old-device": {
wantFailures: field.ErrorList{field.NotSupported(field.NewPath("spec", "devices").Index(0).Child("taints").Index(1).Child("effect"), resourceapi.DeviceTaintEffect("some-other-effect"), []resourceapi.DeviceTaintEffect{resourceapi.DeviceTaintEffectNoExecute, resourceapi.DeviceTaintEffectNoSchedule, resourceapi.DeviceTaintEffectNone})}.MarkCoveredByDeclarative(),
oldResourceSlice: validResourceSlice,
update: func(slice *resourceapi.ResourceSlice) *resourceapi.ResourceSlice {
slice.Spec.Devices[0].Taints = invalidResourceSliceWithTaints.Spec.Devices[0].Taints
return slice
},
},
"valid-old-effect": {
oldResourceSlice: invalidResourceSliceWithTaints,
update: func(slice *resourceapi.ResourceSlice) *resourceapi.ResourceSlice {
slice.Spec.Devices[0].Attributes["foo"] = resourceapi.DeviceAttribute{StringValue: ptr.To("bar")}
return slice
},
},
"invalid-new-effect-in-new-device": {
wantFailures: field.ErrorList{field.NotSupported(field.NewPath("spec", "devices").Index(1).Child("taints").Index(1).Child("effect"), resourceapi.DeviceTaintEffect("some-other-effect"), []resourceapi.DeviceTaintEffect{resourceapi.DeviceTaintEffectNoExecute, resourceapi.DeviceTaintEffectNoSchedule, resourceapi.DeviceTaintEffectNone})}.MarkCoveredByDeclarative(),
oldResourceSlice: invalidResourceSliceWithTaints,
update: func(slice *resourceapi.ResourceSlice) *resourceapi.ResourceSlice {
device := slice.Spec.Devices[0].DeepCopy()
device.Name += "-other"
slice.Spec.Devices = append(slice.Spec.Devices, *device)
return slice
},
},
}
for name, scenario := range scenarios {

View file

@ -831,6 +831,7 @@ func (in *DeviceTaintRule) DeepCopyInto(out *DeviceTaintRule) {
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
@ -907,14 +908,32 @@ func (in *DeviceTaintRuleSpec) DeepCopy() *DeviceTaintRuleSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeviceTaintRuleStatus) DeepCopyInto(out *DeviceTaintRuleStatus) {
*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])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceTaintRuleStatus.
func (in *DeviceTaintRuleStatus) DeepCopy() *DeviceTaintRuleStatus {
if in == nil {
return nil
}
out := new(DeviceTaintRuleStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeviceTaintSelector) DeepCopyInto(out *DeviceTaintSelector) {
*out = *in
if in.DeviceClassName != nil {
in, out := &in.DeviceClassName, &out.DeviceClassName
*out = new(string)
**out = **in
}
if in.Driver != nil {
in, out := &in.Driver, &out.Driver
*out = new(string)
@ -930,13 +949,6 @@ func (in *DeviceTaintSelector) DeepCopyInto(out *DeviceTaintSelector) {
*out = new(string)
**out = **in
}
if in.Selectors != nil {
in, out := &in.Selectors, &out.Selectors
*out = make([]DeviceSelector, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}

View file

@ -25,6 +25,7 @@ import (
cronjobconfig "k8s.io/kubernetes/pkg/controller/cronjob/config"
daemonconfig "k8s.io/kubernetes/pkg/controller/daemon/config"
deploymentconfig "k8s.io/kubernetes/pkg/controller/deployment/config"
devicetaintevictionconfig "k8s.io/kubernetes/pkg/controller/devicetainteviction/config"
endpointconfig "k8s.io/kubernetes/pkg/controller/endpoint/config"
endpointsliceconfig "k8s.io/kubernetes/pkg/controller/endpointslice/config"
endpointslicemirroringconfig "k8s.io/kubernetes/pkg/controller/endpointslicemirroring/config"
@ -62,6 +63,9 @@ type KubeControllerManagerConfiguration struct {
// AttachDetachControllerConfiguration holds configuration for
// AttachDetachController related features.
AttachDetachController attachdetachconfig.AttachDetachControllerConfiguration
// CronJobControllerConfiguration holds configuration for CronJobController
// related features.
CronJobController cronjobconfig.CronJobControllerConfiguration
// CSRSigningControllerConfiguration holds configuration for
// CSRSigningController related features.
CSRSigningController csrsigningconfig.CSRSigningControllerConfiguration
@ -71,9 +75,8 @@ type KubeControllerManagerConfiguration struct {
// DeploymentControllerConfiguration holds configuration for
// DeploymentController related features.
DeploymentController deploymentconfig.DeploymentControllerConfiguration
// StatefulSetControllerConfiguration holds configuration for
// StatefulSetController related features.
StatefulSetController statefulsetconfig.StatefulSetControllerConfiguration
// DeviceTaintEvictionControllerConfiguration contains elements configuring the device taint eviction controller.
DeviceTaintEvictionController devicetaintevictionconfig.DeviceTaintEvictionControllerConfiguration
// DeprecatedControllerConfiguration holds configuration for some deprecated
// features.
DeprecatedController DeprecatedControllerConfiguration
@ -96,9 +99,6 @@ type KubeControllerManagerConfiguration struct {
HPAController poautosclerconfig.HPAControllerConfiguration
// JobControllerConfiguration holds configuration for JobController related features.
JobController jobconfig.JobControllerConfiguration
// CronJobControllerConfiguration holds configuration for CronJobController
// related features.
CronJobController cronjobconfig.CronJobControllerConfiguration
// LegacySATokenCleanerConfiguration holds configuration for LegacySATokenCleaner related features.
LegacySATokenCleaner serviceaccountconfig.LegacySATokenCleanerConfiguration
// NamespaceControllerConfiguration holds configuration for NamespaceController
@ -130,6 +130,9 @@ type KubeControllerManagerConfiguration struct {
// ServiceControllerConfiguration holds configuration for ServiceController
// related features.
ServiceController serviceconfig.ServiceControllerConfiguration
// StatefulSetControllerConfiguration holds configuration for
// StatefulSetController related features.
StatefulSetController statefulsetconfig.StatefulSetControllerConfiguration
// TTLAfterFinishedControllerConfiguration holds configuration for
// TTLAfterFinishedController related features.
TTLAfterFinishedController ttlafterfinishedconfig.TTLAfterFinishedControllerConfiguration

View file

@ -25,6 +25,7 @@ import (
cronjobconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/cronjob/config/v1alpha1"
daemonconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/daemon/config/v1alpha1"
deploymentconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/deployment/config/v1alpha1"
devicetaintevictionconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/devicetainteviction/config/v1alpha1"
endpointconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/endpoint/config/v1alpha1"
endpointsliceconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/endpointslice/config/v1alpha1"
endpointslicemirroringconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/endpointslicemirroring/config/v1alpha1"
@ -71,6 +72,8 @@ func SetDefaults_KubeControllerManagerConfiguration(obj *kubectrlmgrconfigv1alph
daemonconfigv1alpha1.RecommendedDefaultDaemonSetControllerConfiguration(&obj.DaemonSetController)
// Use the default RecommendedDefaultDeploymentControllerConfiguration options
deploymentconfigv1alpha1.RecommendedDefaultDeploymentControllerConfiguration(&obj.DeploymentController)
// Use the default RecommendedDefaultDeviceTaintEvictionControllerConfiguration options
devicetaintevictionconfigv1alpha1.RecommendedDefaultDeviceTaintEvictionControllerConfiguration(&obj.DeviceTaintEvictionController)
// Use the default RecommendedDefaultStatefulSetControllerConfiguration options
statefulsetconfigv1alpha1.RecommendedDefaultStatefulSetControllerConfiguration(&obj.StatefulSetController)
// Use the default RecommendedDefaultEndpointControllerConfiguration options

View file

@ -34,6 +34,7 @@ import (
cronjobconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/cronjob/config/v1alpha1"
daemonconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/daemon/config/v1alpha1"
deploymentconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/deployment/config/v1alpha1"
devicetaintevictionconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/devicetainteviction/config/v1alpha1"
endpointconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/endpoint/config/v1alpha1"
endpointsliceconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/endpointslice/config/v1alpha1"
endpointslicemirroringconfigv1alpha1 "k8s.io/kubernetes/pkg/controller/endpointslicemirroring/config/v1alpha1"
@ -224,6 +225,9 @@ func autoConvert_v1alpha1_KubeControllerManagerConfiguration_To_config_KubeContr
if err := validatingadmissionpolicystatusconfigv1alpha1.Convert_v1alpha1_ValidatingAdmissionPolicyStatusControllerConfiguration_To_config_ValidatingAdmissionPolicyStatusControllerConfiguration(&in.ValidatingAdmissionPolicyStatusController, &out.ValidatingAdmissionPolicyStatusController, s); err != nil {
return err
}
if err := devicetaintevictionconfigv1alpha1.Convert_v1alpha1_DeviceTaintEvictionControllerConfiguration_To_config_DeviceTaintEvictionControllerConfiguration(&in.DeviceTaintEvictionController, &out.DeviceTaintEvictionController, s); err != nil {
return err
}
return nil
}
@ -242,6 +246,9 @@ func autoConvert_config_KubeControllerManagerConfiguration_To_v1alpha1_KubeContr
if err := attachdetachconfigv1alpha1.Convert_config_AttachDetachControllerConfiguration_To_v1alpha1_AttachDetachControllerConfiguration(&in.AttachDetachController, &out.AttachDetachController, s); err != nil {
return err
}
if err := cronjobconfigv1alpha1.Convert_config_CronJobControllerConfiguration_To_v1alpha1_CronJobControllerConfiguration(&in.CronJobController, &out.CronJobController, s); err != nil {
return err
}
if err := signerconfigv1alpha1.Convert_config_CSRSigningControllerConfiguration_To_v1alpha1_CSRSigningControllerConfiguration(&in.CSRSigningController, &out.CSRSigningController, s); err != nil {
return err
}
@ -251,7 +258,7 @@ func autoConvert_config_KubeControllerManagerConfiguration_To_v1alpha1_KubeContr
if err := deploymentconfigv1alpha1.Convert_config_DeploymentControllerConfiguration_To_v1alpha1_DeploymentControllerConfiguration(&in.DeploymentController, &out.DeploymentController, s); err != nil {
return err
}
if err := statefulsetconfigv1alpha1.Convert_config_StatefulSetControllerConfiguration_To_v1alpha1_StatefulSetControllerConfiguration(&in.StatefulSetController, &out.StatefulSetController, s); err != nil {
if err := devicetaintevictionconfigv1alpha1.Convert_config_DeviceTaintEvictionControllerConfiguration_To_v1alpha1_DeviceTaintEvictionControllerConfiguration(&in.DeviceTaintEvictionController, &out.DeviceTaintEvictionController, s); err != nil {
return err
}
if err := Convert_config_DeprecatedControllerConfiguration_To_v1alpha1_DeprecatedControllerConfiguration(&in.DeprecatedController, &out.DeprecatedController, s); err != nil {
@ -278,9 +285,6 @@ func autoConvert_config_KubeControllerManagerConfiguration_To_v1alpha1_KubeContr
if err := jobconfigv1alpha1.Convert_config_JobControllerConfiguration_To_v1alpha1_JobControllerConfiguration(&in.JobController, &out.JobController, s); err != nil {
return err
}
if err := cronjobconfigv1alpha1.Convert_config_CronJobControllerConfiguration_To_v1alpha1_CronJobControllerConfiguration(&in.CronJobController, &out.CronJobController, s); err != nil {
return err
}
if err := serviceaccountconfigv1alpha1.Convert_config_LegacySATokenCleanerConfiguration_To_v1alpha1_LegacySATokenCleanerConfiguration(&in.LegacySATokenCleaner, &out.LegacySATokenCleaner, s); err != nil {
return err
}
@ -314,6 +318,9 @@ func autoConvert_config_KubeControllerManagerConfiguration_To_v1alpha1_KubeContr
if err := serviceconfigv1alpha1.Convert_config_ServiceControllerConfiguration_To_v1alpha1_ServiceControllerConfiguration(&in.ServiceController, &out.ServiceController, s); err != nil {
return err
}
if err := statefulsetconfigv1alpha1.Convert_config_StatefulSetControllerConfiguration_To_v1alpha1_StatefulSetControllerConfiguration(&in.StatefulSetController, &out.StatefulSetController, s); err != nil {
return err
}
if err := ttlafterfinishedconfigv1alpha1.Convert_config_TTLAfterFinishedControllerConfiguration_To_v1alpha1_TTLAfterFinishedControllerConfiguration(&in.TTLAfterFinishedController, &out.TTLAfterFinishedController, s); err != nil {
return err
}

View file

@ -48,10 +48,11 @@ func (in *KubeControllerManagerConfiguration) DeepCopyInto(out *KubeControllerMa
in.Generic.DeepCopyInto(&out.Generic)
out.KubeCloudShared = in.KubeCloudShared
out.AttachDetachController = in.AttachDetachController
out.CronJobController = in.CronJobController
out.CSRSigningController = in.CSRSigningController
out.DaemonSetController = in.DaemonSetController
out.DeploymentController = in.DeploymentController
out.StatefulSetController = in.StatefulSetController
out.DeviceTaintEvictionController = in.DeviceTaintEvictionController
out.DeprecatedController = in.DeprecatedController
out.EndpointController = in.EndpointController
out.EndpointSliceController = in.EndpointSliceController
@ -60,7 +61,6 @@ func (in *KubeControllerManagerConfiguration) DeepCopyInto(out *KubeControllerMa
in.GarbageCollectorController.DeepCopyInto(&out.GarbageCollectorController)
out.HPAController = in.HPAController
out.JobController = in.JobController
out.CronJobController = in.CronJobController
out.LegacySATokenCleaner = in.LegacySATokenCleaner
out.NamespaceController = in.NamespaceController
out.NodeIPAMController = in.NodeIPAMController
@ -72,6 +72,7 @@ func (in *KubeControllerManagerConfiguration) DeepCopyInto(out *KubeControllerMa
out.ResourceQuotaController = in.ResourceQuotaController
out.SAController = in.SAController
out.ServiceController = in.ServiceController
out.StatefulSetController = in.StatefulSetController
out.TTLAfterFinishedController = in.TTLAfterFinishedController
out.ValidatingAdmissionPolicyStatusController = in.ValidatingAdmissionPolicyStatusController
return

View file

@ -0,0 +1,19 @@
/*
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.
*/
// +k8s:deepcopy-gen=package
package config

View file

@ -0,0 +1,26 @@
/*
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.
*/
package config
// DeviceTaintEvictionControllerConfiguration contains elements configuring the device taint eviction controller.
type DeviceTaintEvictionControllerConfiguration struct {
// ConcurrentSyncs is the number of operations (deleting a pod, updating a ResourcClaim status, etc.)
// that will be done concurrently. Larger number = processing, but more CPU (and network) load.
//
// The default is 10.
ConcurrentSyncs int32
}

View file

@ -0,0 +1,40 @@
/*
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.
*/
package v1alpha1
import (
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/kube-controller-manager/config/v1alpha1"
"k8s.io/kubernetes/pkg/controller/devicetainteviction/config"
)
// Important! The public back-and-forth conversion functions for the types in this package
// with DeviceTaintEvictionControllerConfiguration types need to be manually exposed like this in order for
// other packages that reference this package to be able to call these conversion functions
// in an autogenerated manner.
// TODO: Fix the bug in conversion-gen so it automatically discovers these Convert_* functions
// in autogenerated code as well.
// Convert_v1alpha1_DeviceTaintEvictionControllerConfiguration_To_config_DeviceTaintEvictionControllerConfiguration is an autogenerated conversion function.
func Convert_v1alpha1_DeviceTaintEvictionControllerConfiguration_To_config_DeviceTaintEvictionControllerConfiguration(in *v1alpha1.DeviceTaintEvictionControllerConfiguration, out *config.DeviceTaintEvictionControllerConfiguration, s conversion.Scope) error {
return autoConvert_v1alpha1_DeviceTaintEvictionControllerConfiguration_To_config_DeviceTaintEvictionControllerConfiguration(in, out, s)
}
// Convert_config_DeviceTaintEvictionControllerConfiguration_To_v1alpha1_DeviceTaintEvictionControllerConfiguration is an autogenerated conversion function.
func Convert_config_DeviceTaintEvictionControllerConfiguration_To_v1alpha1_DeviceTaintEvictionControllerConfiguration(in *config.DeviceTaintEvictionControllerConfiguration, out *v1alpha1.DeviceTaintEvictionControllerConfiguration, s conversion.Scope) error {
return autoConvert_config_DeviceTaintEvictionControllerConfiguration_To_v1alpha1_DeviceTaintEvictionControllerConfiguration(in, out, s)
}

View file

@ -0,0 +1,41 @@
/*
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.
*/
package v1alpha1
import (
kubectrlmgrconfigv1alpha1 "k8s.io/kube-controller-manager/config/v1alpha1"
)
// RecommendedDefaultDeviceTaintEvictionControllerConfiguration defaults a pointer to a
// DeviceTaintEvictionControllerConfiguration struct. This will set the recommended default
// values, but they may be subject to change between API versions. This function
// is intentionally not registered in the scheme as a "normal" `SetDefaults_Foo`
// function to allow consumers of this type to set whatever defaults for their
// embedded configs. Forcing consumers to use these defaults would be problematic
// as defaulting in the scheme is done as part of the conversion, and there would
// be no easy way to opt-out. Instead, if you want to use this defaulting method
// run it in your wrapper struct of this type in its `SetDefaults_` method.
func RecommendedDefaultDeviceTaintEvictionControllerConfiguration(obj *kubectrlmgrconfigv1alpha1.DeviceTaintEvictionControllerConfiguration) {
if obj.ConcurrentSyncs == 0 {
// This is a compromise between getting work done and not overwhelming the apiserver
// and pod informers. Integration testing with 100 workers modified pods so quickly
// that a watch in the integration test couldn't keep up:
// cacher.go:855] cacher (pods): 100 objects queued in incoming channel.
// cache_watcher.go:203] Forcing pods watcher close due to unresponsiveness: key: "/pods/", labels: "", fields: "". len(c.input) = 10, len(c.result) = 10, graceful = false
obj.ConcurrentSyncs = 8
}
}

View file

@ -0,0 +1,31 @@
/*
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.
*/
package v1alpha1
import (
"testing"
kubectrlmgrconfigv1alpha1 "k8s.io/kube-controller-manager/config/v1alpha1"
)
func TestRecommendedDefaultDeviceTaintEvictionControllerConfiguration(t *testing.T) {
config := new(kubectrlmgrconfigv1alpha1.DeviceTaintEvictionControllerConfiguration)
RecommendedDefaultDeviceTaintEvictionControllerConfiguration(config)
if config.ConcurrentSyncs != 8 {
t.Errorf("incorrect default value, expected 8 but got %v", config.ConcurrentSyncs)
}
}

View file

@ -0,0 +1,21 @@
/*
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.
*/
// +k8s:deepcopy-gen=package
// +k8s:conversion-gen=k8s.io/kubernetes/pkg/controller/devicetainteviction/config
// +k8s:conversion-gen-external-types=k8s.io/kube-controller-manager/config/v1alpha1
package v1alpha1

View file

@ -0,0 +1,31 @@
/*
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.
*/
package v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime"
)
var (
// SchemeBuilder is the scheme builder with scheme init functions to run for this API package
SchemeBuilder runtime.SchemeBuilder
// localSchemeBuilder extends the SchemeBuilder instance with the external types. In this package,
// defaulting and conversion init funcs are registered as well.
localSchemeBuilder = &SchemeBuilder
// AddToScheme is a global function that registers this API group & version to a scheme
AddToScheme = localSchemeBuilder.AddToScheme
)

View file

@ -0,0 +1,92 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
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 conversion-gen. DO NOT EDIT.
package v1alpha1
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
configv1alpha1 "k8s.io/kube-controller-manager/config/v1alpha1"
config "k8s.io/kubernetes/pkg/controller/devicetainteviction/config"
)
func init() {
localSchemeBuilder.Register(RegisterConversions)
}
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*configv1alpha1.GroupResource)(nil), (*v1.GroupResource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_GroupResource_To_v1_GroupResource(a.(*configv1alpha1.GroupResource), b.(*v1.GroupResource), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1.GroupResource)(nil), (*configv1alpha1.GroupResource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_GroupResource_To_v1alpha1_GroupResource(a.(*v1.GroupResource), b.(*configv1alpha1.GroupResource), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*config.DeviceTaintEvictionControllerConfiguration)(nil), (*configv1alpha1.DeviceTaintEvictionControllerConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_config_DeviceTaintEvictionControllerConfiguration_To_v1alpha1_DeviceTaintEvictionControllerConfiguration(a.(*config.DeviceTaintEvictionControllerConfiguration), b.(*configv1alpha1.DeviceTaintEvictionControllerConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*configv1alpha1.DeviceTaintEvictionControllerConfiguration)(nil), (*config.DeviceTaintEvictionControllerConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_DeviceTaintEvictionControllerConfiguration_To_config_DeviceTaintEvictionControllerConfiguration(a.(*configv1alpha1.DeviceTaintEvictionControllerConfiguration), b.(*config.DeviceTaintEvictionControllerConfiguration), scope)
}); err != nil {
return err
}
return nil
}
func autoConvert_v1alpha1_DeviceTaintEvictionControllerConfiguration_To_config_DeviceTaintEvictionControllerConfiguration(in *configv1alpha1.DeviceTaintEvictionControllerConfiguration, out *config.DeviceTaintEvictionControllerConfiguration, s conversion.Scope) error {
out.ConcurrentSyncs = in.ConcurrentSyncs
return nil
}
func autoConvert_config_DeviceTaintEvictionControllerConfiguration_To_v1alpha1_DeviceTaintEvictionControllerConfiguration(in *config.DeviceTaintEvictionControllerConfiguration, out *configv1alpha1.DeviceTaintEvictionControllerConfiguration, s conversion.Scope) error {
out.ConcurrentSyncs = in.ConcurrentSyncs
return nil
}
func autoConvert_v1alpha1_GroupResource_To_v1_GroupResource(in *configv1alpha1.GroupResource, out *v1.GroupResource, s conversion.Scope) error {
out.Group = in.Group
out.Resource = in.Resource
return nil
}
// Convert_v1alpha1_GroupResource_To_v1_GroupResource is an autogenerated conversion function.
func Convert_v1alpha1_GroupResource_To_v1_GroupResource(in *configv1alpha1.GroupResource, out *v1.GroupResource, s conversion.Scope) error {
return autoConvert_v1alpha1_GroupResource_To_v1_GroupResource(in, out, s)
}
func autoConvert_v1_GroupResource_To_v1alpha1_GroupResource(in *v1.GroupResource, out *configv1alpha1.GroupResource, s conversion.Scope) error {
out.Group = in.Group
out.Resource = in.Resource
return nil
}
// Convert_v1_GroupResource_To_v1alpha1_GroupResource is an autogenerated conversion function.
func Convert_v1_GroupResource_To_v1alpha1_GroupResource(in *v1.GroupResource, out *configv1alpha1.GroupResource, s conversion.Scope) error {
return autoConvert_v1_GroupResource_To_v1alpha1_GroupResource(in, out, s)
}

View file

@ -0,0 +1,22 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
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 deepcopy-gen. DO NOT EDIT.
package v1alpha1

View file

@ -0,0 +1,38 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
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 deepcopy-gen. DO NOT EDIT.
package config
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeviceTaintEvictionControllerConfiguration) DeepCopyInto(out *DeviceTaintEvictionControllerConfiguration) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceTaintEvictionControllerConfiguration.
func (in *DeviceTaintEvictionControllerConfiguration) DeepCopy() *DeviceTaintEvictionControllerConfiguration {
if in == nil {
return nil
}
out := new(DeviceTaintEvictionControllerConfiguration)
in.DeepCopyInto(out)
return out
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,257 @@
/*
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 devicetainteviction
import (
"maps"
"slices"
"sync"
"time"
"k8s.io/client-go/util/workqueue"
)
// TODO (pohly): move this to k8s.io/client-go/util/workqueue/mockqueue.go
// if it turns out to be generally useful. Doc comments are already written
// as if the code was there.
// MockQueue is an implementation of [TypedRateLimitingInterface] which
// can be used to test a function which pulls work items out of a queue
// and processes them. It is thread-safe.
//
// A null instance is directly usable. The usual usage is:
//
// var m workqueue.Mock[string]
// m.SyncOne("some-item", func(queue workqueue.TypedRateLimitingInterface[string]) { ... } )
// if diff := cmp.Diff(workqueue.MockState[string]{}, m.State()); diff != "" {
// t.Errorf("unexpected state of mock work queue after sync (-want, +got):\n%s", diff)
// }
//
// All slices get reset to nil when they become empty, so there are no spurious
// differences because of nil vs. empty slice.
type Mock[T comparable] struct {
mutex sync.Mutex
state MockState[T]
}
type MockState[T comparable] struct {
// Ready contains the items which are ready for processing.
Ready []T
// InFlight contains the items which are currently being processed (= Get
// was called, Done not yet).
InFlight []T
// MismatchedDone contains the items for which Done was called without
// a matching Get.
MismatchedDone []T
// Later contains the items which are meant to be added to the queue after
// a certain delay (= AddAfter was called for them). They appear in the
// order in which AddAfter got called.
Later []MockDelayedItem[T]
// Failures contains the items and their retry count which failed to be
// processed (AddRateLimited called at least once, Forget not yet).
// The retry count is always larger than zero.
Failures map[T]int
// ShutDownCalled tracks how often ShutDown got called.
ShutDownCalled int
// ShutDownWithDrainCalled tracks how often ShutDownWithDrain got called.
ShutDownWithDrainCalled int
}
// DeepCopy takes a snapshot of all slices. It cannot do a deep copy of the items in those slices,
// but typically those keys are immutable.
func (m MockState[T]) DeepCopy() *MockState[T] {
m.Ready = slices.Clone(m.Ready)
m.InFlight = slices.Clone(m.InFlight)
m.MismatchedDone = slices.Clone(m.MismatchedDone)
m.Later = slices.Clone(m.Later)
m.Failures = maps.Clone(m.Failures)
return &m
}
// MockDelayedItem is an item which was queue for later processing.
type MockDelayedItem[T comparable] struct {
Item T
Duration time.Duration
}
// SyncOne adds the item to the work queue and calls sync.
// That sync function can pull one or more items from the work
// queue until the queue is empty. Then it is told that the queue
// is shutting down, which must cause it to return.
//
// The test can then retrieve the state of the queue to check the result.
func (m *Mock[T]) SyncOne(item T, sync func(workqueue.TypedRateLimitingInterface[T])) {
// sync must run with the mutex not locked.
defer sync(m)
m.mutex.Lock()
defer m.mutex.Unlock()
m.state.Ready = append(m.state.Ready, item)
}
// State returns the current state of the queue.
func (m *Mock[T]) State() MockState[T] {
m.mutex.Lock()
defer m.mutex.Unlock()
return *m.state.DeepCopy()
}
// Add implements [TypedInterface].
func (m *Mock[T]) Add(item T) {
m.mutex.Lock()
defer m.mutex.Unlock()
if !slices.Contains(m.state.Ready, item) {
m.state.Ready = append(m.state.Ready, item)
}
}
// Len implements [TypedInterface].
func (m *Mock[T]) Len() int {
m.mutex.Lock()
defer m.mutex.Unlock()
return len(m.state.Ready)
}
// Get implements [TypedInterface].
func (m *Mock[T]) Get() (item T, shutdown bool) {
m.mutex.Lock()
defer m.mutex.Unlock()
if len(m.state.Ready) == 0 {
shutdown = true
return
}
item = m.state.Ready[0]
m.state.Ready = m.state.Ready[1:]
if len(m.state.Ready) == 0 {
m.state.Ready = nil
}
m.state.InFlight = append(m.state.InFlight, item)
return item, false
}
// Done implements [TypedInterface].
func (m *Mock[T]) Done(item T) {
m.mutex.Lock()
defer m.mutex.Unlock()
index := slices.Index(m.state.InFlight, item)
if index < 0 {
m.state.MismatchedDone = append(m.state.MismatchedDone, item)
}
m.state.InFlight = slices.Delete(m.state.InFlight, index, index+1)
if len(m.state.InFlight) == 0 {
m.state.InFlight = nil
}
}
// ShutDown implements [TypedInterface].
func (m *Mock[T]) ShutDown() {
m.mutex.Lock()
defer m.mutex.Unlock()
m.state.ShutDownCalled++
}
// ShutDownWithDrain implements [TypedInterface].
func (m *Mock[T]) ShutDownWithDrain() {
m.mutex.Lock()
defer m.mutex.Unlock()
m.state.ShutDownWithDrainCalled++
}
// ShuttingDown implements [TypedInterface].
func (m *Mock[T]) ShuttingDown() bool {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.state.ShutDownCalled > 0 || m.state.ShutDownWithDrainCalled > 0
}
// AddAfter implements [TypedDelayingInterface.AddAfter]
func (m *Mock[T]) AddAfter(item T, duration time.Duration) {
if duration == 0 {
m.Add(item)
return
}
m.mutex.Lock()
defer m.mutex.Unlock()
for i := range m.state.Later {
if m.state.Later[i].Item == item {
// https://github.com/kubernetes/client-go/blob/270e5ab1714527c455865953da8ceba2810dbb50/util/workqueue/delaying_queue.go#L340-L349
// only shortens the delay for an existing item. It does not make it longer.
if m.state.Later[i].Duration > duration {
m.state.Later[i].Duration = duration
}
return
}
}
m.state.Later = append(m.state.Later, MockDelayedItem[T]{Item: item, Duration: duration})
}
// CancelAfter is an extension of the TypedDelayingInterface: it allows a test to remove an item that may or may not have been added before via AddAfter.
func (m *Mock[T]) CancelAfter(item T) {
m.mutex.Lock()
defer m.mutex.Unlock()
m.state.Later = slices.DeleteFunc(m.state.Later, func(later MockDelayedItem[T]) bool {
return later.Item == item
})
}
// AddRateLimited implements [TypedRateLimitingInterface.AddRateLimited].
func (m *Mock[T]) AddRateLimited(item T) {
m.mutex.Lock()
defer m.mutex.Unlock()
if m.state.Failures == nil {
m.state.Failures = make(map[T]int)
}
m.state.Failures[item]++
}
// Forget implements [TypedRateLimitingInterface.Forget].
func (m *Mock[T]) Forget(item T) {
m.mutex.Lock()
defer m.mutex.Unlock()
if m.state.Failures == nil {
return
}
delete(m.state.Failures, item)
}
// NumRequeues implements [TypedRateLimitingInterface.NumRequeues].
func (m *Mock[T]) NumRequeues(item T) int {
m.mutex.Lock()
defer m.mutex.Unlock()
return m.state.Failures[item]
}

View file

@ -1066,6 +1066,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
v1alpha3.DeviceTaintRule{}.OpenAPIModelName(): schema_k8sio_api_resource_v1alpha3_DeviceTaintRule(ref),
v1alpha3.DeviceTaintRuleList{}.OpenAPIModelName(): schema_k8sio_api_resource_v1alpha3_DeviceTaintRuleList(ref),
v1alpha3.DeviceTaintRuleSpec{}.OpenAPIModelName(): schema_k8sio_api_resource_v1alpha3_DeviceTaintRuleSpec(ref),
v1alpha3.DeviceTaintRuleStatus{}.OpenAPIModelName(): schema_k8sio_api_resource_v1alpha3_DeviceTaintRuleStatus(ref),
v1alpha3.DeviceTaintSelector{}.OpenAPIModelName(): schema_k8sio_api_resource_v1alpha3_DeviceTaintSelector(ref),
resourcev1beta1.AllocatedDeviceStatus{}.OpenAPIModelName(): schema_k8sio_api_resource_v1beta1_AllocatedDeviceStatus(ref),
resourcev1beta1.AllocationResult{}.OpenAPIModelName(): schema_k8sio_api_resource_v1beta1_AllocationResult(ref),
@ -1380,6 +1381,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
kubecontrollermanagerconfigv1alpha1.DaemonSetControllerConfiguration{}.OpenAPIModelName(): schema_k8sio_kube_controller_manager_config_v1alpha1_DaemonSetControllerConfiguration(ref),
kubecontrollermanagerconfigv1alpha1.DeploymentControllerConfiguration{}.OpenAPIModelName(): schema_k8sio_kube_controller_manager_config_v1alpha1_DeploymentControllerConfiguration(ref),
kubecontrollermanagerconfigv1alpha1.DeprecatedControllerConfiguration{}.OpenAPIModelName(): schema_k8sio_kube_controller_manager_config_v1alpha1_DeprecatedControllerConfiguration(ref),
kubecontrollermanagerconfigv1alpha1.DeviceTaintEvictionControllerConfiguration{}.OpenAPIModelName(): schema_k8sio_kube_controller_manager_config_v1alpha1_DeviceTaintEvictionControllerConfiguration(ref),
kubecontrollermanagerconfigv1alpha1.EndpointControllerConfiguration{}.OpenAPIModelName(): schema_k8sio_kube_controller_manager_config_v1alpha1_EndpointControllerConfiguration(ref),
kubecontrollermanagerconfigv1alpha1.EndpointSliceControllerConfiguration{}.OpenAPIModelName(): schema_k8sio_kube_controller_manager_config_v1alpha1_EndpointSliceControllerConfiguration(ref),
kubecontrollermanagerconfigv1alpha1.EndpointSliceMirroringControllerConfiguration{}.OpenAPIModelName(): schema_k8sio_kube_controller_manager_config_v1alpha1_EndpointSliceMirroringControllerConfiguration(ref),
@ -48409,7 +48411,7 @@ func schema_k8sio_api_resource_v1_Device(ref common.ReferenceCallback) common.Op
},
},
SchemaProps: spec.SchemaProps{
Description: "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
Description: "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
@ -49373,11 +49375,11 @@ func schema_k8sio_api_resource_v1_DeviceTaint(ref common.ReferenceCallback) comm
},
"effect": {
SchemaProps: spec.SchemaProps{
Description: "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.\n\n\nPossible enum values:\n - `\"NoExecute\"` Evict any already-running pods that do not tolerate the device taint.\n - `\"NoSchedule\"` Do not allow new pods to schedule which use a tainted device unless they tolerate the taint, but allow all pods submitted to Kubelet without going through the scheduler to start, and allow all already-running pods to continue running.",
Description: "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.\n\n\nPossible enum values:\n - `\"NoExecute\"` Evict any already-running pods that do not tolerate the device taint.\n - `\"NoSchedule\"` Do not allow new pods to schedule which use a tainted device unless they tolerate the taint, but allow all pods submitted to Kubelet without going through the scheduler to start, and allow all already-running pods to continue running.\n - `\"None\"` No effect, the taint is purely informational.",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"NoExecute", "NoSchedule"},
Enum: []interface{}{"NoExecute", "NoSchedule", "None"},
},
},
"timeAdded": {
@ -49427,10 +49429,10 @@ func schema_k8sio_api_resource_v1_DeviceToleration(ref common.ReferenceCallback)
},
"effect": {
SchemaProps: spec.SchemaProps{
Description: "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule and NoExecute.\n\n\nPossible enum values:\n - `\"NoExecute\"` Evict any already-running pods that do not tolerate the device taint.\n - `\"NoSchedule\"` Do not allow new pods to schedule which use a tainted device unless they tolerate the taint, but allow all pods submitted to Kubelet without going through the scheduler to start, and allow all already-running pods to continue running.",
Description: "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule and NoExecute.\n\n\nPossible enum values:\n - `\"NoExecute\"` Evict any already-running pods that do not tolerate the device taint.\n - `\"NoSchedule\"` Do not allow new pods to schedule which use a tainted device unless they tolerate the taint, but allow all pods submitted to Kubelet without going through the scheduler to start, and allow all already-running pods to continue running.\n - `\"None\"` No effect, the taint is purely informational.",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"NoExecute", "NoSchedule"},
Enum: []interface{}{"NoExecute", "NoSchedule", "None"},
},
},
"tolerationSeconds": {
@ -50159,7 +50161,7 @@ func schema_k8sio_api_resource_v1_ResourceSliceSpec(ref common.ReferenceCallback
},
},
SchemaProps: spec.SchemaProps{
Description: "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.",
Description: "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints the limit is 64.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
@ -50273,11 +50275,11 @@ func schema_k8sio_api_resource_v1alpha3_DeviceTaint(ref common.ReferenceCallback
},
"effect": {
SchemaProps: spec.SchemaProps{
Description: "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.\n\n\nPossible enum values:\n - `\"NoExecute\"` Evict any already-running pods that do not tolerate the device taint.\n - `\"NoSchedule\"` Do not allow new pods to schedule which use a tainted device unless they tolerate the taint, but allow all pods submitted to Kubelet without going through the scheduler to start, and allow all already-running pods to continue running.",
Description: "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.\n\n\nPossible enum values:\n - `\"NoExecute\"` Evict any already-running pods that do not tolerate the device taint.\n - `\"NoSchedule\"` Do not allow new pods to schedule which use a tainted device unless they tolerate the taint, but allow all pods submitted to Kubelet without going through the scheduler to start, and allow all already-running pods to continue running.\n - `\"None\"` No effect, the taint is purely informational.",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"NoExecute", "NoSchedule"},
Enum: []interface{}{"NoExecute", "NoSchedule", "None"},
},
},
"timeAdded": {
@ -50330,12 +50332,19 @@ func schema_k8sio_api_resource_v1alpha3_DeviceTaintRule(ref common.ReferenceCall
Ref: ref(v1alpha3.DeviceTaintRuleSpec{}.OpenAPIModelName()),
},
},
"status": {
SchemaProps: spec.SchemaProps{
Description: "Status provides information about what was requested in the spec.",
Default: map[string]interface{}{},
Ref: ref(v1alpha3.DeviceTaintRuleStatus{}.OpenAPIModelName()),
},
},
},
Required: []string{"spec"},
},
},
Dependencies: []string{
v1alpha3.DeviceTaintRuleSpec{}.OpenAPIModelName(), metav1.ObjectMeta{}.OpenAPIModelName()},
v1alpha3.DeviceTaintRuleSpec{}.OpenAPIModelName(), v1alpha3.DeviceTaintRuleStatus{}.OpenAPIModelName(), metav1.ObjectMeta{}.OpenAPIModelName()},
}
}
@ -50399,7 +50408,7 @@ func schema_k8sio_api_resource_v1alpha3_DeviceTaintRuleSpec(ref common.Reference
Properties: map[string]spec.Schema{
"deviceSelector": {
SchemaProps: spec.SchemaProps{
Description: "DeviceSelector defines which device(s) the taint is applied to. All selector criteria must be satified for a device to match. The empty selector matches all devices. Without a selector, no devices are matches.",
Description: "DeviceSelector defines which device(s) the taint is applied to. All selector criteria must be satisfied for a device to match. The empty selector matches all devices. Without a selector, no devices are matches.",
Ref: ref(v1alpha3.DeviceTaintSelector{}.OpenAPIModelName()),
},
},
@ -50419,6 +50428,45 @@ func schema_k8sio_api_resource_v1alpha3_DeviceTaintRuleSpec(ref common.Reference
}
}
func schema_k8sio_api_resource_v1alpha3_DeviceTaintRuleStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "DeviceTaintRuleStatus provides information about an on-going pod eviction.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"conditions": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-map-keys": []interface{}{
"type",
},
"x-kubernetes-list-type": "map",
"x-kubernetes-patch-merge-key": "type",
"x-kubernetes-patch-strategy": "merge",
},
},
SchemaProps: spec.SchemaProps{
Description: "Conditions provide information about the state of the DeviceTaintRule and the cluster at some point in time, in a machine-readable and human-readable format.\n\nThe following condition is currently defined as part of this API, more may get added: - Type: EvictionInProgress - Status: True if there are currently pods which need to be evicted, False otherwise\n (includes the effects which don't cause eviction).\n- Reason: not specified, may change - Message: includes information about number of pending pods and already evicted pods\n in a human-readable format, updated periodically, may change\n\nFor `effect: None`, the condition above gets set once for each change to the spec, with the message containing information about what would happen if the effect was `NoExecute`. This feedback can be used to decide whether changing the effect to `NoExecute` will work as intended. It only gets set once to avoid having to constantly update the status.\n\nMust have 8 or fewer entries.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref(metav1.Condition{}.OpenAPIModelName()),
},
},
},
},
},
},
},
},
Dependencies: []string{
metav1.Condition{}.OpenAPIModelName()},
}
}
func schema_k8sio_api_resource_v1alpha3_DeviceTaintSelector(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@ -50426,13 +50474,6 @@ func schema_k8sio_api_resource_v1alpha3_DeviceTaintSelector(ref common.Reference
Description: "DeviceTaintSelector defines which device(s) a DeviceTaintRule applies to. The empty selector matches all devices. Without a selector, no devices are matched.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"deviceClassName": {
SchemaProps: spec.SchemaProps{
Description: "If DeviceClassName is set, the selectors defined there must be satisfied by a device to be selected. This field corresponds to class.metadata.name.",
Type: []string{"string"},
Format: "",
},
},
"driver": {
SchemaProps: spec.SchemaProps{
Description: "If driver is set, only devices from that driver are selected. This fields corresponds to slice.spec.driver.",
@ -50454,30 +50495,9 @@ func schema_k8sio_api_resource_v1alpha3_DeviceTaintSelector(ref common.Reference
Format: "",
},
},
"selectors": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-list-type": "atomic",
},
},
SchemaProps: spec.SchemaProps{
Description: "Selectors contains the same selection criteria as a ResourceClaim. Currently, CEL expressions are supported. All of these selectors must be satisfied.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref(v1alpha3.DeviceSelector{}.OpenAPIModelName()),
},
},
},
},
},
},
},
},
Dependencies: []string{
v1alpha3.DeviceSelector{}.OpenAPIModelName()},
}
}
@ -50679,7 +50699,7 @@ func schema_k8sio_api_resource_v1beta1_BasicDevice(ref common.ReferenceCallback)
},
},
SchemaProps: spec.SchemaProps{
Description: "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
Description: "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
@ -51931,11 +51951,11 @@ func schema_k8sio_api_resource_v1beta1_DeviceTaint(ref common.ReferenceCallback)
},
"effect": {
SchemaProps: spec.SchemaProps{
Description: "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.\n\n\nPossible enum values:\n - `\"NoExecute\"` Evict any already-running pods that do not tolerate the device taint.\n - `\"NoSchedule\"` Do not allow new pods to schedule which use a tainted device unless they tolerate the taint, but allow all pods submitted to Kubelet without going through the scheduler to start, and allow all already-running pods to continue running.",
Description: "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.\n\n\nPossible enum values:\n - `\"NoExecute\"` Evict any already-running pods that do not tolerate the device taint.\n - `\"NoSchedule\"` Do not allow new pods to schedule which use a tainted device unless they tolerate the taint, but allow all pods submitted to Kubelet without going through the scheduler to start, and allow all already-running pods to continue running.\n - `\"None\"` No effect, the taint is purely informational.",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"NoExecute", "NoSchedule"},
Enum: []interface{}{"NoExecute", "NoSchedule", "None"},
},
},
"timeAdded": {
@ -51985,10 +52005,10 @@ func schema_k8sio_api_resource_v1beta1_DeviceToleration(ref common.ReferenceCall
},
"effect": {
SchemaProps: spec.SchemaProps{
Description: "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule and NoExecute.\n\n\nPossible enum values:\n - `\"NoExecute\"` Evict any already-running pods that do not tolerate the device taint.\n - `\"NoSchedule\"` Do not allow new pods to schedule which use a tainted device unless they tolerate the taint, but allow all pods submitted to Kubelet without going through the scheduler to start, and allow all already-running pods to continue running.",
Description: "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule and NoExecute.\n\n\nPossible enum values:\n - `\"NoExecute\"` Evict any already-running pods that do not tolerate the device taint.\n - `\"NoSchedule\"` Do not allow new pods to schedule which use a tainted device unless they tolerate the taint, but allow all pods submitted to Kubelet without going through the scheduler to start, and allow all already-running pods to continue running.\n - `\"None\"` No effect, the taint is purely informational.",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"NoExecute", "NoSchedule"},
Enum: []interface{}{"NoExecute", "NoSchedule", "None"},
},
},
"tolerationSeconds": {
@ -52627,7 +52647,7 @@ func schema_k8sio_api_resource_v1beta1_ResourceSliceSpec(ref common.ReferenceCal
},
},
SchemaProps: spec.SchemaProps{
Description: "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.",
Description: "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints the limit is 64.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
@ -53071,7 +53091,7 @@ func schema_k8sio_api_resource_v1beta2_Device(ref common.ReferenceCallback) comm
},
},
SchemaProps: spec.SchemaProps{
Description: "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
Description: "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
@ -54035,11 +54055,11 @@ func schema_k8sio_api_resource_v1beta2_DeviceTaint(ref common.ReferenceCallback)
},
"effect": {
SchemaProps: spec.SchemaProps{
Description: "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.\n\n\nPossible enum values:\n - `\"NoExecute\"` Evict any already-running pods that do not tolerate the device taint.\n - `\"NoSchedule\"` Do not allow new pods to schedule which use a tainted device unless they tolerate the taint, but allow all pods submitted to Kubelet without going through the scheduler to start, and allow all already-running pods to continue running.",
Description: "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.\n\n\nPossible enum values:\n - `\"NoExecute\"` Evict any already-running pods that do not tolerate the device taint.\n - `\"NoSchedule\"` Do not allow new pods to schedule which use a tainted device unless they tolerate the taint, but allow all pods submitted to Kubelet without going through the scheduler to start, and allow all already-running pods to continue running.\n - `\"None\"` No effect, the taint is purely informational.",
Default: "",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"NoExecute", "NoSchedule"},
Enum: []interface{}{"NoExecute", "NoSchedule", "None"},
},
},
"timeAdded": {
@ -54089,10 +54109,10 @@ func schema_k8sio_api_resource_v1beta2_DeviceToleration(ref common.ReferenceCall
},
"effect": {
SchemaProps: spec.SchemaProps{
Description: "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule and NoExecute.\n\n\nPossible enum values:\n - `\"NoExecute\"` Evict any already-running pods that do not tolerate the device taint.\n - `\"NoSchedule\"` Do not allow new pods to schedule which use a tainted device unless they tolerate the taint, but allow all pods submitted to Kubelet without going through the scheduler to start, and allow all already-running pods to continue running.",
Description: "Effect indicates the taint effect to match. Empty means match all taint effects. When specified, allowed values are NoSchedule and NoExecute.\n\n\nPossible enum values:\n - `\"NoExecute\"` Evict any already-running pods that do not tolerate the device taint.\n - `\"NoSchedule\"` Do not allow new pods to schedule which use a tainted device unless they tolerate the taint, but allow all pods submitted to Kubelet without going through the scheduler to start, and allow all already-running pods to continue running.\n - `\"None\"` No effect, the taint is purely informational.",
Type: []string{"string"},
Format: "",
Enum: []interface{}{"NoExecute", "NoSchedule"},
Enum: []interface{}{"NoExecute", "NoSchedule", "None"},
},
},
"tolerationSeconds": {
@ -54821,7 +54841,7 @@ func schema_k8sio_api_resource_v1beta2_ResourceSliceSpec(ref common.ReferenceCal
},
},
SchemaProps: spec.SchemaProps{
Description: "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.",
Description: "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints the limit is 64.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
@ -66888,6 +66908,28 @@ func schema_k8sio_kube_controller_manager_config_v1alpha1_DeprecatedControllerCo
}
}
func schema_k8sio_kube_controller_manager_config_v1alpha1_DeviceTaintEvictionControllerConfiguration(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "DeviceTaintEvictionControllerConfiguration contains elements configuring the device taint eviction controller.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"ConcurrentSyncs": {
SchemaProps: spec.SchemaProps{
Description: "ConcurrentSyncs is the number of operations (deleting a pod, updating a ResourcClaim status, etc.) that will be done concurrently. Larger number = processing, but more CPU (and network) load.\n\nThe default is 10.",
Default: 0,
Type: []string{"integer"},
Format: "int32",
},
},
},
Required: []string{"ConcurrentSyncs"},
},
},
}
}
func schema_k8sio_kube_controller_manager_config_v1alpha1_EndpointControllerConfiguration(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@ -67393,12 +67435,19 @@ func schema_k8sio_kube_controller_manager_config_v1alpha1_KubeControllerManagerC
Ref: ref(kubecontrollermanagerconfigv1alpha1.ValidatingAdmissionPolicyStatusControllerConfiguration{}.OpenAPIModelName()),
},
},
"DeviceTaintEvictionController": {
SchemaProps: spec.SchemaProps{
Description: "DeviceTaintEvictionControllerConfiguration contains elements configuring the device taint eviction controller.",
Default: map[string]interface{}{},
Ref: ref(kubecontrollermanagerconfigv1alpha1.DeviceTaintEvictionControllerConfiguration{}.OpenAPIModelName()),
},
},
},
Required: []string{"Generic", "KubeCloudShared", "AttachDetachController", "CSRSigningController", "DaemonSetController", "DeploymentController", "StatefulSetController", "DeprecatedController", "EndpointController", "EndpointSliceController", "EndpointSliceMirroringController", "EphemeralVolumeController", "GarbageCollectorController", "HPAController", "JobController", "CronJobController", "LegacySATokenCleaner", "NamespaceController", "NodeIPAMController", "NodeLifecycleController", "PersistentVolumeBinderController", "PodGCController", "ReplicaSetController", "ReplicationController", "ResourceQuotaController", "SAController", "ServiceController", "TTLAfterFinishedController", "ValidatingAdmissionPolicyStatusController"},
Required: []string{"Generic", "KubeCloudShared", "AttachDetachController", "CSRSigningController", "DaemonSetController", "DeploymentController", "StatefulSetController", "DeprecatedController", "EndpointController", "EndpointSliceController", "EndpointSliceMirroringController", "EphemeralVolumeController", "GarbageCollectorController", "HPAController", "JobController", "CronJobController", "LegacySATokenCleaner", "NamespaceController", "NodeIPAMController", "NodeLifecycleController", "PersistentVolumeBinderController", "PodGCController", "ReplicaSetController", "ReplicationController", "ResourceQuotaController", "SAController", "ServiceController", "TTLAfterFinishedController", "ValidatingAdmissionPolicyStatusController", "DeviceTaintEvictionController"},
},
},
Dependencies: []string{
configv1alpha1.KubeCloudSharedConfiguration{}.OpenAPIModelName(), serviceconfigv1alpha1.ServiceControllerConfiguration{}.OpenAPIModelName(), controllermanagerconfigv1alpha1.GenericControllerManagerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.AttachDetachControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.CSRSigningControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.CronJobControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.DaemonSetControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.DeploymentControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.DeprecatedControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.EndpointControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.EndpointSliceControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.EndpointSliceMirroringControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.EphemeralVolumeControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.GarbageCollectorControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.HPAControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.JobControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.LegacySATokenCleanerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.NamespaceControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.NodeIPAMControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.NodeLifecycleControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.PersistentVolumeBinderControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.PodGCControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.ReplicaSetControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.ReplicationControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.ResourceQuotaControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.SAControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.StatefulSetControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.TTLAfterFinishedControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.ValidatingAdmissionPolicyStatusControllerConfiguration{}.OpenAPIModelName()},
configv1alpha1.KubeCloudSharedConfiguration{}.OpenAPIModelName(), serviceconfigv1alpha1.ServiceControllerConfiguration{}.OpenAPIModelName(), controllermanagerconfigv1alpha1.GenericControllerManagerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.AttachDetachControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.CSRSigningControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.CronJobControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.DaemonSetControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.DeploymentControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.DeprecatedControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.DeviceTaintEvictionControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.EndpointControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.EndpointSliceControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.EndpointSliceMirroringControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.EphemeralVolumeControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.GarbageCollectorControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.HPAControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.JobControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.LegacySATokenCleanerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.NamespaceControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.NodeIPAMControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.NodeLifecycleControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.PersistentVolumeBinderControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.PodGCControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.ReplicaSetControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.ReplicationControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.ResourceQuotaControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.SAControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.StatefulSetControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.TTLAfterFinishedControllerConfiguration{}.OpenAPIModelName(), kubecontrollermanagerconfigv1alpha1.ValidatingAdmissionPolicyStatusControllerConfiguration{}.OpenAPIModelName()},
}
}

View file

@ -17,14 +17,19 @@ limitations under the License.
package storage
import (
"context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
"k8s.io/kubernetes/pkg/apis/resource"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/resource/devicetaintrule"
"sigs.k8s.io/structured-merge-diff/v6/fieldpath"
)
// REST implements a RESTStorage for DeviceTaintRule.
@ -33,7 +38,7 @@ type REST struct {
}
// NewREST returns a RESTStorage object that will work against DeviceTaintRule.
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) {
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, *StatusREST, error) {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &resource.DeviceTaintRule{} },
NewListFunc: func() runtime.Object { return &resource.DeviceTaintRuleList{} },
@ -44,13 +49,50 @@ func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) {
UpdateStrategy: devicetaintrule.Strategy,
DeleteStrategy: devicetaintrule.Strategy,
ReturnDeletedObject: true,
ResetFieldsStrategy: devicetaintrule.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
}
options := &generic.StoreOptions{RESTOptions: optsGetter}
if err := store.CompleteWithOptions(options); err != nil {
return nil, err
return nil, nil, err
}
return &REST{store}, nil
statusStore := *store
statusStore.UpdateStrategy = devicetaintrule.StatusStrategy
statusStore.ResetFieldsStrategy = devicetaintrule.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}, nil
}
// StatusREST implements the REST endpoint for changing the status of a DeviceTaintRule.
type StatusREST struct {
store *genericregistry.Store
}
// New creates a new DeviceTaintRule object.
func (r *StatusREST) New() runtime.Object {
return &resource.DeviceTaintRule{}
}
func (r *StatusREST) Destroy() {
// Given that underlying store is shared with REST,
// we don't destroy it here explicitly.
}
// Get retrieves the object from the storage. It is required to support Patch.
func (r *StatusREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
return r.store.Get(ctx, name, options)
}
// Update alters the status subset of an object.
func (r *StatusREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
// We are explicitly setting forceAllowCreate to false in the call to the underlying storage because
// subresources should never allow create on update.
return r.store.Update(ctx, name, objInfo, createValidation, updateValidation, false, options)
}
// GetResetFields implements rest.ResetFieldsStrategy
func (r *StatusREST) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
return r.store.GetResetFields()
}

View file

@ -20,19 +20,24 @@ import (
"testing"
"time"
"github.com/google/go-cmp/cmp"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/generic"
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
"k8s.io/apiserver/pkg/registry/rest"
etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
"k8s.io/kubernetes/pkg/apis/resource"
_ "k8s.io/kubernetes/pkg/apis/resource/install"
"k8s.io/kubernetes/pkg/registry/registrytest"
)
func newStorage(t *testing.T) (*REST, *etcd3testing.EtcdTestServer) {
func newStorage(t *testing.T) (*REST, *StatusREST, *etcd3testing.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorageForResource(t, resource.Resource("devicetaintrules"))
restOptions := generic.RESTOptions{
StorageConfig: etcdStorage,
@ -40,11 +45,11 @@ func newStorage(t *testing.T) (*REST, *etcd3testing.EtcdTestServer) {
DeleteCollectionWorkers: 1,
ResourcePrefix: "devicetaintrules",
}
deviceTaintStorage, err := NewREST(restOptions)
deviceTaintStorage, statusStorage, err := NewREST(restOptions)
if err != nil {
t.Fatalf("unexpected error from REST storage: %v", err)
}
return deviceTaintStorage, server
return deviceTaintStorage, statusStorage, server
}
func validNewDeviceTaint(name string) *resource.DeviceTaintRule {
@ -63,7 +68,7 @@ func validNewDeviceTaint(name string) *resource.DeviceTaintRule {
}
func TestCreate(t *testing.T) {
storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
@ -80,7 +85,7 @@ func TestCreate(t *testing.T) {
}
func TestUpdate(t *testing.T) {
storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
@ -98,7 +103,7 @@ func TestUpdate(t *testing.T) {
}
func TestDelete(t *testing.T) {
storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope().ReturnDeletedObject()
@ -106,7 +111,7 @@ func TestDelete(t *testing.T) {
}
func TestGet(t *testing.T) {
storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
@ -114,7 +119,7 @@ func TestGet(t *testing.T) {
}
func TestList(t *testing.T) {
storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
@ -122,7 +127,7 @@ func TestList(t *testing.T) {
}
func TestWatch(t *testing.T) {
storage, server := newStorage(t)
storage, _, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store).ClusterScope()
@ -144,3 +149,39 @@ func TestWatch(t *testing.T) {
},
)
}
func TestUpdateStatus(t *testing.T) {
storage, statusStorage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
ctx := genericapirequest.NewDefaultContext()
key, _ := storage.KeyFunc(ctx, "foo")
deviceTaintStart := validNewDeviceTaint("foo")
err := storage.Storage.Create(ctx, key, deviceTaintStart, nil, 0, false)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
deviceTaint := deviceTaintStart.DeepCopy()
deviceTaint.Status.Conditions = []metav1.Condition{{
Type: "EvicitionInProgress",
Status: metav1.ConditionTrue,
Reason: "PodsLeft",
Message: "100 pods left",
LastTransitionTime: metav1.Time{Time: time.Now().Truncate(time.Second)},
}}
_, _, err = statusStorage.Update(ctx, deviceTaint.Name, rest.DefaultUpdatedObjectInfo(deviceTaint), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
obj, err := storage.Get(ctx, "foo", &metav1.GetOptions{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
deviceTaintOut := obj.(*resource.DeviceTaintRule)
// only compare relevant changes b/c of difference in metadata
if !apiequality.Semantic.DeepEqual(deviceTaint.Status, deviceTaintOut.Status) {
t.Errorf("unexpected object: %s", cmp.Diff(deviceTaint.Status, deviceTaintOut.Status))
}
}

View file

@ -20,12 +20,14 @@ import (
"context"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/resource"
"k8s.io/kubernetes/pkg/apis/resource/validation"
"sigs.k8s.io/structured-merge-diff/v6/fieldpath"
)
// deviceTaintRuleStrategy implements behavior for DeviceTaintRule objects
@ -34,51 +36,105 @@ type deviceTaintRuleStrategy struct {
names.NameGenerator
}
var Strategy = deviceTaintRuleStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
var (
Strategy = &deviceTaintRuleStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
StatusStrategy = &deviceTaintRuleStatusStrategy{deviceTaintRuleStrategy: Strategy}
)
func (deviceTaintRuleStrategy) NamespaceScoped() bool {
return false
}
func (deviceTaintRuleStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
patch := obj.(*resource.DeviceTaintRule)
patch.Generation = 1
// GetResetFields returns the set of fields that get reset by the strategy and
// should not be modified by the user. For a new DeviceTaintRule that is the
// status.
func (*deviceTaintRuleStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"resource.k8s.io/v1alpha3": fieldpath.NewSet(
fieldpath.MakePathOrDie("status"),
),
}
return fields
}
func (deviceTaintRuleStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
patch := obj.(*resource.DeviceTaintRule)
return validation.ValidateDeviceTaintRule(patch)
func (*deviceTaintRuleStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
rule := obj.(*resource.DeviceTaintRule)
// Status must not be set by user on create.
rule.Status = resource.DeviceTaintRuleStatus{}
rule.Generation = 1
}
func (deviceTaintRuleStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
func (*deviceTaintRuleStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
rule := obj.(*resource.DeviceTaintRule)
return validation.ValidateDeviceTaintRule(rule)
}
func (*deviceTaintRuleStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
func (deviceTaintRuleStrategy) Canonicalize(obj runtime.Object) {
func (*deviceTaintRuleStrategy) Canonicalize(obj runtime.Object) {
}
func (deviceTaintRuleStrategy) AllowCreateOnUpdate() bool {
func (*deviceTaintRuleStrategy) AllowCreateOnUpdate() bool {
return false
}
func (deviceTaintRuleStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
patch := obj.(*resource.DeviceTaintRule)
oldPatch := old.(*resource.DeviceTaintRule)
func (*deviceTaintRuleStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
rule := obj.(*resource.DeviceTaintRule)
oldRule := old.(*resource.DeviceTaintRule)
rule.Status = oldRule.Status
// Any changes to the spec increment the generation number.
if !apiequality.Semantic.DeepEqual(oldPatch.Spec, patch.Spec) {
patch.Generation = oldPatch.Generation + 1
if !apiequality.Semantic.DeepEqual(oldRule.Spec, rule.Spec) {
rule.Generation = oldRule.Generation + 1
}
}
func (deviceTaintRuleStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
func (*deviceTaintRuleStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
return validation.ValidateDeviceTaintRuleUpdate(obj.(*resource.DeviceTaintRule), old.(*resource.DeviceTaintRule))
}
func (deviceTaintRuleStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
func (*deviceTaintRuleStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
func (deviceTaintRuleStrategy) AllowUnconditionalUpdate() bool {
func (*deviceTaintRuleStrategy) AllowUnconditionalUpdate() bool {
return true
}
type deviceTaintRuleStatusStrategy struct {
*deviceTaintRuleStrategy
}
// GetResetFields returns the set of fields that get reset by the strategy and
// should not be modified by the user. For a status update that is the spec.
func (*deviceTaintRuleStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"resource.k8s.io/v1alpha3": fieldpath.NewSet(
fieldpath.MakePathOrDie("metadata"),
fieldpath.MakePathOrDie("spec"),
),
}
return fields
}
func (*deviceTaintRuleStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
newRule := obj.(*resource.DeviceTaintRule)
oldRule := old.(*resource.DeviceTaintRule)
newRule.Spec = oldRule.Spec
metav1.ResetObjectMetaForStatus(&newRule.ObjectMeta, &oldRule.ObjectMeta)
}
func (r *deviceTaintRuleStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
newRule := obj.(*resource.DeviceTaintRule)
oldRule := old.(*resource.DeviceTaintRule)
return validation.ValidateDeviceTaintRuleStatusUpdate(newRule, oldRule)
}
// WarningsOnUpdate returns warnings for the given update.
func (*deviceTaintRuleStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}

View file

@ -19,14 +19,17 @@ package devicetaintrule
import (
"testing"
"github.com/stretchr/testify/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kubernetes/pkg/apis/resource"
)
var patch = &resource.DeviceTaintRule{
var obj = &resource.DeviceTaintRule{
ObjectMeta: metav1.ObjectMeta{
Name: "valid-patch",
Name: "valid-patch",
Generation: 1,
},
Spec: resource.DeviceTaintRuleSpec{
Taint: resource.DeviceTaint{
@ -36,6 +39,31 @@ var patch = &resource.DeviceTaintRule{
},
}
var objWithStatus = &resource.DeviceTaintRule{
ObjectMeta: metav1.ObjectMeta{
Name: "valid-patch",
Generation: 1,
},
Spec: resource.DeviceTaintRuleSpec{
Taint: resource.DeviceTaint{
Key: "example.com/tainted",
Effect: resource.DeviceTaintEffectNoExecute,
},
},
Status: resource.DeviceTaintRuleStatus{
Conditions: []metav1.Condition{{
Type: "foo",
Status: metav1.ConditionFalse,
LastTransitionTime: metav1.Now(),
Reason: "something",
Message: "else",
}},
},
}
var fieldImmutableError = "field is immutable"
var metadataError = "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters"
func TestDeviceTaintRuleStrategy(t *testing.T) {
if Strategy.NamespaceScoped() {
t.Errorf("DeviceTaintRule must not be namespace scoped")
@ -47,40 +75,202 @@ func TestDeviceTaintRuleStrategy(t *testing.T) {
func TestDeviceTaintRuleStrategyCreate(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
patch := patch.DeepCopy()
testcases := map[string]struct {
obj *resource.DeviceTaintRule
expectValidationError string
expectObj *resource.DeviceTaintRule
}{
"simple": {
obj: obj,
expectObj: obj,
},
"validation-error": {
obj: func() *resource.DeviceTaintRule {
obj := obj.DeepCopy()
obj.Name = "%#@$%$"
return obj
}(),
expectValidationError: metadataError,
},
"drop-status": {
obj: objWithStatus,
expectObj: obj,
},
"set-generation": {
obj: func() *resource.DeviceTaintRule {
obj := obj.DeepCopy()
obj.Generation = 42 // Cannot be set by client on create, overwritten with 1.
return obj
}(),
expectObj: obj,
},
}
Strategy.PrepareForCreate(ctx, patch)
errs := Strategy.Validate(ctx, patch)
if len(errs) != 0 {
t.Errorf("unexpected error validating for create %v", errs)
for name, tc := range testcases {
t.Run(name, func(t *testing.T) {
obj := tc.obj.DeepCopy()
Strategy.PrepareForCreate(ctx, obj)
if errs := Strategy.Validate(ctx, obj); len(errs) != 0 {
if tc.expectValidationError == "" {
t.Fatalf("unexpected error(s): %v", errs)
}
assert.ErrorContains(t, errs[0], tc.expectValidationError, "the error message should have contained the expected error message")
return
}
if tc.expectValidationError != "" {
t.Fatal("expected validation error(s), got none")
}
if warnings := Strategy.WarningsOnCreate(ctx, obj); len(warnings) != 0 {
t.Fatalf("unexpected warnings: %q", warnings)
}
Strategy.Canonicalize(obj)
assert.Equal(t, tc.expectObj, obj)
})
}
}
func TestDeviceTaintRuleStrategyUpdate(t *testing.T) {
t.Run("no-changes-okay", func(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
patch := patch.DeepCopy()
newPatch := patch.DeepCopy()
newPatch.ResourceVersion = "4"
ctx := genericapirequest.NewDefaultContext()
Strategy.PrepareForUpdate(ctx, newPatch, patch)
errs := Strategy.ValidateUpdate(ctx, newPatch, patch)
if len(errs) != 0 {
t.Errorf("unexpected validation errors: %v", errs)
}
})
testcases := map[string]struct {
oldObj *resource.DeviceTaintRule
newObj *resource.DeviceTaintRule
expectValidationError string
expectObj *resource.DeviceTaintRule
}{
"no-changes-okay": {
oldObj: obj,
newObj: obj,
expectObj: obj,
},
"name-change-not-allowed": {
oldObj: obj,
newObj: func() *resource.DeviceTaintRule {
obj := obj.DeepCopy()
obj.Name += "-2"
return obj
}(),
expectValidationError: fieldImmutableError,
},
"drop-status": {
oldObj: obj,
newObj: objWithStatus,
expectObj: obj,
},
"bump-generation": {
oldObj: obj,
newObj: func() *resource.DeviceTaintRule {
obj := obj.DeepCopy()
obj.Spec.Taint.Effect = resource.DeviceTaintEffectNone
return obj
}(),
expectObj: func() *resource.DeviceTaintRule {
obj := obj.DeepCopy()
obj.Spec.Taint.Effect = resource.DeviceTaintEffectNone
obj.Generation++
return obj
}(),
},
}
t.Run("name-change-not-allowed", func(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
patch := patch.DeepCopy()
newPatch := patch.DeepCopy()
newPatch.Name = "valid-patch-2"
newPatch.ResourceVersion = "4"
for name, tc := range testcases {
t.Run(name, func(t *testing.T) {
oldObj := tc.oldObj.DeepCopy()
newObj := tc.newObj.DeepCopy()
newObj.ResourceVersion = "4"
Strategy.PrepareForUpdate(ctx, newPatch, patch)
errs := Strategy.ValidateUpdate(ctx, newPatch, patch)
if len(errs) == 0 {
t.Errorf("expected a validation error")
}
})
Strategy.PrepareForUpdate(ctx, newObj, oldObj)
if errs := Strategy.ValidateUpdate(ctx, newObj, oldObj); len(errs) != 0 {
if tc.expectValidationError == "" {
t.Fatalf("unexpected error(s): %v", errs)
}
assert.ErrorContains(t, errs[0], tc.expectValidationError, "the error message should have contained the expected error message")
return
}
if tc.expectValidationError != "" {
t.Fatal("expected validation error(s), got none")
}
if warnings := Strategy.WarningsOnUpdate(ctx, newObj, oldObj); len(warnings) != 0 {
t.Fatalf("unexpected warnings: %q", warnings)
}
Strategy.Canonicalize(newObj)
expectObj := tc.expectObj.DeepCopy()
expectObj.ResourceVersion = "4"
assert.Equal(t, expectObj, newObj)
})
}
}
func TestStatusStrategyUpdate(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
testcases := map[string]struct {
oldObj *resource.DeviceTaintRule
newObj *resource.DeviceTaintRule
expectValidationError string
expectObj *resource.DeviceTaintRule
}{
"no-changes-okay": {
oldObj: obj,
newObj: obj,
expectObj: obj,
},
"name-change-not-allowed": {
oldObj: obj,
newObj: func() *resource.DeviceTaintRule {
obj := obj.DeepCopy()
obj.Name += "-2"
return obj
}(),
expectValidationError: fieldImmutableError,
},
// Cannot add finalizers, annotations and labels during status update.
"drop-meta-changes": {
oldObj: obj,
newObj: func() *resource.DeviceTaintRule {
obj := obj.DeepCopy()
obj.Finalizers = []string{"foo"}
obj.Annotations = map[string]string{"foo": "bar"}
obj.Labels = map[string]string{"foo": "bar"}
return obj
}(),
expectObj: obj,
},
"drop-spec": {
oldObj: obj,
newObj: func() *resource.DeviceTaintRule {
obj := obj.DeepCopy()
obj.Spec.Taint.Effect = resource.DeviceTaintEffectNone
return obj
}(),
expectObj: obj,
},
}
for name, tc := range testcases {
t.Run(name, func(t *testing.T) {
oldObj := tc.oldObj.DeepCopy()
newObj := tc.newObj.DeepCopy()
newObj.ResourceVersion = "4"
StatusStrategy.PrepareForUpdate(ctx, newObj, oldObj)
if errs := StatusStrategy.ValidateUpdate(ctx, newObj, oldObj); len(errs) != 0 {
if tc.expectValidationError == "" {
t.Fatalf("unexpected error(s): %v", errs)
}
assert.ErrorContains(t, errs[0], tc.expectValidationError, "the error message should have contained the expected error message")
return
}
if tc.expectValidationError != "" {
t.Fatal("expected validation error(s), got none")
}
if warnings := StatusStrategy.WarningsOnUpdate(ctx, newObj, oldObj); len(warnings) != 0 {
t.Fatalf("unexpected warnings: %q", warnings)
}
StatusStrategy.Canonicalize(newObj)
expectObj := tc.expectObj.DeepCopy()
expectObj.ResourceVersion = "4"
assert.Equal(t, expectObj, newObj)
})
}
}

View file

@ -153,15 +153,19 @@ func NewStatusStrategy(resourceclaimStrategy *resourceclaimStrategy) *resourcecl
func (*resourceclaimStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
fields := map[fieldpath.APIVersion]*fieldpath.Set{
"resource.k8s.io/v1alpha3": fieldpath.NewSet(
fieldpath.MakePathOrDie("metadata"),
fieldpath.MakePathOrDie("spec"),
),
"resource.k8s.io/v1beta1": fieldpath.NewSet(
fieldpath.MakePathOrDie("metadata"),
fieldpath.MakePathOrDie("spec"),
),
"resource.k8s.io/v1beta2": fieldpath.NewSet(
fieldpath.MakePathOrDie("metadata"),
fieldpath.MakePathOrDie("spec"),
),
"resource.k8s.io/v1": fieldpath.NewSet(
fieldpath.MakePathOrDie("metadata"),
fieldpath.MakePathOrDie("spec"),
),
}

View file

@ -118,11 +118,12 @@ func (p RESTStorageProvider) v1alpha3Storage(apiResourceConfigSource serverstora
storage := map[string]rest.Storage{}
if resource := "devicetaintrules"; apiResourceConfigSource.ResourceEnabled(resourcev1alpha3.SchemeGroupVersion.WithResource(resource)) {
deviceTaintStorage, err := devicetaintrulestore.NewREST(restOptionsGetter)
deviceTaintStorage, deviceTaintStatusStorage, err := devicetaintrulestore.NewREST(restOptionsGetter)
if err != nil {
return nil, err
}
storage[resource] = deviceTaintStorage
storage[resource+"/status"] = deviceTaintStatusStorage
}
return storage, nil

View file

@ -224,6 +224,8 @@ func buildControllerRoles() ([]rbacv1.ClusterRole, []rbacv1.ClusterRoleBinding)
rbacv1helpers.NewRule("get", "list", "watch", "delete").Groups(legacyGroup).Resources("pods").RuleOrDie(),
// Sets pod conditions.
rbacv1helpers.NewRule("update", "patch").Groups(legacyGroup).Resources("pods/status").RuleOrDie(),
// Sets DeviceTaintRule conditions.
rbacv1helpers.NewRule("update", "patch").Groups(resourceGroup).Resources("devicetaintrules/status").RuleOrDie(),
// The rest is read-only.
rbacv1helpers.NewRule("get", "list", "watch").Groups(resourceGroup).Resources("resourceclaims").RuleOrDie(),
rbacv1helpers.NewRule("get", "list", "watch").Groups(resourceGroup).Resources("resourceslices").RuleOrDie(),

View file

@ -395,7 +395,9 @@ message Device {
// If specified, these are the driver-defined taints.
//
// The maximum number of taints is 4.
// The maximum number of taints is 16. If taints are set for
// any device in a ResourceSlice, then the maximum number of
// allowed devices per ResourceSlice is 64 instead of 128.
//
// This is an alpha field and requires enabling the DRADeviceTaints
// feature gate.
@ -1116,8 +1118,10 @@ message DeviceTaint {
// The effect of the taint on claims that do not tolerate the taint
// and through such claims on the pods using them.
// Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here.
//
// Valid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here. More effects may get added in the future.
// Consumers must treat unknown effects like None.
//
// +required
// +k8s:required
@ -1658,7 +1662,7 @@ message ResourceSliceSpec {
// Devices lists some or all of the devices in this pool.
//
// Must not have more than 128 entries.
// Must not have more than 128 entries. If any device uses taints the limit is 64.
//
// +optional
// +listType=atomic

View file

@ -149,7 +149,7 @@ type ResourceSliceSpec struct {
// Devices lists some or all of the devices in this pool.
//
// Must not have more than 128 entries.
// Must not have more than 128 entries. If any device uses taints the limit is 64.
//
// +optional
// +listType=atomic
@ -250,6 +250,7 @@ type ResourcePool struct {
const ResourceSliceMaxSharedCapacity = 128
const ResourceSliceMaxDevices = 128
const ResourceSliceMaxDevicesWithTaints = 64
const PoolNameMaxLength = validation.DNS1123SubdomainMaxLength // Same as for a single node name.
const BindingConditionsMaxSize = 4
const BindingFailureConditionsMaxSize = 4
@ -333,7 +334,9 @@ type Device struct {
// If specified, these are the driver-defined taints.
//
// The maximum number of taints is 4.
// The maximum number of taints is 16. If taints are set for
// any device in a ResourceSlice, then the maximum number of
// allowed devices per ResourceSlice is 64 instead of 128.
//
// This is an alpha field and requires enabling the DRADeviceTaints
// feature gate.
@ -618,8 +621,8 @@ type DeviceAttribute struct {
// DeviceAttributeMaxValueLength is the maximum length of a string or version attribute value.
const DeviceAttributeMaxValueLength = 64
// DeviceTaintsMaxLength is the maximum number of taints per device.
const DeviceTaintsMaxLength = 4
// DeviceTaintsMaxLength is the maximum number of taints per Device.
const DeviceTaintsMaxLength = 16
// The device this taint is attached to has the "effect" on
// any claim which does not tolerate the taint and, through the claim,
@ -641,8 +644,10 @@ type DeviceTaint struct {
// The effect of the taint on claims that do not tolerate the taint
// and through such claims on the pods using them.
// Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here.
//
// Valid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here. More effects may get added in the future.
// Consumers must treat unknown effects like None.
//
// +required
// +k8s:required
@ -652,6 +657,14 @@ type DeviceTaint struct {
//
// Implementing PreferNoSchedule would depend on a scoring solution for DRA.
// It might get added as part of that.
//
// A possible future new effect is NoExecuteWithPodDisruptionBudget:
// honor the pod disruption budget instead of simply deleting pods.
// This is currently undecided, it could also be a separate field.
//
// Validation must be prepared to allow unknown enums in stored objects,
// which will enable adding new enums within a single release without
// ratcheting.
// TimeAdded represents the time at which the taint was added.
// Added automatically during create or update if not set.
@ -671,6 +684,9 @@ type DeviceTaint struct {
type DeviceTaintEffect string
const (
// No effect, the taint is purely informational.
DeviceTaintEffectNone DeviceTaintEffect = "None"
// Do not allow new pods to schedule which use a tainted device unless they tolerate the taint,
// but allow all pods submitted to Kubelet without going through the scheduler
// to start, and allow all already-running pods to continue running.

View file

@ -121,7 +121,7 @@ var map_Device = map[string]string{
"nodeName": "NodeName identifies the node where the device is available.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set.",
"nodeSelector": "NodeSelector defines the nodes where the device is available.\n\nMust use exactly one term.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set.",
"allNodes": "AllNodes indicates that all nodes have access to the device.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set.",
"taints": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"taints": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"bindsToNode": "BindsToNode indicates if the usage of an allocation involving this device has to be limited to exactly the node that was chosen when allocating the claim. If set to true, the scheduler will set the ResourceClaim.Status.Allocation.NodeSelector to match the node where the allocation was made.\n\nThis is an alpha field and requires enabling the DRADeviceBindingConditions and DRAResourceClaimDeviceStatus feature gates.",
"bindingConditions": "BindingConditions defines the conditions for proceeding with binding. All of these conditions must be set in the per-device status conditions with a value of True to proceed with binding the pod to the node while scheduling the pod.\n\nThe maximum number of binding conditions is 4.\n\nThe conditions must be a valid condition type string.\n\nThis is an alpha field and requires enabling the DRADeviceBindingConditions and DRAResourceClaimDeviceStatus feature gates.",
"bindingFailureConditions": "BindingFailureConditions defines the conditions for binding failure. They may be set in the per-device status conditions. If any is set to \"True\", a binding failure occurred.\n\nThe maximum number of binding failure conditions is 4.\n\nThe conditions must be a valid condition type string.\n\nThis is an alpha field and requires enabling the DRADeviceBindingConditions and DRAResourceClaimDeviceStatus feature gates.",
@ -320,7 +320,7 @@ var map_DeviceTaint = map[string]string{
"": "The device this taint is attached to has the \"effect\" on any claim which does not tolerate the taint and, through the claim, to pods using the claim.",
"key": "The taint key to be applied to a device. Must be a label name.",
"value": "The taint value corresponding to the taint key. Must be a label value.",
"effect": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.",
"effect": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.",
"timeAdded": "TimeAdded represents the time at which the taint was added. Added automatically during create or update if not set.",
}
@ -498,7 +498,7 @@ var map_ResourceSliceSpec = map[string]string{
"nodeName": "NodeName identifies the node which provides the resources in this pool. A field selector can be used to list only ResourceSlice objects belonging to a certain node.\n\nThis field can be used to limit access from nodes to ResourceSlices with the same node name. It also indicates to autoscalers that adding new nodes of the same type as some old node might also make new resources available.\n\nExactly one of NodeName, NodeSelector, AllNodes, and PerDeviceNodeSelection must be set. This field is immutable.",
"nodeSelector": "NodeSelector defines which nodes have access to the resources in the pool, when that pool is not limited to a single node.\n\nMust use exactly one term.\n\nExactly one of NodeName, NodeSelector, AllNodes, and PerDeviceNodeSelection must be set.",
"allNodes": "AllNodes indicates that all nodes have access to the resources in the pool.\n\nExactly one of NodeName, NodeSelector, AllNodes, and PerDeviceNodeSelection must be set.",
"devices": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.",
"devices": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints the limit is 64.",
"perDeviceNodeSelection": "PerDeviceNodeSelection defines whether the access from nodes to resources in the pool is set on the ResourceSlice level or on each device. If it is set to true, every device defined the ResourceSlice must specify this individually.\n\nExactly one of NodeName, NodeSelector, AllNodes, and PerDeviceNodeSelection must be set.",
"sharedCounters": "SharedCounters defines a list of counter sets, each of which has a name and a list of counters available.\n\nThe names of the SharedCounters must be unique in the ResourceSlice.\n\nThe maximum number of counters in all sets is 32.",
}

View file

@ -43,6 +43,8 @@ func (m *DeviceTaintRuleList) Reset() { *m = DeviceTaintRuleList{} }
func (m *DeviceTaintRuleSpec) Reset() { *m = DeviceTaintRuleSpec{} }
func (m *DeviceTaintRuleStatus) Reset() { *m = DeviceTaintRuleStatus{} }
func (m *DeviceTaintSelector) Reset() { *m = DeviceTaintSelector{} }
func (m *CELDeviceSelector) Marshal() (dAtA []byte, err error) {
@ -178,6 +180,16 @@ func (m *DeviceTaintRule) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
{
size, err := m.Status.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintGenerated(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x1a
{
size, err := m.Spec.MarshalToSizedBuffer(dAtA[:i])
if err != nil {
@ -293,6 +305,43 @@ func (m *DeviceTaintRuleSpec) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
func (m *DeviceTaintRuleStatus) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *DeviceTaintRuleStatus) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *DeviceTaintRuleStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Conditions) > 0 {
for iNdEx := len(m.Conditions) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Conditions[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintGenerated(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0xa
}
}
return len(dAtA) - i, nil
}
func (m *DeviceTaintSelector) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@ -313,20 +362,6 @@ func (m *DeviceTaintSelector) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if len(m.Selectors) > 0 {
for iNdEx := len(m.Selectors) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Selectors[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintGenerated(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x2a
}
}
if m.Device != nil {
i -= len(*m.Device)
copy(dAtA[i:], *m.Device)
@ -348,13 +383,6 @@ func (m *DeviceTaintSelector) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i--
dAtA[i] = 0x12
}
if m.DeviceClassName != nil {
i -= len(*m.DeviceClassName)
copy(dAtA[i:], *m.DeviceClassName)
i = encodeVarintGenerated(dAtA, i, uint64(len(*m.DeviceClassName)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
@ -422,6 +450,8 @@ func (m *DeviceTaintRule) Size() (n int) {
n += 1 + l + sovGenerated(uint64(l))
l = m.Spec.Size()
n += 1 + l + sovGenerated(uint64(l))
l = m.Status.Size()
n += 1 + l + sovGenerated(uint64(l))
return n
}
@ -457,16 +487,27 @@ func (m *DeviceTaintRuleSpec) Size() (n int) {
return n
}
func (m *DeviceTaintRuleStatus) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if len(m.Conditions) > 0 {
for _, e := range m.Conditions {
l = e.Size()
n += 1 + l + sovGenerated(uint64(l))
}
}
return n
}
func (m *DeviceTaintSelector) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.DeviceClassName != nil {
l = len(*m.DeviceClassName)
n += 1 + l + sovGenerated(uint64(l))
}
if m.Driver != nil {
l = len(*m.Driver)
n += 1 + l + sovGenerated(uint64(l))
@ -479,12 +520,6 @@ func (m *DeviceTaintSelector) Size() (n int) {
l = len(*m.Device)
n += 1 + l + sovGenerated(uint64(l))
}
if len(m.Selectors) > 0 {
for _, e := range m.Selectors {
l = e.Size()
n += 1 + l + sovGenerated(uint64(l))
}
}
return n
}
@ -521,6 +556,7 @@ func (this *DeviceTaintRule) String() string {
s := strings.Join([]string{`&DeviceTaintRule{`,
`ObjectMeta:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.ObjectMeta), "ObjectMeta", "v1.ObjectMeta", 1), `&`, ``, 1) + `,`,
`Spec:` + strings.Replace(strings.Replace(this.Spec.String(), "DeviceTaintRuleSpec", "DeviceTaintRuleSpec", 1), `&`, ``, 1) + `,`,
`Status:` + strings.Replace(strings.Replace(this.Status.String(), "DeviceTaintRuleStatus", "DeviceTaintRuleStatus", 1), `&`, ``, 1) + `,`,
`}`,
}, "")
return s
@ -552,21 +588,29 @@ func (this *DeviceTaintRuleSpec) String() string {
}, "")
return s
}
func (this *DeviceTaintRuleStatus) String() string {
if this == nil {
return "nil"
}
repeatedStringForConditions := "[]Condition{"
for _, f := range this.Conditions {
repeatedStringForConditions += fmt.Sprintf("%v", f) + ","
}
repeatedStringForConditions += "}"
s := strings.Join([]string{`&DeviceTaintRuleStatus{`,
`Conditions:` + repeatedStringForConditions + `,`,
`}`,
}, "")
return s
}
func (this *DeviceTaintSelector) String() string {
if this == nil {
return "nil"
}
repeatedStringForSelectors := "[]DeviceSelector{"
for _, f := range this.Selectors {
repeatedStringForSelectors += strings.Replace(strings.Replace(f.String(), "DeviceSelector", "DeviceSelector", 1), `&`, ``, 1) + ","
}
repeatedStringForSelectors += "}"
s := strings.Join([]string{`&DeviceTaintSelector{`,
`DeviceClassName:` + valueToStringGenerated(this.DeviceClassName) + `,`,
`Driver:` + valueToStringGenerated(this.Driver) + `,`,
`Pool:` + valueToStringGenerated(this.Pool) + `,`,
`Device:` + valueToStringGenerated(this.Device) + `,`,
`Selectors:` + repeatedStringForSelectors + `,`,
`}`,
}, "")
return s
@ -1024,6 +1068,39 @@ func (m *DeviceTaintRule) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
case 3:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Status", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := m.Status.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
@ -1281,6 +1358,90 @@ func (m *DeviceTaintRuleSpec) Unmarshal(dAtA []byte) error {
}
return nil
}
func (m *DeviceTaintRuleStatus) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: DeviceTaintRuleStatus: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: DeviceTaintRuleStatus: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Conditions", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Conditions = append(m.Conditions, v1.Condition{})
if err := m.Conditions[len(m.Conditions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthGenerated
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *DeviceTaintSelector) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
@ -1310,39 +1471,6 @@ func (m *DeviceTaintSelector) Unmarshal(dAtA []byte) error {
return fmt.Errorf("proto: DeviceTaintSelector: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field DeviceClassName", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
s := string(dAtA[iNdEx:postIndex])
m.DeviceClassName = &s
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Driver", wireType)
@ -1442,40 +1570,6 @@ func (m *DeviceTaintSelector) Unmarshal(dAtA []byte) error {
s := string(dAtA[iNdEx:postIndex])
m.Device = &s
iNdEx = postIndex
case 5:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Selectors", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthGenerated
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthGenerated
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Selectors = append(m.Selectors, DeviceSelector{})
if err := m.Selectors[len(m.Selectors)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])

View file

@ -114,8 +114,10 @@ message DeviceTaint {
// The effect of the taint on claims that do not tolerate the taint
// and through such claims on the pods using them.
// Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here.
//
// Valid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here. More effects may get added in the future.
// Consumers must treat unknown effects like None.
//
// +required
optional string effect = 3;
@ -139,6 +141,11 @@ message DeviceTaintRule {
//
// Changing the spec automatically increments the metadata.generation number.
optional DeviceTaintRuleSpec spec = 2;
// Status provides information about what was requested in the spec.
//
// +optional
optional DeviceTaintRuleStatus status = 3;
}
// DeviceTaintRuleList is a collection of DeviceTaintRules.
@ -154,7 +161,7 @@ message DeviceTaintRuleList {
// DeviceTaintRuleSpec specifies the selector and one taint.
message DeviceTaintRuleSpec {
// DeviceSelector defines which device(s) the taint is applied to.
// All selector criteria must be satified for a device to
// All selector criteria must be satisfied for a device to
// match. The empty selector matches all devices. Without
// a selector, no devices are matches.
//
@ -167,17 +174,41 @@ message DeviceTaintRuleSpec {
optional DeviceTaint taint = 2;
}
// DeviceTaintRuleStatus provides information about an on-going pod eviction.
message DeviceTaintRuleStatus {
// Conditions provide information about the state of the DeviceTaintRule
// and the cluster at some point in time,
// in a machine-readable and human-readable format.
//
// The following condition is currently defined as part of this API, more may
// get added:
// - Type: EvictionInProgress
// - Status: True if there are currently pods which need to be evicted, False otherwise
// (includes the effects which don't cause eviction).
// - Reason: not specified, may change
// - Message: includes information about number of pending pods and already evicted pods
// in a human-readable format, updated periodically, may change
//
// For `effect: None`, the condition above gets set once for each change to
// the spec, with the message containing information about what would happen
// if the effect was `NoExecute`. This feedback can be used to decide whether
// changing the effect to `NoExecute` will work as intended. It only gets
// set once to avoid having to constantly update the status.
//
// Must have 8 or fewer entries.
//
// +optional
// +listType=map
// +listMapKey=type
// +patchStrategy=merge
// +patchMergeKey=type
repeated .k8s.io.apimachinery.pkg.apis.meta.v1.Condition conditions = 1;
}
// DeviceTaintSelector defines which device(s) a DeviceTaintRule applies to.
// The empty selector matches all devices. Without a selector, no devices
// are matched.
message DeviceTaintSelector {
// If DeviceClassName is set, the selectors defined there must be
// satisfied by a device to be selected. This field corresponds
// to class.metadata.name.
//
// +optional
optional string deviceClassName = 1;
// If driver is set, only devices from that driver are selected.
// This fields corresponds to slice.spec.driver.
//
@ -204,13 +235,5 @@ message DeviceTaintSelector {
//
// +optional
optional string device = 4;
// Selectors contains the same selection criteria as a ResourceClaim.
// Currently, CEL expressions are supported. All of these selectors
// must be satisfied.
//
// +optional
// +listType=atomic
repeated DeviceSelector selectors = 5;
}

View file

@ -33,4 +33,6 @@ func (*DeviceTaintRuleList) ProtoMessage() {}
func (*DeviceTaintRuleSpec) ProtoMessage() {}
func (*DeviceTaintRuleStatus) ProtoMessage() {}
func (*DeviceTaintSelector) ProtoMessage() {}

View file

@ -134,8 +134,10 @@ type DeviceTaint struct {
// The effect of the taint on claims that do not tolerate the taint
// and through such claims on the pods using them.
// Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here.
//
// Valid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here. More effects may get added in the future.
// Consumers must treat unknown effects like None.
//
// +required
Effect DeviceTaintEffect `json:"effect" protobuf:"bytes,3,name=effect,casttype=DeviceTaintEffect"`
@ -144,6 +146,14 @@ type DeviceTaint struct {
//
// Implementing PreferNoSchedule would depend on a scoring solution for DRA.
// It might get added as part of that.
//
// A possible future new effect is NoExecuteWithPodDisruptionBudget:
// honor the pod disruption budget instead of simply deleting pods.
// This is currently undecided, it could also be a separate field.
//
// Validation must be prepared to allow unknown enums in stored objects,
// which will enable adding new enums within a single release without
// ratcheting.
// TimeAdded represents the time at which the taint was added.
// Added automatically during create or update if not set.
@ -162,6 +172,9 @@ type DeviceTaint struct {
type DeviceTaintEffect string
const (
// No effect, the taint is purely informational.
DeviceTaintEffectNone DeviceTaintEffect = "None"
// Do not allow new pods to schedule which use a tainted device unless they tolerate the taint,
// but allow all pods submitted to Kubelet without going through the scheduler
// to start, and allow all already-running pods to continue running.
@ -190,18 +203,16 @@ type DeviceTaintRule struct {
// Changing the spec automatically increments the metadata.generation number.
Spec DeviceTaintRuleSpec `json:"spec" protobuf:"bytes,2,name=spec"`
// ^^^
// A spec gets added because adding a status seems likely.
// Such a status could provide feedback on applying the
// eviction and/or statistics (number of matching devices,
// affected allocated claims, pods remaining to be evicted,
// etc.).
// Status provides information about what was requested in the spec.
//
// +optional
Status DeviceTaintRuleStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
// DeviceTaintRuleSpec specifies the selector and one taint.
type DeviceTaintRuleSpec struct {
// DeviceSelector defines which device(s) the taint is applied to.
// All selector criteria must be satified for a device to
// All selector criteria must be satisfied for a device to
// match. The empty selector matches all devices. Without
// a selector, no devices are matches.
//
@ -223,7 +234,12 @@ type DeviceTaintSelector struct {
// to class.metadata.name.
//
// +optional
DeviceClassName *string `json:"deviceClassName,omitempty" protobuf:"bytes,1,opt,name=deviceClassName"`
//
// Tombstoned since 1.35 because it turned out that supporting this in all cases
// would depend on copying the device attributes into the ResourceClaim allocation
// result. Without that the eviction controller cannot evaluate these CEL expressions.
//
// DeviceClassName *string `json:"deviceClassName,omitempty" protobuf:"bytes,1,opt,name=deviceClassName"`
// If driver is set, only devices from that driver are selected.
// This fields corresponds to slice.spec.driver.
@ -258,9 +274,51 @@ type DeviceTaintSelector struct {
//
// +optional
// +listType=atomic
Selectors []DeviceSelector `json:"selectors,omitempty" protobuf:"bytes,5,rep,name=selectors"`
//
// Tombstoned since 1.35 because it turned out that supporting this in all cases
// would depend on copying the device attributes into the ResourceClaim allocation
// result. Without that the eviction controller cannot evaluate these CEL expressions.
//
// Selectors []DeviceSelector `json:"selectors,omitempty" protobuf:"bytes,5,rep,name=selectors"`
}
// DeviceTaintRuleStatus provides information about an on-going pod eviction.
type DeviceTaintRuleStatus struct {
// Conditions provide information about the state of the DeviceTaintRule
// and the cluster at some point in time,
// in a machine-readable and human-readable format.
//
// The following condition is currently defined as part of this API, more may
// get added:
// - Type: EvictionInProgress
// - Status: True if there are currently pods which need to be evicted, False otherwise
// (includes the effects which don't cause eviction).
// - Reason: not specified, may change
// - Message: includes information about number of pending pods and already evicted pods
// in a human-readable format, updated periodically, may change
//
// For `effect: None`, the condition above gets set once for each change to
// the spec, with the message containing information about what would happen
// if the effect was `NoExecute`. This feedback can be used to decide whether
// changing the effect to `NoExecute` will work as intended. It only gets
// set once to avoid having to constantly update the status.
//
// Must have 8 or fewer entries.
//
// +optional
// +listType=map
// +listMapKey=type
// +patchStrategy=merge
// +patchMergeKey=type
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`
}
// DeviceTaintRuleStatusMaxConditions is the maximum number of conditions in DeviceTaintRuleStatus.
const DeviceTaintRuleStatusMaxConditions = 8
// DeviceTaintConditionEvictionInProgress is the publicly documented condition type for the DeviceTaintRuleStatus.
const DeviceTaintConditionEvictionInProgress = "EvictionInProgress"
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +k8s:prerelease-lifecycle-gen:introduced=1.33

View file

@ -49,7 +49,7 @@ var map_DeviceTaint = map[string]string{
"": "The device this taint is attached to has the \"effect\" on any claim which does not tolerate the taint and, through the claim, to pods using the claim.",
"key": "The taint key to be applied to a device. Must be a label name.",
"value": "The taint value corresponding to the taint key. Must be a label value.",
"effect": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.",
"effect": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.",
"timeAdded": "TimeAdded represents the time at which the taint was added. Added automatically during create or update if not set.",
}
@ -61,6 +61,7 @@ var map_DeviceTaintRule = map[string]string{
"": "DeviceTaintRule adds one taint to all devices which match the selector. This has the same effect as if the taint was specified directly in the ResourceSlice by the DRA driver.",
"metadata": "Standard object metadata",
"spec": "Spec specifies the selector and one taint.\n\nChanging the spec automatically increments the metadata.generation number.",
"status": "Status provides information about what was requested in the spec.",
}
func (DeviceTaintRule) SwaggerDoc() map[string]string {
@ -79,7 +80,7 @@ func (DeviceTaintRuleList) SwaggerDoc() map[string]string {
var map_DeviceTaintRuleSpec = map[string]string{
"": "DeviceTaintRuleSpec specifies the selector and one taint.",
"deviceSelector": "DeviceSelector defines which device(s) the taint is applied to. All selector criteria must be satified for a device to match. The empty selector matches all devices. Without a selector, no devices are matches.",
"deviceSelector": "DeviceSelector defines which device(s) the taint is applied to. All selector criteria must be satisfied for a device to match. The empty selector matches all devices. Without a selector, no devices are matches.",
"taint": "The taint that gets applied to matching devices.",
}
@ -87,13 +88,20 @@ func (DeviceTaintRuleSpec) SwaggerDoc() map[string]string {
return map_DeviceTaintRuleSpec
}
var map_DeviceTaintRuleStatus = map[string]string{
"": "DeviceTaintRuleStatus provides information about an on-going pod eviction.",
"conditions": "Conditions provide information about the state of the DeviceTaintRule and the cluster at some point in time, in a machine-readable and human-readable format.\n\nThe following condition is currently defined as part of this API, more may get added: - Type: EvictionInProgress - Status: True if there are currently pods which need to be evicted, False otherwise\n (includes the effects which don't cause eviction).\n- Reason: not specified, may change - Message: includes information about number of pending pods and already evicted pods\n in a human-readable format, updated periodically, may change\n\nFor `effect: None`, the condition above gets set once for each change to the spec, with the message containing information about what would happen if the effect was `NoExecute`. This feedback can be used to decide whether changing the effect to `NoExecute` will work as intended. It only gets set once to avoid having to constantly update the status.\n\nMust have 8 or fewer entries.",
}
func (DeviceTaintRuleStatus) SwaggerDoc() map[string]string {
return map_DeviceTaintRuleStatus
}
var map_DeviceTaintSelector = map[string]string{
"": "DeviceTaintSelector defines which device(s) a DeviceTaintRule applies to. The empty selector matches all devices. Without a selector, no devices are matched.",
"deviceClassName": "If DeviceClassName is set, the selectors defined there must be satisfied by a device to be selected. This field corresponds to class.metadata.name.",
"driver": "If driver is set, only devices from that driver are selected. This fields corresponds to slice.spec.driver.",
"pool": "If pool is set, only devices in that pool are selected.\n\nAlso setting the driver name may be useful to avoid ambiguity when different drivers use the same pool name, but this is not required because selecting pools from different drivers may also be useful, for example when drivers with node-local devices use the node name as their pool name.",
"device": "If device is set, only devices with that name are selected. This field corresponds to slice.spec.devices[].name.\n\nSetting also driver and pool may be required to avoid ambiguity, but is not required.",
"selectors": "Selectors contains the same selection criteria as a ResourceClaim. Currently, CEL expressions are supported. All of these selectors must be satisfied.",
"": "DeviceTaintSelector defines which device(s) a DeviceTaintRule applies to. The empty selector matches all devices. Without a selector, no devices are matched.",
"driver": "If driver is set, only devices from that driver are selected. This fields corresponds to slice.spec.driver.",
"pool": "If pool is set, only devices in that pool are selected.\n\nAlso setting the driver name may be useful to avoid ambiguity when different drivers use the same pool name, but this is not required because selecting pools from different drivers may also be useful, for example when drivers with node-local devices use the node name as their pool name.",
"device": "If device is set, only devices with that name are selected. This field corresponds to slice.spec.devices[].name.\n\nSetting also driver and pool may be required to avoid ambiguity, but is not required.",
}
func (DeviceTaintSelector) SwaggerDoc() map[string]string {

View file

@ -22,6 +22,7 @@ limitations under the License.
package v1alpha3
import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
@ -88,6 +89,7 @@ func (in *DeviceTaintRule) DeepCopyInto(out *DeviceTaintRule) {
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
@ -164,14 +166,32 @@ func (in *DeviceTaintRuleSpec) DeepCopy() *DeviceTaintRuleSpec {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeviceTaintRuleStatus) DeepCopyInto(out *DeviceTaintRuleStatus) {
*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])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeviceTaintRuleStatus.
func (in *DeviceTaintRuleStatus) DeepCopy() *DeviceTaintRuleStatus {
if in == nil {
return nil
}
out := new(DeviceTaintRuleStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DeviceTaintSelector) DeepCopyInto(out *DeviceTaintSelector) {
*out = *in
if in.DeviceClassName != nil {
in, out := &in.DeviceClassName, &out.DeviceClassName
*out = new(string)
**out = **in
}
if in.Driver != nil {
in, out := &in.Driver, &out.Driver
*out = new(string)
@ -187,13 +207,6 @@ func (in *DeviceTaintSelector) DeepCopyInto(out *DeviceTaintSelector) {
*out = new(string)
**out = **in
}
if in.Selectors != nil {
in, out := &in.Selectors, &out.Selectors
*out = make([]DeviceSelector, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}

View file

@ -51,6 +51,11 @@ func (in DeviceTaintRuleSpec) OpenAPIModelName() string {
return "io.k8s.api.resource.v1alpha3.DeviceTaintRuleSpec"
}
// OpenAPIModelName returns the OpenAPI model name for this type.
func (in DeviceTaintRuleStatus) OpenAPIModelName() string {
return "io.k8s.api.resource.v1alpha3.DeviceTaintRuleStatus"
}
// OpenAPIModelName returns the OpenAPI model name for this type.
func (in DeviceTaintSelector) OpenAPIModelName() string {
return "io.k8s.api.resource.v1alpha3.DeviceTaintSelector"

View file

@ -185,7 +185,9 @@ message BasicDevice {
// If specified, these are the driver-defined taints.
//
// The maximum number of taints is 4.
// The maximum number of taints is 16. If taints are set for
// any device in a ResourceSlice, then the maximum number of
// allowed devices per ResourceSlice is 64 instead of 128.
//
// This is an alpha field and requires enabling the DRADeviceTaints
// feature gate.
@ -1241,8 +1243,10 @@ message DeviceTaint {
// The effect of the taint on claims that do not tolerate the taint
// and through such claims on the pods using them.
// Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here.
//
// Valid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here. More effects may get added in the future.
// Consumers must treat unknown effects like None.
//
// +required
// +k8s:required
@ -1672,7 +1676,7 @@ message ResourceSliceSpec {
// Devices lists some or all of the devices in this pool.
//
// Must not have more than 128 entries.
// Must not have more than 128 entries. If any device uses taints the limit is 64.
//
// +optional
// +listType=atomic

View file

@ -149,7 +149,7 @@ type ResourceSliceSpec struct {
// Devices lists some or all of the devices in this pool.
//
// Must not have more than 128 entries.
// Must not have more than 128 entries. If any device uses taints the limit is 64.
//
// +optional
// +listType=atomic
@ -258,6 +258,7 @@ type ResourcePool struct {
const ResourceSliceMaxSharedCapacity = 128
const ResourceSliceMaxDevices = 128
const ResourceSliceMaxDevicesWithTaints = 64
const PoolNameMaxLength = validation.DNS1123SubdomainMaxLength // Same as for a single node name.
const BindingConditionsMaxSize = 4
const BindingFailureConditionsMaxSize = 4
@ -345,7 +346,9 @@ type BasicDevice struct {
// If specified, these are the driver-defined taints.
//
// The maximum number of taints is 4.
// The maximum number of taints is 16. If taints are set for
// any device in a ResourceSlice, then the maximum number of
// allowed devices per ResourceSlice is 64 instead of 128.
//
// This is an alpha field and requires enabling the DRADeviceTaints
// feature gate.
@ -622,8 +625,8 @@ type DeviceAttribute struct {
// DeviceAttributeMaxValueLength is the maximum length of a string or version attribute value.
const DeviceAttributeMaxValueLength = 64
// DeviceTaintsMaxLength is the maximum number of taints per device.
const DeviceTaintsMaxLength = 4
// DeviceTaintsMaxLength is the maximum number of taints per Device.
const DeviceTaintsMaxLength = 16
// The device this taint is attached to has the "effect" on
// any claim which does not tolerate the taint and, through the claim,
@ -645,8 +648,10 @@ type DeviceTaint struct {
// The effect of the taint on claims that do not tolerate the taint
// and through such claims on the pods using them.
// Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here.
//
// Valid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here. More effects may get added in the future.
// Consumers must treat unknown effects like None.
//
// +required
// +k8s:required
@ -656,6 +661,14 @@ type DeviceTaint struct {
//
// Implementing PreferNoSchedule would depend on a scoring solution for DRA.
// It might get added as part of that.
//
// A possible future new effect is NoExecuteWithPodDisruptionBudget:
// honor the pod disruption budget instead of simply deleting pods.
// This is currently undecided, it could also be a separate field.
//
// Validation must be prepared to allow unknown enums in stored objects,
// which will enable adding new enums within a single release without
// ratcheting.
// TimeAdded represents the time at which the taint was added.
// Added automatically during create or update if not set.
@ -675,6 +688,9 @@ type DeviceTaint struct {
type DeviceTaintEffect string
const (
// No effect, the taint is purely informational.
DeviceTaintEffectNone DeviceTaintEffect = "None"
// Do not allow new pods to schedule which use a tainted device unless they tolerate the taint,
// but allow all pods submitted to Kubelet without going through the scheduler
// to start, and allow all already-running pods to continue running.

View file

@ -61,7 +61,7 @@ var map_BasicDevice = map[string]string{
"nodeName": "NodeName identifies the node where the device is available.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set.",
"nodeSelector": "NodeSelector defines the nodes where the device is available.\n\nMust use exactly one term.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set.",
"allNodes": "AllNodes indicates that all nodes have access to the device.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set.",
"taints": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"taints": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"bindsToNode": "BindsToNode indicates if the usage of an allocation involving this device has to be limited to exactly the node that was chosen when allocating the claim. If set to true, the scheduler will set the ResourceClaim.Status.Allocation.NodeSelector to match the node where the allocation was made.\n\nThis is an alpha field and requires enabling the DRADeviceBindingConditions and DRAResourceClaimDeviceStatus feature gates.",
"bindingConditions": "BindingConditions defines the conditions for proceeding with binding. All of these conditions must be set in the per-device status conditions with a value of True to proceed with binding the pod to the node while scheduling the pod.\n\nThe maximum number of binding conditions is 4.\n\nThe conditions must be a valid condition type string.\n\nThis is an alpha field and requires enabling the DRADeviceBindingConditions and DRAResourceClaimDeviceStatus feature gates.",
"bindingFailureConditions": "BindingFailureConditions defines the conditions for binding failure. They may be set in the per-device status conditions. If any is true, a binding failure occurred.\n\nThe maximum number of binding failure conditions is 4.\n\nThe conditions must be a valid condition type string.\n\nThis is an alpha field and requires enabling the DRADeviceBindingConditions and DRAResourceClaimDeviceStatus feature gates.",
@ -335,7 +335,7 @@ var map_DeviceTaint = map[string]string{
"": "The device this taint is attached to has the \"effect\" on any claim which does not tolerate the taint and, through the claim, to pods using the claim.",
"key": "The taint key to be applied to a device. Must be a label name.",
"value": "The taint value corresponding to the taint key. Must be a label value.",
"effect": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.",
"effect": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.",
"timeAdded": "TimeAdded represents the time at which the taint was added. Added automatically during create or update if not set.",
}
@ -498,7 +498,7 @@ var map_ResourceSliceSpec = map[string]string{
"nodeName": "NodeName identifies the node which provides the resources in this pool. A field selector can be used to list only ResourceSlice objects belonging to a certain node.\n\nThis field can be used to limit access from nodes to ResourceSlices with the same node name. It also indicates to autoscalers that adding new nodes of the same type as some old node might also make new resources available.\n\nExactly one of NodeName, NodeSelector, AllNodes, and PerDeviceNodeSelection must be set. This field is immutable.",
"nodeSelector": "NodeSelector defines which nodes have access to the resources in the pool, when that pool is not limited to a single node.\n\nMust use exactly one term.\n\nExactly one of NodeName, NodeSelector, AllNodes, and PerDeviceNodeSelection must be set.",
"allNodes": "AllNodes indicates that all nodes have access to the resources in the pool.\n\nExactly one of NodeName, NodeSelector, AllNodes, and PerDeviceNodeSelection must be set.",
"devices": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.",
"devices": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints the limit is 64.",
"perDeviceNodeSelection": "PerDeviceNodeSelection defines whether the access from nodes to resources in the pool is set on the ResourceSlice level or on each device. If it is set to true, every device defined the ResourceSlice must specify this individually.\n\nExactly one of NodeName, NodeSelector, AllNodes, and PerDeviceNodeSelection must be set.",
"sharedCounters": "SharedCounters defines a list of counter sets, each of which has a name and a list of counters available.\n\nThe names of the SharedCounters must be unique in the ResourceSlice.\n\nThe maximum number of SharedCounters is 32.",
}

View file

@ -395,7 +395,9 @@ message Device {
// If specified, these are the driver-defined taints.
//
// The maximum number of taints is 4.
// The maximum number of taints is 16. If taints are set for
// any device in a ResourceSlice, then the maximum number of
// allowed devices per ResourceSlice is 64 instead of 128.
//
// This is an alpha field and requires enabling the DRADeviceTaints
// feature gate.
@ -1116,8 +1118,10 @@ message DeviceTaint {
// The effect of the taint on claims that do not tolerate the taint
// and through such claims on the pods using them.
// Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here.
//
// Valid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here. More effects may get added in the future.
// Consumers must treat unknown effects like None.
//
// +required
// +k8s:required
@ -1658,7 +1662,7 @@ message ResourceSliceSpec {
// Devices lists some or all of the devices in this pool.
//
// Must not have more than 128 entries.
// Must not have more than 128 entries. If any device uses taints the limit is 64.
//
// +optional
// +listType=atomic

View file

@ -149,7 +149,7 @@ type ResourceSliceSpec struct {
// Devices lists some or all of the devices in this pool.
//
// Must not have more than 128 entries.
// Must not have more than 128 entries. If any device uses taints the limit is 64.
//
// +optional
// +listType=atomic
@ -250,6 +250,7 @@ type ResourcePool struct {
const ResourceSliceMaxSharedCapacity = 128
const ResourceSliceMaxDevices = 128
const ResourceSliceMaxDevicesWithTaints = 64
const PoolNameMaxLength = validation.DNS1123SubdomainMaxLength // Same as for a single node name.
const BindingConditionsMaxSize = 4
const BindingFailureConditionsMaxSize = 4
@ -333,7 +334,9 @@ type Device struct {
// If specified, these are the driver-defined taints.
//
// The maximum number of taints is 4.
// The maximum number of taints is 16. If taints are set for
// any device in a ResourceSlice, then the maximum number of
// allowed devices per ResourceSlice is 64 instead of 128.
//
// This is an alpha field and requires enabling the DRADeviceTaints
// feature gate.
@ -618,8 +621,8 @@ type DeviceAttribute struct {
// DeviceAttributeMaxValueLength is the maximum length of a string or version attribute value.
const DeviceAttributeMaxValueLength = 64
// DeviceTaintsMaxLength is the maximum number of taints per device.
const DeviceTaintsMaxLength = 4
// DeviceTaintsMaxLength is the maximum number of taints per Device.
const DeviceTaintsMaxLength = 16
// The device this taint is attached to has the "effect" on
// any claim which does not tolerate the taint and, through the claim,
@ -641,8 +644,10 @@ type DeviceTaint struct {
// The effect of the taint on claims that do not tolerate the taint
// and through such claims on the pods using them.
// Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here.
//
// Valid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here. More effects may get added in the future.
// Consumers must treat unknown effects like None.
//
// +required
// +k8s:required
@ -652,6 +657,14 @@ type DeviceTaint struct {
//
// Implementing PreferNoSchedule would depend on a scoring solution for DRA.
// It might get added as part of that.
//
// A possible future new effect is NoExecuteWithPodDisruptionBudget:
// honor the pod disruption budget instead of simply deleting pods.
// This is currently undecided, it could also be a separate field.
//
// Validation must be prepared to allow unknown enums in stored objects,
// which will enable adding new enums within a single release without
// ratcheting.
// TimeAdded represents the time at which the taint was added.
// Added automatically during create or update if not set.
@ -671,6 +684,9 @@ type DeviceTaint struct {
type DeviceTaintEffect string
const (
// No effect, the taint is purely informational.
DeviceTaintEffectNone DeviceTaintEffect = "None"
// Do not allow new pods to schedule which use a tainted device unless they tolerate the taint,
// but allow all pods submitted to Kubelet without going through the scheduler
// to start, and allow all already-running pods to continue running.

View file

@ -121,7 +121,7 @@ var map_Device = map[string]string{
"nodeName": "NodeName identifies the node where the device is available.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set.",
"nodeSelector": "NodeSelector defines the nodes where the device is available.\n\nMust use exactly one term.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set.",
"allNodes": "AllNodes indicates that all nodes have access to the device.\n\nMust only be set if Spec.PerDeviceNodeSelection is set to true. At most one of NodeName, NodeSelector and AllNodes can be set.",
"taints": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 4.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"taints": "If specified, these are the driver-defined taints.\n\nThe maximum number of taints is 16. If taints are set for any device in a ResourceSlice, then the maximum number of allowed devices per ResourceSlice is 64 instead of 128.\n\nThis is an alpha field and requires enabling the DRADeviceTaints feature gate.",
"bindsToNode": "BindsToNode indicates if the usage of an allocation involving this device has to be limited to exactly the node that was chosen when allocating the claim. If set to true, the scheduler will set the ResourceClaim.Status.Allocation.NodeSelector to match the node where the allocation was made.\n\nThis is an alpha field and requires enabling the DRADeviceBindingConditions and DRAResourceClaimDeviceStatus feature gates.",
"bindingConditions": "BindingConditions defines the conditions for proceeding with binding. All of these conditions must be set in the per-device status conditions with a value of True to proceed with binding the pod to the node while scheduling the pod.\n\nThe maximum number of binding conditions is 4.\n\nThe conditions must be a valid condition type string.\n\nThis is an alpha field and requires enabling the DRADeviceBindingConditions and DRAResourceClaimDeviceStatus feature gates.",
"bindingFailureConditions": "BindingFailureConditions defines the conditions for binding failure. They may be set in the per-device status conditions. If any is set to \"True\", a binding failure occurred.\n\nThe maximum number of binding failure conditions is 4.\n\nThe conditions must be a valid condition type string.\n\nThis is an alpha field and requires enabling the DRADeviceBindingConditions and DRAResourceClaimDeviceStatus feature gates.",
@ -320,7 +320,7 @@ var map_DeviceTaint = map[string]string{
"": "The device this taint is attached to has the \"effect\" on any claim which does not tolerate the taint and, through the claim, to pods using the claim.",
"key": "The taint key to be applied to a device. Must be a label name.",
"value": "The taint value corresponding to the taint key. Must be a label value.",
"effect": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them. Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here.",
"effect": "The effect of the taint on claims that do not tolerate the taint and through such claims on the pods using them.\n\nValid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for nodes is not valid here. More effects may get added in the future. Consumers must treat unknown effects like None.",
"timeAdded": "TimeAdded represents the time at which the taint was added. Added automatically during create or update if not set.",
}
@ -498,7 +498,7 @@ var map_ResourceSliceSpec = map[string]string{
"nodeName": "NodeName identifies the node which provides the resources in this pool. A field selector can be used to list only ResourceSlice objects belonging to a certain node.\n\nThis field can be used to limit access from nodes to ResourceSlices with the same node name. It also indicates to autoscalers that adding new nodes of the same type as some old node might also make new resources available.\n\nExactly one of NodeName, NodeSelector, AllNodes, and PerDeviceNodeSelection must be set. This field is immutable.",
"nodeSelector": "NodeSelector defines which nodes have access to the resources in the pool, when that pool is not limited to a single node.\n\nMust use exactly one term.\n\nExactly one of NodeName, NodeSelector, AllNodes, and PerDeviceNodeSelection must be set.",
"allNodes": "AllNodes indicates that all nodes have access to the resources in the pool.\n\nExactly one of NodeName, NodeSelector, AllNodes, and PerDeviceNodeSelection must be set.",
"devices": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries.",
"devices": "Devices lists some or all of the devices in this pool.\n\nMust not have more than 128 entries. If any device uses taints the limit is 64.",
"perDeviceNodeSelection": "PerDeviceNodeSelection defines whether the access from nodes to resources in the pool is set on the ResourceSlice level or on each device. If it is set to true, every device defined the ResourceSlice must specify this individually.\n\nExactly one of NodeName, NodeSelector, AllNodes, and PerDeviceNodeSelection must be set.",
"sharedCounters": "SharedCounters defines a list of counter sets, each of which has a name and a list of counters available.\n\nThe names of the SharedCounters must be unique in the ResourceSlice.\n\nThe maximum number of counters in all sets is 32.",
}

View file

@ -45,17 +45,9 @@
},
"spec": {
"deviceSelector": {
"deviceClassName": "deviceClassNameValue",
"driver": "driverValue",
"pool": "poolValue",
"device": "deviceValue",
"selectors": [
{
"cel": {
"expression": "expressionValue"
}
}
]
"device": "deviceValue"
},
"taint": {
"key": "keyValue",
@ -63,5 +55,17 @@
"effect": "effectValue",
"timeAdded": "2004-01-01T01:01:01Z"
}
},
"status": {
"conditions": [
{
"type": "typeValue",
"status": "statusValue",
"observedGeneration": 3,
"lastTransitionTime": "2004-01-01T01:01:01Z",
"reason": "reasonValue",
"message": "messageValue"
}
]
}
}

View file

@ -35,14 +35,18 @@ metadata:
spec:
deviceSelector:
device: deviceValue
deviceClassName: deviceClassNameValue
driver: driverValue
pool: poolValue
selectors:
- cel:
expression: expressionValue
taint:
effect: effectValue
key: keyValue
timeAdded: "2004-01-01T01:01:01Z"
value: valueValue
status:
conditions:
- lastTransitionTime: "2004-01-01T01:01:01Z"
message: messageValue
observedGeneration: 3
reason: reasonValue
status: statusValue
type: typeValue

View file

@ -0,0 +1,60 @@
{
"kind": "DeviceTaintRule",
"apiVersion": "resource.k8s.io/v1alpha3",
"metadata": {
"name": "nameValue",
"generateName": "generateNameValue",
"namespace": "namespaceValue",
"selfLink": "selfLinkValue",
"uid": "uidValue",
"resourceVersion": "resourceVersionValue",
"generation": 7,
"creationTimestamp": "2008-01-01T01:01:01Z",
"deletionTimestamp": "2009-01-01T01:01:01Z",
"deletionGracePeriodSeconds": 10,
"labels": {
"labelsKey": "labelsValue"
},
"annotations": {
"annotationsKey": "annotationsValue"
},
"ownerReferences": [
{
"apiVersion": "apiVersionValue",
"kind": "kindValue",
"name": "nameValue",
"uid": "uidValue",
"controller": true,
"blockOwnerDeletion": true
}
],
"finalizers": [
"finalizersValue"
],
"managedFields": [
{
"manager": "managerValue",
"operation": "operationValue",
"apiVersion": "apiVersionValue",
"time": "2004-01-01T01:01:01Z",
"fieldsType": "fieldsTypeValue",
"fieldsV1": {},
"subresource": "subresourceValue"
}
]
},
"spec": {
"deviceSelector": {
"driver": "driverValue",
"pool": "poolValue",
"device": "deviceValue"
},
"taint": {
"key": "keyValue",
"value": "valueValue",
"effect": "effectValue",
"timeAdded": "2004-01-01T01:01:01Z"
}
},
"status": {}
}

View file

@ -0,0 +1,45 @@
apiVersion: resource.k8s.io/v1alpha3
kind: DeviceTaintRule
metadata:
annotations:
annotationsKey: annotationsValue
creationTimestamp: "2008-01-01T01:01:01Z"
deletionGracePeriodSeconds: 10
deletionTimestamp: "2009-01-01T01:01:01Z"
finalizers:
- finalizersValue
generateName: generateNameValue
generation: 7
labels:
labelsKey: labelsValue
managedFields:
- apiVersion: apiVersionValue
fieldsType: fieldsTypeValue
fieldsV1: {}
manager: managerValue
operation: operationValue
subresource: subresourceValue
time: "2004-01-01T01:01:01Z"
name: nameValue
namespace: namespaceValue
ownerReferences:
- apiVersion: apiVersionValue
blockOwnerDeletion: true
controller: true
kind: kindValue
name: nameValue
uid: uidValue
resourceVersion: resourceVersionValue
selfLink: selfLinkValue
uid: uidValue
spec:
deviceSelector:
device: deviceValue
driver: driverValue
pool: poolValue
taint:
effect: effectValue
key: keyValue
timeAdded: "2004-01-01T01:01:01Z"
value: valueValue
status: {}

View file

@ -0,0 +1,60 @@
{
"kind": "DeviceTaintRule",
"apiVersion": "resource.k8s.io/v1alpha3",
"metadata": {
"name": "nameValue",
"generateName": "generateNameValue",
"namespace": "namespaceValue",
"selfLink": "selfLinkValue",
"uid": "uidValue",
"resourceVersion": "resourceVersionValue",
"generation": 7,
"creationTimestamp": "2008-01-01T01:01:01Z",
"deletionTimestamp": "2009-01-01T01:01:01Z",
"deletionGracePeriodSeconds": 10,
"labels": {
"labelsKey": "labelsValue"
},
"annotations": {
"annotationsKey": "annotationsValue"
},
"ownerReferences": [
{
"apiVersion": "apiVersionValue",
"kind": "kindValue",
"name": "nameValue",
"uid": "uidValue",
"controller": true,
"blockOwnerDeletion": true
}
],
"finalizers": [
"finalizersValue"
],
"managedFields": [
{
"manager": "managerValue",
"operation": "operationValue",
"apiVersion": "apiVersionValue",
"time": "2004-01-01T01:01:01Z",
"fieldsType": "fieldsTypeValue",
"fieldsV1": {},
"subresource": "subresourceValue"
}
]
},
"spec": {
"deviceSelector": {
"driver": "driverValue",
"pool": "poolValue",
"device": "deviceValue"
},
"taint": {
"key": "keyValue",
"value": "valueValue",
"effect": "effectValue",
"timeAdded": "2004-01-01T01:01:01Z"
}
},
"status": {}
}

View file

@ -0,0 +1,45 @@
apiVersion: resource.k8s.io/v1alpha3
kind: DeviceTaintRule
metadata:
annotations:
annotationsKey: annotationsValue
creationTimestamp: "2008-01-01T01:01:01Z"
deletionGracePeriodSeconds: 10
deletionTimestamp: "2009-01-01T01:01:01Z"
finalizers:
- finalizersValue
generateName: generateNameValue
generation: 7
labels:
labelsKey: labelsValue
managedFields:
- apiVersion: apiVersionValue
fieldsType: fieldsTypeValue
fieldsV1: {}
manager: managerValue
operation: operationValue
subresource: subresourceValue
time: "2004-01-01T01:01:01Z"
name: nameValue
namespace: namespaceValue
ownerReferences:
- apiVersion: apiVersionValue
blockOwnerDeletion: true
controller: true
kind: kindValue
name: nameValue
uid: uidValue
resourceVersion: resourceVersionValue
selfLink: selfLinkValue
uid: uidValue
spec:
deviceSelector:
device: deviceValue
driver: driverValue
pool: poolValue
taint:
effect: effectValue
key: keyValue
timeAdded: "2004-01-01T01:01:01Z"
value: valueValue
status: {}

View file

@ -13486,19 +13486,6 @@ var schemaYAML = typed.YAMLObject(`types:
elementType:
namedType: io.k8s.api.resource.v1.CounterSet
elementRelationship: atomic
- name: io.k8s.api.resource.v1alpha3.CELDeviceSelector
map:
fields:
- name: expression
type:
scalar: string
default: ""
- name: io.k8s.api.resource.v1alpha3.DeviceSelector
map:
fields:
- name: cel
type:
namedType: io.k8s.api.resource.v1alpha3.CELDeviceSelector
- name: io.k8s.api.resource.v1alpha3.DeviceTaint
map:
fields:
@ -13533,6 +13520,10 @@ var schemaYAML = typed.YAMLObject(`types:
type:
namedType: io.k8s.api.resource.v1alpha3.DeviceTaintRuleSpec
default: {}
- name: status
type:
namedType: io.k8s.api.resource.v1alpha3.DeviceTaintRuleStatus
default: {}
- name: io.k8s.api.resource.v1alpha3.DeviceTaintRuleSpec
map:
fields:
@ -13543,27 +13534,29 @@ var schemaYAML = typed.YAMLObject(`types:
type:
namedType: io.k8s.api.resource.v1alpha3.DeviceTaint
default: {}
- name: io.k8s.api.resource.v1alpha3.DeviceTaintRuleStatus
map:
fields:
- name: conditions
type:
list:
elementType:
namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Condition
elementRelationship: associative
keys:
- type
- name: io.k8s.api.resource.v1alpha3.DeviceTaintSelector
map:
fields:
- name: device
type:
scalar: string
- name: deviceClassName
type:
scalar: string
- name: driver
type:
scalar: string
- name: pool
type:
scalar: string
- name: selectors
type:
list:
elementType:
namedType: io.k8s.api.resource.v1alpha3.DeviceSelector
elementRelationship: atomic
- name: io.k8s.api.resource.v1beta1.AllocatedDeviceStatus
map:
fields:

View file

@ -72,7 +72,9 @@ type DeviceApplyConfiguration struct {
AllNodes *bool `json:"allNodes,omitempty"`
// If specified, these are the driver-defined taints.
//
// The maximum number of taints is 4.
// The maximum number of taints is 16. If taints are set for
// any device in a ResourceSlice, then the maximum number of
// allowed devices per ResourceSlice is 64 instead of 128.
//
// This is an alpha field and requires enabling the DRADeviceTaints
// feature gate.

View file

@ -38,8 +38,10 @@ type DeviceTaintApplyConfiguration struct {
Value *string `json:"value,omitempty"`
// The effect of the taint on claims that do not tolerate the taint
// and through such claims on the pods using them.
// Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here.
//
// Valid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here. More effects may get added in the future.
// Consumers must treat unknown effects like None.
Effect *resourcev1.DeviceTaintEffect `json:"effect,omitempty"`
// TimeAdded represents the time at which the taint was added.
// Added automatically during create or update if not set.

View file

@ -62,7 +62,7 @@ type ResourceSliceSpecApplyConfiguration struct {
AllNodes *bool `json:"allNodes,omitempty"`
// Devices lists some or all of the devices in this pool.
//
// Must not have more than 128 entries.
// Must not have more than 128 entries. If any device uses taints the limit is 64.
Devices []DeviceApplyConfiguration `json:"devices,omitempty"`
// PerDeviceNodeSelection defines whether the access from nodes to
// resources in the pool is set on the ResourceSlice level or on each

View file

@ -1,91 +0,0 @@
/*
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
// CELDeviceSelectorApplyConfiguration represents a declarative configuration of the CELDeviceSelector type for use
// with apply.
//
// CELDeviceSelector contains a CEL expression for selecting a device.
type CELDeviceSelectorApplyConfiguration struct {
// Expression is a CEL expression which evaluates a single device. It
// must evaluate to true when the device under consideration satisfies
// the desired criteria, and false when it does not. Any other result
// is an error and causes allocation of devices to abort.
//
// The expression's input is an object named "device", which carries
// the following properties:
// - driver (string): the name of the driver which defines this device.
// - attributes (map[string]object): the device's attributes, grouped by prefix
// (e.g. device.attributes["dra.example.com"] evaluates to an object with all
// of the attributes which were prefixed by "dra.example.com".
// - capacity (map[string]object): the device's capacities, grouped by prefix.
//
// Example: Consider a device with driver="dra.example.com", which exposes
// two attributes named "model" and "ext.example.com/family" and which
// exposes one capacity named "modules". This input to this expression
// would have the following fields:
//
// device.driver
// device.attributes["dra.example.com"].model
// device.attributes["ext.example.com"].family
// device.capacity["dra.example.com"].modules
//
// The device.driver field can be used to check for a specific driver,
// either as a high-level precondition (i.e. you only want to consider
// devices from this driver) or as part of a multi-clause expression
// that is meant to consider devices from different drivers.
//
// The value type of each attribute is defined by the device
// definition, and users who write these expressions must consult the
// documentation for their specific drivers. The value type of each
// capacity is Quantity.
//
// If an unknown prefix is used as a lookup in either device.attributes
// or device.capacity, an empty map will be returned. Any reference to
// an unknown field will cause an evaluation error and allocation to
// abort.
//
// A robust expression should check for the existence of attributes
// before referencing them.
//
// For ease of use, the cel.bind() function is enabled, and can be used
// to simplify expressions that access multiple attributes with the
// same domain. For example:
//
// cel.bind(dra, device.attributes["dra.example.com"], dra.someBool && dra.anotherBool)
//
// The length of the expression must be smaller or equal to 10 Ki. The
// cost of evaluating it is also limited based on the estimated number
// of logical steps.
Expression *string `json:"expression,omitempty"`
}
// CELDeviceSelectorApplyConfiguration constructs a declarative configuration of the CELDeviceSelector type for use with
// apply.
func CELDeviceSelector() *CELDeviceSelectorApplyConfiguration {
return &CELDeviceSelectorApplyConfiguration{}
}
// WithExpression sets the Expression 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 Expression field is set to the value of the last call.
func (b *CELDeviceSelectorApplyConfiguration) WithExpression(value string) *CELDeviceSelectorApplyConfiguration {
b.Expression = &value
return b
}

View file

@ -1,42 +0,0 @@
/*
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
// DeviceSelectorApplyConfiguration represents a declarative configuration of the DeviceSelector type for use
// with apply.
//
// DeviceSelector must have exactly one field set.
type DeviceSelectorApplyConfiguration struct {
// CEL contains a CEL expression for selecting a device.
CEL *CELDeviceSelectorApplyConfiguration `json:"cel,omitempty"`
}
// DeviceSelectorApplyConfiguration constructs a declarative configuration of the DeviceSelector type for use with
// apply.
func DeviceSelector() *DeviceSelectorApplyConfiguration {
return &DeviceSelectorApplyConfiguration{}
}
// WithCEL sets the CEL 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 CEL field is set to the value of the last call.
func (b *DeviceSelectorApplyConfiguration) WithCEL(value *CELDeviceSelectorApplyConfiguration) *DeviceSelectorApplyConfiguration {
b.CEL = value
return b
}

View file

@ -38,8 +38,10 @@ type DeviceTaintApplyConfiguration struct {
Value *string `json:"value,omitempty"`
// The effect of the taint on claims that do not tolerate the taint
// and through such claims on the pods using them.
// Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here.
//
// Valid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here. More effects may get added in the future.
// Consumers must treat unknown effects like None.
Effect *resourcev1alpha3.DeviceTaintEffect `json:"effect,omitempty"`
// TimeAdded represents the time at which the taint was added.
// Added automatically during create or update if not set.

View file

@ -41,6 +41,8 @@ type DeviceTaintRuleApplyConfiguration struct {
//
// Changing the spec automatically increments the metadata.generation number.
Spec *DeviceTaintRuleSpecApplyConfiguration `json:"spec,omitempty"`
// Status provides information about what was requested in the spec.
Status *DeviceTaintRuleStatusApplyConfiguration `json:"status,omitempty"`
}
// DeviceTaintRule constructs a declarative configuration of the DeviceTaintRule type for use with
@ -87,6 +89,12 @@ func ExtractDeviceTaintRule(deviceTaintRule *resourcev1alpha3.DeviceTaintRule, f
return ExtractDeviceTaintRuleFrom(deviceTaintRule, fieldManager, "")
}
// ExtractDeviceTaintRuleStatus extracts the applied configuration owned by fieldManager from
// deviceTaintRule for the status subresource.
func ExtractDeviceTaintRuleStatus(deviceTaintRule *resourcev1alpha3.DeviceTaintRule, fieldManager string) (*DeviceTaintRuleApplyConfiguration, error) {
return ExtractDeviceTaintRuleFrom(deviceTaintRule, fieldManager, "status")
}
func (b DeviceTaintRuleApplyConfiguration) IsApplyConfiguration() {}
// WithKind sets the Kind field in the declarative configuration to the given value
@ -255,6 +263,14 @@ func (b *DeviceTaintRuleApplyConfiguration) WithSpec(value *DeviceTaintRuleSpecA
return b
}
// WithStatus sets the Status 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 Status field is set to the value of the last call.
func (b *DeviceTaintRuleApplyConfiguration) WithStatus(value *DeviceTaintRuleStatusApplyConfiguration) *DeviceTaintRuleApplyConfiguration {
b.Status = value
return b
}
// GetKind retrieves the value of the Kind field in the declarative configuration.
func (b *DeviceTaintRuleApplyConfiguration) GetKind() *string {
return b.TypeMetaApplyConfiguration.Kind

View file

@ -24,7 +24,7 @@ package v1alpha3
// DeviceTaintRuleSpec specifies the selector and one taint.
type DeviceTaintRuleSpecApplyConfiguration struct {
// DeviceSelector defines which device(s) the taint is applied to.
// All selector criteria must be satified for a device to
// All selector criteria must be satisfied for a device to
// match. The empty selector matches all devices. Without
// a selector, no devices are matches.
DeviceSelector *DeviceTaintSelectorApplyConfiguration `json:"deviceSelector,omitempty"`

View file

@ -0,0 +1,70 @@
/*
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 (
v1 "k8s.io/client-go/applyconfigurations/meta/v1"
)
// DeviceTaintRuleStatusApplyConfiguration represents a declarative configuration of the DeviceTaintRuleStatus type for use
// with apply.
//
// DeviceTaintRuleStatus provides information about an on-going pod eviction.
type DeviceTaintRuleStatusApplyConfiguration struct {
// Conditions provide information about the state of the DeviceTaintRule
// and the cluster at some point in time,
// in a machine-readable and human-readable format.
//
// The following condition is currently defined as part of this API, more may
// get added:
// - Type: EvictionInProgress
// - Status: True if there are currently pods which need to be evicted, False otherwise
// (includes the effects which don't cause eviction).
// - Reason: not specified, may change
// - Message: includes information about number of pending pods and already evicted pods
// in a human-readable format, updated periodically, may change
//
// For `effect: None`, the condition above gets set once for each change to
// the spec, with the message containing information about what would happen
// if the effect was `NoExecute`. This feedback can be used to decide whether
// changing the effect to `NoExecute` will work as intended. It only gets
// set once to avoid having to constantly update the status.
//
// Must have 8 or fewer entries.
Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"`
}
// DeviceTaintRuleStatusApplyConfiguration constructs a declarative configuration of the DeviceTaintRuleStatus type for use with
// apply.
func DeviceTaintRuleStatus() *DeviceTaintRuleStatusApplyConfiguration {
return &DeviceTaintRuleStatusApplyConfiguration{}
}
// 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 *DeviceTaintRuleStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *DeviceTaintRuleStatusApplyConfiguration {
for i := range values {
if values[i] == nil {
panic("nil value passed to WithConditions")
}
b.Conditions = append(b.Conditions, *values[i])
}
return b
}

View file

@ -25,10 +25,6 @@ package v1alpha3
// The empty selector matches all devices. Without a selector, no devices
// are matched.
type DeviceTaintSelectorApplyConfiguration struct {
// If DeviceClassName is set, the selectors defined there must be
// satisfied by a device to be selected. This field corresponds
// to class.metadata.name.
DeviceClassName *string `json:"deviceClassName,omitempty"`
// If driver is set, only devices from that driver are selected.
// This fields corresponds to slice.spec.driver.
Driver *string `json:"driver,omitempty"`
@ -47,10 +43,6 @@ type DeviceTaintSelectorApplyConfiguration struct {
// Setting also driver and pool may be required to avoid ambiguity,
// but is not required.
Device *string `json:"device,omitempty"`
// Selectors contains the same selection criteria as a ResourceClaim.
// Currently, CEL expressions are supported. All of these selectors
// must be satisfied.
Selectors []DeviceSelectorApplyConfiguration `json:"selectors,omitempty"`
}
// DeviceTaintSelectorApplyConfiguration constructs a declarative configuration of the DeviceTaintSelector type for use with
@ -59,14 +51,6 @@ func DeviceTaintSelector() *DeviceTaintSelectorApplyConfiguration {
return &DeviceTaintSelectorApplyConfiguration{}
}
// WithDeviceClassName sets the DeviceClassName 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 DeviceClassName field is set to the value of the last call.
func (b *DeviceTaintSelectorApplyConfiguration) WithDeviceClassName(value string) *DeviceTaintSelectorApplyConfiguration {
b.DeviceClassName = &value
return b
}
// 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.
@ -90,16 +74,3 @@ func (b *DeviceTaintSelectorApplyConfiguration) WithDevice(value string) *Device
b.Device = &value
return b
}
// WithSelectors adds the given value to the Selectors 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 Selectors field.
func (b *DeviceTaintSelectorApplyConfiguration) WithSelectors(values ...*DeviceSelectorApplyConfiguration) *DeviceTaintSelectorApplyConfiguration {
for i := range values {
if values[i] == nil {
panic("nil value passed to WithSelectors")
}
b.Selectors = append(b.Selectors, *values[i])
}
return b
}

View file

@ -68,7 +68,9 @@ type BasicDeviceApplyConfiguration struct {
AllNodes *bool `json:"allNodes,omitempty"`
// If specified, these are the driver-defined taints.
//
// The maximum number of taints is 4.
// The maximum number of taints is 16. If taints are set for
// any device in a ResourceSlice, then the maximum number of
// allowed devices per ResourceSlice is 64 instead of 128.
//
// This is an alpha field and requires enabling the DRADeviceTaints
// feature gate.

View file

@ -38,8 +38,10 @@ type DeviceTaintApplyConfiguration struct {
Value *string `json:"value,omitempty"`
// The effect of the taint on claims that do not tolerate the taint
// and through such claims on the pods using them.
// Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here.
//
// Valid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here. More effects may get added in the future.
// Consumers must treat unknown effects like None.
Effect *resourcev1beta1.DeviceTaintEffect `json:"effect,omitempty"`
// TimeAdded represents the time at which the taint was added.
// Added automatically during create or update if not set.

View file

@ -62,7 +62,7 @@ type ResourceSliceSpecApplyConfiguration struct {
AllNodes *bool `json:"allNodes,omitempty"`
// Devices lists some or all of the devices in this pool.
//
// Must not have more than 128 entries.
// Must not have more than 128 entries. If any device uses taints the limit is 64.
Devices []DeviceApplyConfiguration `json:"devices,omitempty"`
// PerDeviceNodeSelection defines whether the access from nodes to
// resources in the pool is set on the ResourceSlice level or on each

View file

@ -72,7 +72,9 @@ type DeviceApplyConfiguration struct {
AllNodes *bool `json:"allNodes,omitempty"`
// If specified, these are the driver-defined taints.
//
// The maximum number of taints is 4.
// The maximum number of taints is 16. If taints are set for
// any device in a ResourceSlice, then the maximum number of
// allowed devices per ResourceSlice is 64 instead of 128.
//
// This is an alpha field and requires enabling the DRADeviceTaints
// feature gate.

View file

@ -38,8 +38,10 @@ type DeviceTaintApplyConfiguration struct {
Value *string `json:"value,omitempty"`
// The effect of the taint on claims that do not tolerate the taint
// and through such claims on the pods using them.
// Valid effects are NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here.
//
// Valid effects are None, NoSchedule and NoExecute. PreferNoSchedule as used for
// nodes is not valid here. More effects may get added in the future.
// Consumers must treat unknown effects like None.
Effect *resourcev1beta2.DeviceTaintEffect `json:"effect,omitempty"`
// TimeAdded represents the time at which the taint was added.
// Added automatically during create or update if not set.

View file

@ -62,7 +62,7 @@ type ResourceSliceSpecApplyConfiguration struct {
AllNodes *bool `json:"allNodes,omitempty"`
// Devices lists some or all of the devices in this pool.
//
// Must not have more than 128 entries.
// Must not have more than 128 entries. If any device uses taints the limit is 64.
Devices []DeviceApplyConfiguration `json:"devices,omitempty"`
// PerDeviceNodeSelection defines whether the access from nodes to
// resources in the pool is set on the ResourceSlice level or on each

View file

@ -1710,16 +1710,14 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &applyconfigurationsresourcev1.ResourceSliceSpecApplyConfiguration{}
// Group=resource.k8s.io, Version=v1alpha3
case v1alpha3.SchemeGroupVersion.WithKind("CELDeviceSelector"):
return &resourcev1alpha3.CELDeviceSelectorApplyConfiguration{}
case v1alpha3.SchemeGroupVersion.WithKind("DeviceSelector"):
return &resourcev1alpha3.DeviceSelectorApplyConfiguration{}
case v1alpha3.SchemeGroupVersion.WithKind("DeviceTaint"):
return &resourcev1alpha3.DeviceTaintApplyConfiguration{}
case v1alpha3.SchemeGroupVersion.WithKind("DeviceTaintRule"):
return &resourcev1alpha3.DeviceTaintRuleApplyConfiguration{}
case v1alpha3.SchemeGroupVersion.WithKind("DeviceTaintRuleSpec"):
return &resourcev1alpha3.DeviceTaintRuleSpecApplyConfiguration{}
case v1alpha3.SchemeGroupVersion.WithKind("DeviceTaintRuleStatus"):
return &resourcev1alpha3.DeviceTaintRuleStatusApplyConfiguration{}
case v1alpha3.SchemeGroupVersion.WithKind("DeviceTaintSelector"):
return &resourcev1alpha3.DeviceTaintSelectorApplyConfiguration{}

View file

@ -40,6 +40,8 @@ type DeviceTaintRulesGetter interface {
type DeviceTaintRuleInterface interface {
Create(ctx context.Context, deviceTaintRule *resourcev1alpha3.DeviceTaintRule, opts v1.CreateOptions) (*resourcev1alpha3.DeviceTaintRule, error)
Update(ctx context.Context, deviceTaintRule *resourcev1alpha3.DeviceTaintRule, opts v1.UpdateOptions) (*resourcev1alpha3.DeviceTaintRule, error)
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
UpdateStatus(ctx context.Context, deviceTaintRule *resourcev1alpha3.DeviceTaintRule, opts v1.UpdateOptions) (*resourcev1alpha3.DeviceTaintRule, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
Get(ctx context.Context, name string, opts v1.GetOptions) (*resourcev1alpha3.DeviceTaintRule, error)
@ -47,6 +49,8 @@ type DeviceTaintRuleInterface interface {
Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error)
Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *resourcev1alpha3.DeviceTaintRule, err error)
Apply(ctx context.Context, deviceTaintRule *applyconfigurationsresourcev1alpha3.DeviceTaintRuleApplyConfiguration, opts v1.ApplyOptions) (result *resourcev1alpha3.DeviceTaintRule, err error)
// Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus().
ApplyStatus(ctx context.Context, deviceTaintRule *applyconfigurationsresourcev1alpha3.DeviceTaintRuleApplyConfiguration, opts v1.ApplyOptions) (result *resourcev1alpha3.DeviceTaintRule, err error)
DeviceTaintRuleExpansion
}

View file

@ -216,6 +216,16 @@ func (m *Mock[T]) AddAfter(item T, duration time.Duration) {
m.state.Later = append(m.state.Later, MockDelayedItem[T]{Item: item, Duration: duration})
}
// CancelAfter is an extension of the TypedDelayingInterface: it allows a test to remove an item that may or may not have been added before via AddAfter.
func (m *Mock[T]) CancelAfter(item T) {
m.mutex.Lock()
defer m.mutex.Unlock()
m.state.Later = slices.DeleteFunc(m.state.Later, func(later MockDelayedItem[T]) bool {
return later.Item == item
})
}
// AddRateLimited implements [TypedRateLimitingInterface.AddRateLimited].
func (m *Mock[T]) AddRateLimited(item T) {
m.mutex.Lock()

View file

@ -29,6 +29,9 @@ import (
// 3. Empty toleration.key means to match all taint keys.
// If toleration.key is empty, toleration.operator must be 'Exists';
// this combination means to match all taint values and all taint keys.
//
// Callers must check separately what the effect is and only react to
// known effects. Unknown effects and the None effect must be ignored.
func ToleratesTaint(toleration resourceapi.DeviceToleration, taint resourceapi.DeviceTaint) bool {
// This code was copied from https://github.com/kubernetes/kubernetes/blob/f007012f5fe49e40ae0596cf463a8e7b247b3357/staging/src/k8s.io/api/core/v1/toleration.go#L39-L56.
// It wasn't placed in the resourceapi package because code related to logic

View file

@ -38,7 +38,6 @@ import (
resourcelisters "k8s.io/client-go/listers/resource/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/dynamic-resource-allocation/cel"
"k8s.io/klog/v2"
"k8s.io/utils/buffer"
"k8s.io/utils/ptr"
@ -65,7 +64,6 @@ type Tracker struct {
deviceTaintsHandle cache.ResourceEventHandlerRegistration
deviceClasses cache.SharedIndexInformer
deviceClassesHandle cache.ResourceEventHandlerRegistration
celCache *cel.Cache
patchedResourceSlices cache.Store
broadcaster record.EventBroadcaster
recorder record.EventRecorder
@ -155,7 +153,6 @@ func newTracker(ctx context.Context, opts Options) (finalT *Tracker, finalErr er
resourceSlices: opts.SliceInformer.Informer(),
deviceTaints: opts.TaintInformer.Informer(),
deviceClasses: opts.ClassInformer.Informer(),
celCache: cel.NewCache(10, cel.Features{EnableConsumableCapacity: opts.EnableConsumableCapacity}),
patchedResourceSlices: cache.NewStore(cache.MetaNamespaceKeyFunc),
handleError: utilruntime.HandleErrorWithContext,
eventQueue: *buffer.NewRing[func()](buffer.RingOptions{InitialSize: 0, NormalSize: 4}),
@ -644,8 +641,6 @@ func (t *Tracker) applyPatches(ctx context.Context, slice *resourceapi.ResourceS
logger.V(6).Info("processing DeviceTaintRule")
deviceSelector := taintRule.Spec.DeviceSelector
var deviceClassExprs []cel.CompilationResult
var selectorExprs []cel.CompilationResult
var deviceName *string
if deviceSelector != nil {
if deviceSelector.Driver != nil && *deviceSelector.Driver != slice.Spec.Driver {
@ -657,32 +652,7 @@ func (t *Tracker) applyPatches(ctx context.Context, slice *resourceapi.ResourceS
continue
}
deviceName = deviceSelector.Device
if deviceSelector.DeviceClassName != nil {
logger := logger.WithValues("deviceClassName", *deviceSelector.DeviceClassName)
classObj, exists, err := t.deviceClasses.GetIndexer().GetByKey(*deviceSelector.DeviceClassName)
if err != nil {
return nil, fmt.Errorf("failed to get device class %s for DeviceTaintRule %s", *deviceSelector.DeviceClassName, taintRule.Name)
}
if !exists {
logger.V(7).Info("DeviceTaintRule does not apply, DeviceClass does not exist")
continue
}
class := classObj.(*resourceapi.DeviceClass)
for _, selector := range class.Spec.Selectors {
if selector.CEL != nil {
expr := t.celCache.GetOrCompile(selector.CEL.Expression)
deviceClassExprs = append(deviceClassExprs, expr)
}
}
}
for _, selector := range deviceSelector.Selectors {
if selector.CEL != nil {
expr := t.celCache.GetOrCompile(selector.CEL.Expression)
selectorExprs = append(selectorExprs, expr)
}
}
}
devices:
for dIndex, device := range slice.Spec.Devices {
deviceID := deviceID(slice.Spec.Driver, slice.Spec.Pool.Name, device.Name)
logger := logger.WithValues("device", deviceID)
@ -692,47 +662,6 @@ func (t *Tracker) applyPatches(ctx context.Context, slice *resourceapi.ResourceS
continue
}
for i, expr := range deviceClassExprs {
if expr.Error != nil {
// Could happen if some future apiserver accepted some
// future expression and then got downgraded. Normally
// the "stored expression" mechanism prevents that, but
// this code here might be more than one release older
// than the cluster it runs in.
return nil, fmt.Errorf("DeviceTaintRule %s: class %s: selector #%d: CEL compile error: %w", taintRule.Name, *deviceSelector.DeviceClassName, i, expr.Error)
}
matches, details, err := expr.DeviceMatches(ctx, cel.Device{Driver: slice.Spec.Driver, Attributes: device.Attributes, Capacity: device.Capacity})
logger.V(7).Info("CEL result", "class", *deviceSelector.DeviceClassName, "selector", i, "expression", expr.Expression, "matches", matches, "actualCost", ptr.Deref(details.ActualCost(), 0), "err", err)
if err != nil {
continue devices
}
if !matches {
continue devices
}
}
for i, expr := range selectorExprs {
if expr.Error != nil {
// Could happen if some future apiserver accepted some
// future expression and then got downgraded. Normally
// the "stored expression" mechanism prevents that, but
// this code here might be more than one release older
// than the cluster it runs in.
return nil, fmt.Errorf("DeviceTaintRule %s: selector #%d: CEL compile error: %w", taintRule.Name, i, expr.Error)
}
matches, details, err := expr.DeviceMatches(ctx, cel.Device{Driver: slice.Spec.Driver, Attributes: device.Attributes, Capacity: device.Capacity})
logger.V(7).Info("CEL result", "selector", i, "expression", expr.Expression, "matches", matches, "actualCost", ptr.Deref(details.ActualCost(), 0), "err", err)
if err != nil {
if t.recorder != nil {
t.recorder.Eventf(taintRule, v1.EventTypeWarning, "CELRuntimeError", "selector #%d: runtime error: %v", i, err)
}
continue devices
}
if !matches {
continue devices
}
}
logger.V(6).Info("applying matching DeviceTaintRule")
// TODO: remove conversion once taint is already in the right API package.

View file

@ -316,37 +316,10 @@ var (
}
return rule
}
taintCELSelectedDevicesRule = func(rule *resourcealphaapi.DeviceTaintRule, exprs ...string) *resourcealphaapi.DeviceTaintRule {
rule = rule.DeepCopy()
var selectors []resourcealphaapi.DeviceSelector
for _, expr := range exprs {
selectors = append(selectors, resourcealphaapi.DeviceSelector{
CEL: &resourcealphaapi.CELDeviceSelector{
Expression: expr,
},
})
}
rule.Spec.DeviceSelector = &resourcealphaapi.DeviceTaintSelector{
Selectors: selectors,
}
return rule
}
taintDeviceClassRule = func(rule *resourcealphaapi.DeviceTaintRule, deviceClassName string) *resourcealphaapi.DeviceTaintRule {
rule = rule.DeepCopy()
rule.Spec.DeviceSelector = &resourcealphaapi.DeviceTaintSelector{
DeviceClassName: &deviceClassName,
}
return rule
}
taintPool1DevicesRule = taintPoolDevicesRule(taintAllDevicesRule, pool1)
taintPool2DevicesRule = taintPoolDevicesRule(taintAllDevicesRule, pool2)
taintDriver1DevicesRule = taintDriverDevicesRule(taintAllDevicesRule, driver1)
taintDevice1Rule = taintNamedDevicesRule(taintAllDevicesRule, device1Name)
taintDriver1DevicesCELRule = taintCELSelectedDevicesRule(taintAllDevicesRule, `device.driver == "`+driver1+`"`)
taintNoDevicesCELRule = taintCELSelectedDevicesRule(taintAllDevicesRule, `true`, `false`, `true`)
taintNoDevicesCELRuntimeErrorRule = taintCELSelectedDevicesRule(taintAllDevicesRule, `device.attributes["test.example.com"].deviceAttr`)
taintNoDevicesInvalidCELRule = taintCELSelectedDevicesRule(taintAllDevicesRule, `invalid`)
taintDeviceClass1Rule = taintDeviceClassRule(taintAllDevicesRule, deviceClass1.Name)
taintPool1DevicesRule = taintPoolDevicesRule(taintAllDevicesRule, pool1)
taintPool2DevicesRule = taintPoolDevicesRule(taintAllDevicesRule, pool2)
taintDriver1DevicesRule = taintDriverDevicesRule(taintAllDevicesRule, driver1)
taintDevice1Rule = taintNamedDevicesRule(taintAllDevicesRule, device1Name)
)
func TestListPatchedResourceSlices(t *testing.T) {
@ -519,102 +492,19 @@ func TestListPatchedResourceSlices(t *testing.T) {
{event: handlerEventAdd, newObj: slice2},
},
},
"add-attribute-for-selector": {
events: []any{
add(taintDriver1DevicesCELRule),
add(slice1),
add(slice2),
},
expectedPatchedSlices: []*resourceapi.ResourceSlice{
slice1Tainted,
slice2,
},
expectedHandlerEvents: []handlerEvent{
{event: handlerEventAdd, newObj: slice1Tainted},
{event: handlerEventAdd, newObj: slice2},
},
},
"selector-does-not-match": {
events: []any{
add(taintNoDevicesCELRule),
add(slice1),
},
expectedPatchedSlices: []*resourceapi.ResourceSlice{
slice1,
},
expectedHandlerEvents: []handlerEvent{
{event: handlerEventAdd, newObj: slice1},
},
},
"runtime-CEL-errors-skip-devices": {
events: []any{
add(taintNoDevicesCELRuntimeErrorRule),
add(slice1),
},
expectedPatchedSlices: []*resourceapi.ResourceSlice{
slice1,
},
expectEvents: func(t *assert.CollectT, events *v1.EventList) {
if !assert.Len(t, events.Items, 1) {
return
}
assert.Equal(t, taintNoDevicesCELRuntimeErrorRule.Name, events.Items[0].InvolvedObject.Name)
assert.Equal(t, "CELRuntimeError", events.Items[0].Reason)
},
expectedHandlerEvents: []handlerEvent{
{event: handlerEventAdd, newObj: slice1},
},
},
"invalid-CEL-expression-throws-error": {
events: []any{
[]any{
add(taintNoDevicesInvalidCELRule),
add(slice1),
},
},
expectedPatchedSlices: []*resourceapi.ResourceSlice{},
expectUnhandledErrors: func(t *testing.T, errs []error) {
if !assert.Len(t, errs, 1) {
return
}
assert.ErrorContains(t, errs[0], "CEL compile error")
},
},
"add-taint-for-device-class": {
events: []any{
add(deviceClass1),
add(taintDeviceClass1Rule),
add(slice1),
add(slice2),
},
expectedPatchedSlices: []*resourceapi.ResourceSlice{
slice1Tainted,
slice2,
},
expectedHandlerEvents: []handlerEvent{
{event: handlerEventAdd, newObj: slice1Tainted},
{event: handlerEventAdd, newObj: slice2},
},
},
"filter-all-criteria": {
events: []any{
add(deviceClass1),
add(
taintDeviceClassRule(
taintDriverDevicesRule(
taintPoolDevicesRule(
taintNamedDevicesRule(
taintCELSelectedDevicesRule(
taintAllDevicesRule,
`true`,
),
device1Name,
),
pool1,
taintDriverDevicesRule(
taintPoolDevicesRule(
taintNamedDevicesRule(
taintAllDevicesRule,
device1Name,
),
driver1,
pool1,
),
deviceClass1.Name,
driver1,
),
),
add(slice1),

View file

@ -869,6 +869,11 @@ func TestAllocator(t *testing.T,
taintKey := "taint-key"
taintValue := "taint-value"
taintValue2 := "taint-value-2"
taintNone := resourceapi.DeviceTaint{
Key: taintKey,
Value: taintValue,
Effect: resourceapi.DeviceTaintEffectNone,
}
taintNoSchedule := resourceapi.DeviceTaint{
Key: taintKey,
Value: taintValue,
@ -3565,6 +3570,21 @@ func TestAllocator(t *testing.T,
deviceAllocationResult(req0SubReq1, driverA, pool1, device2, false, tolerationNoExecute), // Only second device's taints are tolerated.
)},
},
"tainted-no-effect": {
features: Features{
DeviceTaints: true,
},
claimsToAllocate: objects(claimWithRequest(claim0, req0, classA)),
classes: objects(class(classA, driverA)),
slices: unwrap(slice(slice1, node1, pool1, driverA,
device(device1, nil, nil).withTaints(taintNone),
)),
node: node(node1, region1),
expectResults: []any{allocationResult(
localNodeSelector(node1),
deviceAllocationResult(req0, driverA, pool1, device1, false),
)},
},
"tainted-one-device-two-taints-both-tolerated": {
features: Features{
DeviceTaints: true,

View file

@ -1291,7 +1291,7 @@ func (alloc *allocator) allocateDevice(r deviceIndices, device deviceWithID, mus
// Might be tainted, in which case the taint has to be tolerated.
// The check is skipped if the feature is disabled.
if alloc.features.DeviceTaints && !allTaintsTolerated(device.Device, request) {
if alloc.features.DeviceTaints && taintPreventsAllocation(device.Device, request) {
return false, nil, nil
}
@ -1387,13 +1387,17 @@ func (alloc *allocator) allocateDevice(r deviceIndices, device deviceWithID, mus
}, nil
}
func allTaintsTolerated(device *draapi.Device, request requestAccessor) bool {
func taintPreventsAllocation(device *draapi.Device, request requestAccessor) bool {
for _, taint := range device.Taints {
if !taintTolerated(taint, request) {
return false
switch taint.Effect {
// Only known effects prevent allocation, others (including None) are ignored.
case resourceapi.DeviceTaintEffectNoExecute, resourceapi.DeviceTaintEffectNoSchedule:
if !taintTolerated(taint, request) {
return true
}
}
}
return true
return false
}
func taintTolerated(taint resourceapi.DeviceTaint, request requestAccessor) bool {

View file

@ -1134,7 +1134,7 @@ func (alloc *allocator) allocateDevice(r deviceIndices, device deviceWithID, mus
// Might be tainted, in which case the taint has to be tolerated.
// The check is skipped if the feature is disabled.
if alloc.features.DeviceTaints && !allTaintsTolerated(device.Device, request) {
if alloc.features.DeviceTaints && taintPreventsAllocation(device.Device, request) {
return false, nil, nil
}
@ -1192,13 +1192,17 @@ func (alloc *allocator) allocateDevice(r deviceIndices, device deviceWithID, mus
}, nil
}
func allTaintsTolerated(device *draapi.Device, request requestAccessor) bool {
func taintPreventsAllocation(device *draapi.Device, request requestAccessor) bool {
for _, taint := range device.Taints {
if !taintTolerated(taint, request) {
return false
switch taint.Effect {
// Only known effects prevent allocation, others (including None) are ignored.
case resourceapi.DeviceTaintEffectNoExecute, resourceapi.DeviceTaintEffectNoSchedule:
if !taintTolerated(taint, request) {
return true
}
}
}
return true
return false
}
func taintTolerated(taint resourceapi.DeviceTaint, request requestAccessor) bool {

View file

@ -168,6 +168,8 @@ type KubeControllerManagerConfiguration struct {
// ValidatingAdmissionPolicyStatusControllerConfiguration holds configuration for
// ValidatingAdmissionPolicyStatusController related features.
ValidatingAdmissionPolicyStatusController ValidatingAdmissionPolicyStatusControllerConfiguration
// DeviceTaintEvictionControllerConfiguration contains elements configuring the device taint eviction controller.
DeviceTaintEvictionController DeviceTaintEvictionControllerConfiguration
}
// AttachDetachControllerConfiguration contains elements describing AttachDetachController.
@ -488,3 +490,12 @@ type ValidatingAdmissionPolicyStatusControllerConfiguration struct {
// The default value is 5.
ConcurrentPolicySyncs int32
}
// DeviceTaintEvictionControllerConfiguration contains elements configuring the device taint eviction controller.
type DeviceTaintEvictionControllerConfiguration struct {
// ConcurrentSyncs is the number of operations (deleting a pod, updating a ResourcClaim status, etc.)
// that will be done concurrently. Larger number = processing, but more CPU (and network) load.
//
// The default is 10.
ConcurrentSyncs int32
}

Some files were not shown because too many files have changed in this diff Show more