Allow filtering node resources on taint. (#1840)

* allow filtering node resources on taint
This commit is contained in:
Ash
2025-09-09 14:33:51 +01:00
committed by GitHub
parent c5dc2a19f7
commit dd48aadf7f
12 changed files with 368 additions and 2 deletions

View File

@@ -1282,6 +1282,34 @@ spec:
type: string
type: object
type: object
taint:
description: |-
The node this Taint is attached to has the "effect" on
any pod that does not tolerate the Taint.
properties:
effect:
description: |-
Required. The effect of the taint on pods
that do not tolerate the taint.
Valid effects are NoSchedule, PreferNoSchedule and NoExecute.
type: string
key:
description: Required. The taint key to be applied
to a node.
type: string
timeAdded:
description: TimeAdded represents the time at which
the taint was added.
format: date-time
type: string
value:
description: The taint value corresponding to the
taint key.
type: string
required:
- effect
- key
type: object
type: object
outcomes:
items:

View File

@@ -1282,6 +1282,34 @@ spec:
type: string
type: object
type: object
taint:
description: |-
The node this Taint is attached to has the "effect" on
any pod that does not tolerate the Taint.
properties:
effect:
description: |-
Required. The effect of the taint on pods
that do not tolerate the taint.
Valid effects are NoSchedule, PreferNoSchedule and NoExecute.
type: string
key:
description: Required. The taint key to be applied
to a node.
type: string
timeAdded:
description: TimeAdded represents the time at which
the taint was added.
format: date-time
type: string
value:
description: The taint value corresponding to the
taint key.
type: string
required:
- effect
- key
type: object
type: object
outcomes:
items:

View File

@@ -1313,6 +1313,34 @@ spec:
type: string
type: object
type: object
taint:
description: |-
The node this Taint is attached to has the "effect" on
any pod that does not tolerate the Taint.
properties:
effect:
description: |-
Required. The effect of the taint on pods
that do not tolerate the taint.
Valid effects are NoSchedule, PreferNoSchedule and NoExecute.
type: string
key:
description: Required. The taint key to be applied
to a node.
type: string
timeAdded:
description: TimeAdded represents the time at which
the taint was added.
format: date-time
type: string
value:
description: The taint value corresponding to the
taint key.
type: string
required:
- effect
- key
type: object
type: object
outcomes:
items:

1
go.mod
View File

@@ -55,6 +55,7 @@ require (
k8s.io/cli-runtime v0.34.0
k8s.io/client-go v0.34.0
k8s.io/klog/v2 v2.130.1
k8s.io/kubernetes v1.33.4
oras.land/oras-go v1.2.6
sigs.k8s.io/controller-runtime v0.22.0
sigs.k8s.io/e2e-framework v0.6.0

2
go.sum
View File

@@ -2261,6 +2261,8 @@ k8s.io/kubectl v0.33.3 h1:r/phHvH1iU7gO/l7tTjQk2K01ER7/OAJi8uFHHyWSac=
k8s.io/kubectl v0.33.3/go.mod h1:euj2bG56L6kUGOE/ckZbCoudPwuj4Kud7BR0GzyNiT0=
k8s.io/kubelet v0.34.0 h1:1nZt1Q6Kfx7xCaTS9vnqR9sjZDxf3cRSQkAFCczULmc=
k8s.io/kubelet v0.34.0/go.mod h1:NqbF8ViVettlZbf9hw9DJhubaWn7rGvDDTcLMDm6tQ0=
k8s.io/kubernetes v1.33.4 h1:T1d5FLUYm3/KyUeV7YJhKTR980zHCHb7K2xhCSo3lE8=
k8s.io/kubernetes v1.33.4/go.mod h1:nrt8sldmckKz2fCZhgRX3SKfS2e+CzXATPv6ITNkU00=
k8s.io/metrics v0.34.0 h1:nYSfG2+tnL6/MRC2I+sGHjtNEGoEoM/KktgGOoQFwws=
k8s.io/metrics v0.34.0/go.mod h1:KCuXmotE0v4AvoARKUP8NC4lUnbK/Du1mluGdor5h4M=
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=

View File

@@ -12,6 +12,7 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/kubernetes/pkg/util/taints"
"github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
@@ -451,6 +452,10 @@ func nodeMatchesFilters(node corev1.Node, filters *troubleshootv1beta2.NodeResou
}
}
if filters.Taint != nil {
return taints.TaintExists(node.Spec.Taints, filters.Taint), nil
}
if filters.CPUArchitecture != "" {
parsed := filters.CPUArchitecture

View File

@@ -702,6 +702,191 @@ func Test_nodeMatchesFilters(t *testing.T) {
},
expectResult: false,
},
{
name: "true when taint exists",
node: corev1.Node{
Spec: corev1.NodeSpec{
Taints: []corev1.Taint{
{
Key: "node.kubernetes.io/not-ready",
Value: "",
Effect: corev1.TaintEffectNoSchedule,
},
},
},
},
filters: &troubleshootv1beta2.NodeResourceFilters{
Taint: &corev1.Taint{
Key: "node.kubernetes.io/not-ready",
Effect: corev1.TaintEffectNoSchedule,
},
},
expectResult: true,
},
{
name: "true when taint exists with value match",
node: corev1.Node{
Spec: corev1.NodeSpec{
Taints: []corev1.Taint{
{
Key: "dedicated",
Value: "gpu",
Effect: corev1.TaintEffectNoSchedule,
},
},
},
},
filters: &troubleshootv1beta2.NodeResourceFilters{
Taint: &corev1.Taint{
Key: "dedicated",
Value: "gpu",
Effect: corev1.TaintEffectNoSchedule,
},
},
expectResult: true,
},
{
name: "false when taint key does not exist",
node: corev1.Node{
Spec: corev1.NodeSpec{
Taints: []corev1.Taint{
{
Key: "node.kubernetes.io/not-ready",
Value: "",
Effect: corev1.TaintEffectNoSchedule,
},
},
},
},
filters: &troubleshootv1beta2.NodeResourceFilters{
Taint: &corev1.Taint{
Key: "different-key",
Effect: corev1.TaintEffectNoSchedule,
},
},
expectResult: false,
},
{
name: "false when taint effect does not match",
node: corev1.Node{
Spec: corev1.NodeSpec{
Taints: []corev1.Taint{
{
Key: "node.kubernetes.io/not-ready",
Value: "",
Effect: corev1.TaintEffectNoSchedule,
},
},
},
},
filters: &troubleshootv1beta2.NodeResourceFilters{
Taint: &corev1.Taint{
Key: "node.kubernetes.io/not-ready",
Effect: corev1.TaintEffectNoExecute,
},
},
expectResult: false,
},
{
name: "true when taint value does not match but key and effect do (TaintExists only matches key and effect)",
node: corev1.Node{
Spec: corev1.NodeSpec{
Taints: []corev1.Taint{
{
Key: "dedicated",
Value: "gpu",
Effect: corev1.TaintEffectNoSchedule,
},
},
},
},
filters: &troubleshootv1beta2.NodeResourceFilters{
Taint: &corev1.Taint{
Key: "dedicated",
Value: "cpu",
Effect: corev1.TaintEffectNoSchedule,
},
},
expectResult: true,
},
{
name: "true when node has multiple taints and filter matches one",
node: corev1.Node{
Spec: corev1.NodeSpec{
Taints: []corev1.Taint{
{
Key: "node.kubernetes.io/not-ready",
Value: "",
Effect: corev1.TaintEffectNoSchedule,
},
{
Key: "dedicated",
Value: "gpu",
Effect: corev1.TaintEffectNoSchedule,
},
{
Key: "example.com/special-hardware",
Value: "true",
Effect: corev1.TaintEffectNoExecute,
},
},
},
},
filters: &troubleshootv1beta2.NodeResourceFilters{
Taint: &corev1.Taint{
Key: "dedicated",
Value: "gpu",
Effect: corev1.TaintEffectNoSchedule,
},
},
expectResult: true,
},
{
name: "true when node has no taints but no taint filter is specified",
node: corev1.Node{
Spec: corev1.NodeSpec{
Taints: []corev1.Taint{},
},
},
filters: &troubleshootv1beta2.NodeResourceFilters{},
expectResult: true,
},
{
name: "false when node has no taints but taint filter is specified",
node: corev1.Node{
Spec: corev1.NodeSpec{
Taints: []corev1.Taint{},
},
},
filters: &troubleshootv1beta2.NodeResourceFilters{
Taint: &corev1.Taint{
Key: "node.kubernetes.io/not-ready",
Effect: corev1.TaintEffectNoSchedule,
},
},
expectResult: false,
},
{
name: "true when matching taint with PreferNoSchedule effect",
node: corev1.Node{
Spec: corev1.NodeSpec{
Taints: []corev1.Taint{
{
Key: "node.kubernetes.io/memory-pressure",
Value: "",
Effect: corev1.TaintEffectPreferNoSchedule,
},
},
},
},
filters: &troubleshootv1beta2.NodeResourceFilters{
Taint: &corev1.Taint{
Key: "node.kubernetes.io/memory-pressure",
Effect: corev1.TaintEffectPreferNoSchedule,
},
},
expectResult: true,
},
}
for _, test := range tests {

View File

@@ -1,6 +1,7 @@
package v1beta2
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/replicatedhq/troubleshoot/pkg/multitype"
@@ -142,6 +143,7 @@ type NodeResourceFilters struct {
ResourceName string `json:"resourceName,omitempty" yaml:"resourceName,omitempty"`
ResourceAllocatable string `json:"resourceAllocatable,omitempty" yaml:"resourceAllocatable,omitempty"`
ResourceCapacity string `json:"resourceCapacity,omitempty" yaml:"resourceCapacity,omitempty"`
Taint *corev1.Taint `json:"taint,omitempty" yaml:"taint,omitempty"`
}
type NodeResourceSelectors struct {

View File

@@ -22,7 +22,8 @@ package v1beta2
import (
"github.com/replicatedhq/troubleshoot/pkg/multitype"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
@@ -3429,6 +3430,11 @@ func (in *NodeResourceFilters) DeepCopyInto(out *NodeResourceFilters) {
*out = new(NodeResourceSelectors)
(*in).DeepCopyInto(*out)
}
if in.Taint != nil {
in, out := &in.Taint, &out.Taint
*out = new(v1.Taint)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeResourceFilters.
@@ -3453,7 +3459,7 @@ func (in *NodeResourceSelectors) DeepCopyInto(out *NodeResourceSelectors) {
}
if in.MatchExpressions != nil {
in, out := &in.MatchExpressions, &out.MatchExpressions
*out = make([]v1.LabelSelectorRequirement, len(*in))
*out = make([]metav1.LabelSelectorRequirement, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}

View File

@@ -1914,6 +1914,33 @@
}
}
}
},
"taint": {
"description": "The node this Taint is attached to has the \"effect\" on\nany pod that does not tolerate the Taint.",
"type": "object",
"required": [
"effect",
"key"
],
"properties": {
"effect": {
"description": "Required. The effect of the taint on pods\nthat do not tolerate the taint.\nValid effects are NoSchedule, PreferNoSchedule and NoExecute.",
"type": "string"
},
"key": {
"description": "Required. The taint key to be applied to a node.",
"type": "string"
},
"timeAdded": {
"description": "TimeAdded represents the time at which the taint was added.",
"type": "string",
"format": "date-time"
},
"value": {
"description": "The taint value corresponding to the taint key.",
"type": "string"
}
}
}
}
},

View File

@@ -1914,6 +1914,33 @@
}
}
}
},
"taint": {
"description": "The node this Taint is attached to has the \"effect\" on\nany pod that does not tolerate the Taint.",
"type": "object",
"required": [
"effect",
"key"
],
"properties": {
"effect": {
"description": "Required. The effect of the taint on pods\nthat do not tolerate the taint.\nValid effects are NoSchedule, PreferNoSchedule and NoExecute.",
"type": "string"
},
"key": {
"description": "Required. The taint key to be applied to a node.",
"type": "string"
},
"timeAdded": {
"description": "TimeAdded represents the time at which the taint was added.",
"type": "string",
"format": "date-time"
},
"value": {
"description": "The taint value corresponding to the taint key.",
"type": "string"
}
}
}
}
},

View File

@@ -1960,6 +1960,33 @@
}
}
}
},
"taint": {
"description": "The node this Taint is attached to has the \"effect\" on\nany pod that does not tolerate the Taint.",
"type": "object",
"required": [
"effect",
"key"
],
"properties": {
"effect": {
"description": "Required. The effect of the taint on pods\nthat do not tolerate the taint.\nValid effects are NoSchedule, PreferNoSchedule and NoExecute.",
"type": "string"
},
"key": {
"description": "Required. The taint key to be applied to a node.",
"type": "string"
},
"timeAdded": {
"description": "TimeAdded represents the time at which the taint was added.",
"type": "string",
"format": "date-time"
},
"value": {
"description": "The taint value corresponding to the taint key.",
"type": "string"
}
}
}
}
},