Skip to content

Commit

Permalink
feat: add support for matchExpressions when filtering for nodes (#1697)
Browse files Browse the repository at this point in the history
* feat: add support for matchExpressions when filtering for nodes

* fix: make generate
  • Loading branch information
miguelvr authored Nov 30, 2024
1 parent 6e5db6d commit 8e26470
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 12 deletions.
30 changes: 30 additions & 0 deletions config/crds/troubleshoot.sh_analyzers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1186,6 +1186,36 @@ spec:
type: string
selector:
properties:
matchExpressions:
items:
description: |-
A label selector requirement is a selector that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the
selector applies to.
type: string
operator:
description: |-
operator represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists and DoesNotExist.
type: string
values:
description: |-
values is an array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
matchLabel:
additionalProperties:
type: string
Expand Down
30 changes: 30 additions & 0 deletions config/crds/troubleshoot.sh_preflights.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1186,6 +1186,36 @@ spec:
type: string
selector:
properties:
matchExpressions:
items:
description: |-
A label selector requirement is a selector that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the
selector applies to.
type: string
operator:
description: |-
operator represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists and DoesNotExist.
type: string
values:
description: |-
values is an array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
matchLabel:
additionalProperties:
type: string
Expand Down
30 changes: 30 additions & 0 deletions config/crds/troubleshoot.sh_supportbundles.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1217,6 +1217,36 @@ spec:
type: string
selector:
properties:
matchExpressions:
items:
description: |-
A label selector requirement is a selector that contains values, a key, and an operator that
relates the key and values.
properties:
key:
description: key is the label key that the
selector applies to.
type: string
operator:
description: |-
operator represents a key's relationship to a set of values.
Valid operators are In, NotIn, Exists and DoesNotExist.
type: string
values:
description: |-
values is an array of string values. If the operator is In or NotIn,
the values array must be non-empty. If the operator is Exists or DoesNotExist,
the values array must be empty. This array is replaced during a strategic
merge patch.
items:
type: string
type: array
x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
matchLabel:
additionalProperties:
type: string
Expand Down
29 changes: 19 additions & 10 deletions pkg/analyze/node_resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ import (
"strings"

"github.com/pkg/errors"
util "github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/constants"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"

"github.com/replicatedhq/troubleshoot/internal/util"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/replicatedhq/troubleshoot/pkg/constants"
)

type AnalyzeNodeResources struct {
Expand Down Expand Up @@ -381,13 +384,19 @@ func nodeMatchesFilters(node corev1.Node, filters *troubleshootv1beta2.NodeResou

// all filters must pass for this to pass
if filters.Selector != nil {
for k, v := range filters.Selector.MatchLabel {
l, found := node.Labels[k]
if !found {
return false, nil
} else if l != v {
return false, nil
}
selector, err := metav1.LabelSelectorAsSelector(
&metav1.LabelSelector{
MatchLabels: filters.Selector.MatchLabel,
MatchExpressions: filters.Selector.MatchExpressions,
},
)
if err != nil {
return false, errors.Wrap(err, "failed to create label selector")
}

found := selector.Matches(labels.Set(node.Labels))
if !found {
return false, nil
}
}

Expand Down
127 changes: 126 additions & 1 deletion pkg/analyze/node_resources_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package analyzer
import (
"testing"

troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
)

func Test_compareNodeResourceConditionalToActual(t *testing.T) {
Expand Down Expand Up @@ -501,6 +502,130 @@ func Test_nodeMatchesFilters(t *testing.T) {
},
expectResult: true,
},
{
name: "true when the label expression matches with operator In",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"value"},
},
},
},
},
expectResult: true,
},
{
name: "false when the label expression does not match with operator In",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label",
Operator: metav1.LabelSelectorOpIn,
Values: []string{"value2"},
},
},
},
},
expectResult: false,
},
{
name: "true when the label expression matches with operator NotIn",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label",
Operator: metav1.LabelSelectorOpNotIn,
Values: []string{"value2"},
},
},
},
},
expectResult: true,
},
{
name: "false when the label expression does not match with operator NotIn",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label",
Operator: metav1.LabelSelectorOpNotIn,
Values: []string{"value"},
},
},
},
},
expectResult: false,
},
{
name: "true when the label expression matches with operator Exists",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label",
Operator: metav1.LabelSelectorOpExists,
},
},
},
},
expectResult: true,
},
{
name: "false when the label expression matches with operator Exists",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label2",
Operator: metav1.LabelSelectorOpExists,
},
},
},
},
expectResult: false,
},
{
name: "true when the label expression matches with operator DoesNotExist",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label2",
Operator: metav1.LabelSelectorOpDoesNotExist,
},
},
},
},
expectResult: true,
},
{
name: "false when the label expression does not match with operator DoesNotExist",
node: node,
filters: &troubleshootv1beta2.NodeResourceFilters{
Selector: &troubleshootv1beta2.NodeResourceSelectors{
MatchExpressions: []metav1.LabelSelectorRequirement{
{
Key: "label",
Operator: metav1.LabelSelectorOpDoesNotExist,
},
},
},
},
expectResult: false,
},
}

for _, test := range tests {
Expand Down
5 changes: 4 additions & 1 deletion pkg/apis/troubleshoot/v1beta2/analyzer_shared.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package v1beta2

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/replicatedhq/troubleshoot/pkg/multitype"
)

Expand Down Expand Up @@ -133,7 +135,8 @@ type NodeResourceFilters struct {
}

type NodeResourceSelectors struct {
MatchLabel map[string]string `json:"matchLabel,omitempty" yaml:"matchLabel,omitempty"`
MatchLabel map[string]string `json:"matchLabel,omitempty" yaml:"matchLabel,omitempty"`
MatchExpressions []metav1.LabelSelectorRequirement `json:"matchExpressions,omitempty" yaml:"matchExpressions,omitempty"`
}

type TextAnalyze struct {
Expand Down
8 changes: 8 additions & 0 deletions pkg/apis/troubleshoot/v1beta2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions schemas/analyzer-troubleshoot-v1beta2.json
Original file line number Diff line number Diff line change
Expand Up @@ -1785,6 +1785,35 @@
"selector": {
"type": "object",
"properties": {
"matchExpressions": {
"type": "array",
"items": {
"description": "A label selector requirement is a selector that contains values, a key, and an operator that\nrelates the key and values.",
"type": "object",
"required": [
"key",
"operator"
],
"properties": {
"key": {
"description": "key is the label key that the selector applies to.",
"type": "string"
},
"operator": {
"description": "operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist.",
"type": "string"
},
"values": {
"description": "values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch.",
"type": "array",
"items": {
"type": "string"
},
"x-kubernetes-list-type": "atomic"
}
}
}
},
"matchLabel": {
"type": "object",
"additionalProperties": {
Expand Down
Loading

0 comments on commit 8e26470

Please sign in to comment.