Quellcode durchsuchen

Fix __unallocated__ filter behavior for labels

toCompareMissing special behavior was causing __unallocated__ logic to
be short-circuited.
Michael Dresser vor 3 Jahren
Ursprung
Commit
fb284e88e3

+ 10 - 9
pkg/kubecost/allocationfilter.go

@@ -208,12 +208,9 @@ func (filter AllocationFilterCondition) Matches(a *Allocation) bool {
 
 	switch filter.Op {
 	case FilterEquals:
-		if toCompareMissing {
-			return false
-		}
-
 		// namespace:"__unallocated__" should match a.Properties.Namespace = ""
-		if valueToCompare == "" {
+		// label[app]:"__unallocated__" should match _, ok := Labels[app]; !ok
+		if toCompareMissing || valueToCompare == "" {
 			return filter.Value == UnallocatedSuffix
 		}
 
@@ -221,16 +218,20 @@ func (filter AllocationFilterCondition) Matches(a *Allocation) bool {
 			return true
 		}
 	case FilterNotEquals:
-		if toCompareMissing {
-			return true
-		}
-
 		// namespace!:"__unallocated__" should match
 		// a.Properties.Namespace != ""
+		// label[app]!:"__unallocated__" should match _, ok := Labels[app]; ok
 		if filter.Value == UnallocatedSuffix {
+			if toCompareMissing {
+				return false
+			}
 			return valueToCompare != ""
 		}
 
+		if toCompareMissing {
+			return true
+		}
+
 		if valueToCompare != filter.Value {
 			return true
 		}

+ 72 - 0
pkg/kubecost/allocationfilter_test.go

@@ -246,6 +246,78 @@ func Test_AllocationFilterCondition_Matches(t *testing.T) {
 
 			expected: false,
 		},
+		{
+			name: `label[app]=Unallocated -> label missing -> true`,
+			a: &Allocation{
+				Properties: &AllocationProperties{
+					Labels: map[string]string{
+						"someotherlabel": "someothervalue",
+					},
+				},
+			},
+			filter: AllocationFilterCondition{
+				Field: FilterLabel,
+				Op:    FilterEquals,
+				Key:   "app",
+				Value: UnallocatedSuffix,
+			},
+
+			expected: true,
+		},
+		{
+			name: `label[app]=Unallocated -> label present -> false`,
+			a: &Allocation{
+				Properties: &AllocationProperties{
+					Labels: map[string]string{
+						"app": "test",
+					},
+				},
+			},
+			filter: AllocationFilterCondition{
+				Field: FilterLabel,
+				Op:    FilterEquals,
+				Key:   "app",
+				Value: UnallocatedSuffix,
+			},
+
+			expected: false,
+		},
+		{
+			name: `label[app]!=Unallocated -> label missing -> false`,
+			a: &Allocation{
+				Properties: &AllocationProperties{
+					Labels: map[string]string{
+						"someotherlabel": "someothervalue",
+					},
+				},
+			},
+			filter: AllocationFilterCondition{
+				Field: FilterLabel,
+				Op:    FilterNotEquals,
+				Key:   "app",
+				Value: UnallocatedSuffix,
+			},
+
+			expected: false,
+		},
+		{
+			name: `label[app]!=Unallocated -> label present -> true`,
+			a: &Allocation{
+				Properties: &AllocationProperties{
+					Labels: map[string]string{
+						"app": "test",
+					},
+				},
+			},
+			filter: AllocationFilterCondition{
+				Field: FilterLabel,
+				Op:    FilterNotEquals,
+				Key:   "app",
+				Value: UnallocatedSuffix,
+			},
+
+			expected: true,
+		},
 		{
 			name: `label[app]!="foo" -> label missing -> true`,
 			a: &Allocation{

+ 38 - 0
pkg/util/allocationfilterutil/v2/parser_test.go

@@ -382,6 +382,44 @@ services!:"abc123"
 				allocGenerator(kubecost.AllocationProperties{ControllerKind: ""}),
 			},
 		},
+		{
+			input: `label[app]:"__unallocated__"`,
+			expected: kubecost.AllocationFilterAnd{[]kubecost.AllocationFilter{
+				kubecost.AllocationFilterOr{[]kubecost.AllocationFilter{
+					kubecost.AllocationFilterCondition{
+						Field: kubecost.FilterLabel,
+						Key:   "app",
+						Op:    kubecost.FilterEquals,
+						Value: kubecost.UnallocatedSuffix,
+					},
+				}},
+			}},
+			shouldMatch: []kubecost.Allocation{
+				allocGenerator(kubecost.AllocationProperties{Labels: map[string]string{"foo": "bar"}}),
+			},
+			shouldNotMatch: []kubecost.Allocation{
+				allocGenerator(kubecost.AllocationProperties{Labels: map[string]string{"app": "test"}}),
+			},
+		},
+		{
+			input: `label[app]!:"__unallocated__"`,
+			expected: kubecost.AllocationFilterAnd{[]kubecost.AllocationFilter{
+				kubecost.AllocationFilterAnd{[]kubecost.AllocationFilter{
+					kubecost.AllocationFilterCondition{
+						Field: kubecost.FilterLabel,
+						Key:   "app",
+						Op:    kubecost.FilterNotEquals,
+						Value: kubecost.UnallocatedSuffix,
+					},
+				}},
+			}},
+			shouldMatch: []kubecost.Allocation{
+				allocGenerator(kubecost.AllocationProperties{Labels: map[string]string{"app": "test"}}),
+			},
+			shouldNotMatch: []kubecost.Allocation{
+				allocGenerator(kubecost.AllocationProperties{Labels: map[string]string{"foo": "bar"}}),
+			},
+		},
 		{
 			input: `services:"__unallocated__"`,
 			expected: kubecost.AllocationFilterAnd{[]kubecost.AllocationFilter{