diff --git a/pkg/analyze/node_resources.go b/pkg/analyze/node_resources.go index 7698593df..1f6e62c6f 100644 --- a/pkg/analyze/node_resources.go +++ b/pkg/analyze/node_resources.go @@ -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 { @@ -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 } } diff --git a/pkg/analyze/node_resources_test.go b/pkg/analyze/node_resources_test.go index 87e37327e..204840843 100644 --- a/pkg/analyze/node_resources_test.go +++ b/pkg/analyze/node_resources_test.go @@ -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) { @@ -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 { diff --git a/pkg/apis/troubleshoot/v1beta2/analyzer_shared.go b/pkg/apis/troubleshoot/v1beta2/analyzer_shared.go index 297a4163e..0ce3e8f24 100644 --- a/pkg/apis/troubleshoot/v1beta2/analyzer_shared.go +++ b/pkg/apis/troubleshoot/v1beta2/analyzer_shared.go @@ -1,6 +1,8 @@ package v1beta2 import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/replicatedhq/troubleshoot/pkg/multitype" ) @@ -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 {