Просмотр исходного кода

Add support for label-mapped filters

Michael Dresser 4 лет назад
Родитель
Сommit
ede1685cbf
2 измененных файлов с 72 добавлено и 80 удалено
  1. 48 79
      pkg/util/filterutil/allocationfilters.go
  2. 24 1
      pkg/util/filterutil/allocationfilters_test.go

+ 48 - 79
pkg/util/filterutil/allocationfilters.go

@@ -9,12 +9,9 @@ import (
 	"github.com/kubecost/cost-model/pkg/util/httputil"
 )
 
-func FiltersFromParamsV1(qp httputil.QueryParams) kubecost.AllocationFilter {
-	if qp.Get("filter", "") != "" {
-		// TODO: short-circuit to a query language parser if the filter= param is
-		// present.
-	}
-
+// TODO: Make sure KCM callers provide a label config if possible/
+// necessary
+func FiltersFromParamsV1(qp httputil.QueryParams, labelConfig *kubecost.LabelConfig) kubecost.AllocationFilter {
 	// TODO: wildcard handling
 
 	filter := kubecost.AllocationFilterAnd{
@@ -24,19 +21,6 @@ func FiltersFromParamsV1(qp httputil.QueryParams) kubecost.AllocationFilter {
 	// TODO: remove comment
 	// The following is adapted from KCM's original pkg/allocation/filters.go
 
-	// Load Label Config
-	// Pull a LabelConfig from the app configuration, or default if
-	// configuration is unavailable.
-	// labelConfig := kubecost.NewLabelConfig()
-
-	// TODO: label config from analyzer in OSS?
-	// cfg, err := config.GetAnalyzerConfig()
-	// if err != nil {
-	// 	log.Warnf("AnalyzerConfig is nil")
-	// } else {
-	// 	labelConfig = cfg.LabelConfig()
-	// }
-
 	filter.Filters = append(filter.Filters,
 		filterV1SingleValueFromList(qp.GetList("filterClusters", ","), kubecost.FilterClusterID),
 	)
@@ -106,66 +90,26 @@ func FiltersFromParamsV1(qp httputil.QueryParams) kubecost.AllocationFilter {
 		filterV1SingleValueFromList(qp.GetList("filterContainers", ","), kubecost.FilterContainer),
 	)
 
-	// TODO: label mapping special things
-	// filterDepartments := qp.GetList("filterDepartments", ",")
-	// if len(filterDepartments) > 0 {
-	// 	subFilter := kubecost.AllocationFilterOr{
-	// 		Filters: []kubecost.AllocationFilter{},
-	// 	}
-
-	// 	for _, filter := range filterDepartments {
-	// 		ffs = append(ffs, GetDepartmentFilterFunc(labelConfig, filter))
-	// 	}
-	// 	filter.Filters = append(filter.Filters, subFilter)
-	// }
-
-	// filterEnvironments := qp.GetList("filterEnvironments", ",")
-	// if len(filterEnvironments) > 0 {
-	// 	subFilter := kubecost.AllocationFilterOr{
-	// 		Filters: []kubecost.AllocationFilter{},
-	// 	}
-
-	// 	for _, filter := range filterEnvironments {
-	// 		ffs = append(ffs, GetEnvironmentFilterFunc(labelConfig, filter))
-	// 	}
-	// 	filter.Filters = append(filter.Filters, subFilter)
-	// }
-
-	// filterOwners := qp.GetList("filterOwners", ",")
-	// if len(filterOwners) > 0 {
-	// 	subFilter := kubecost.AllocationFilterOr{
-	// 		Filters: []kubecost.AllocationFilter{},
-	// 	}
-
-	// 	for _, filter := range filterOwners {
-	// 		ffs = append(ffs, GetOwnerFilterFunc(labelConfig, filter))
-	// 	}
-	// 	filter.Filters = append(filter.Filters, subFilter)
-	// }
-
-	// filterProducts := qp.GetList("filterProducts", ",")
-	// if len(filterProducts) > 0 {
-	// 	subFilter := kubecost.AllocationFilterOr{
-	// 		Filters: []kubecost.AllocationFilter{},
-	// 	}
-
-	// 	for _, filter := range filterProducts {
-	// 		ffs = append(ffs, GetProductFilterFunc(labelConfig, filter))
-	// 	}
-	// 	filter.Filters = append(filter.Filters, subFilter)
-	// }
-
-	// filterTeams := qp.GetList("filterTeams", ",")
-	// if len(filterTeams) > 0 {
-	// 	subFilter := kubecost.AllocationFilterOr{
-	// 		Filters: []kubecost.AllocationFilter{},
-	// 	}
-
-	// 	for _, filter := range filterTeams {
-	// 		ffs = append(ffs, GetTeamFilterFunc(labelConfig, filter))
-	// 	}
-	// 	filter.Filters = append(filter.Filters, subFilter)
-	// }
+	// Label-mapped queries require a label config to be present.
+	if labelConfig != nil {
+		filter.Filters = append(filter.Filters,
+			filterV1LabelMappedFromList(qp.GetList("filterDepartments", ","), labelConfig.DepartmentLabel),
+		)
+		filter.Filters = append(filter.Filters,
+			filterV1LabelMappedFromList(qp.GetList("filterEnvironments", ","), labelConfig.EnvironmentLabel),
+		)
+		filter.Filters = append(filter.Filters,
+			filterV1LabelMappedFromList(qp.GetList("filterOwners", ","), labelConfig.OwnerLabel),
+		)
+		filter.Filters = append(filter.Filters,
+			filterV1LabelMappedFromList(qp.GetList("filterProducts", ","), labelConfig.ProductLabel),
+		)
+		filter.Filters = append(filter.Filters,
+			filterV1LabelMappedFromList(qp.GetList("filterTeams", ","), labelConfig.TeamLabel),
+		)
+	} else {
+		log.Debugf("No label config is available. Not creating filters for label-mapped 'fields'.")
+	}
 
 	filter.Filters = append(filter.Filters,
 		filterV1DoubleValueFromList(qp.GetList("filterAnnotations", ","), kubecost.FilterAnnotation),
@@ -214,6 +158,31 @@ func filterV1SingleValueFromList(rawFilterValues []string, filterField kubecost.
 	return filter
 }
 
+// TODO: comment
+// We don't need the filter op because all filter V1 comparisons are equality
+// We don't need the filter field because it is always a label
+func filterV1LabelMappedFromList(rawFilterValues []string, labelName string) kubecost.AllocationFilter {
+	// The v1 query language (e.g. "filterNamespaces=XYZ,ABC") uses or within
+	// a field (e.g. namespace = XYZ OR namespace = ABC)
+	filter := kubecost.AllocationFilterOr{
+		Filters: []kubecost.AllocationFilter{},
+	}
+
+	for _, filterValue := range rawFilterValues {
+		filterValue = strings.TrimSpace(filterValue)
+
+		filter.Filters = append(filter.Filters,
+			kubecost.AllocationFilterCondition{
+				Field: kubecost.FilterLabel,
+				Op:    kubecost.FilterEquals,
+				Key:   labelName,
+				Value: filterValue,
+			})
+	}
+
+	return filter
+}
+
 // TODO: comment
 // We don't need the filter op because all filter V1 comparisons are equality
 func filterV1DoubleValueFromList(rawFilterValuesUnsplit []string, filterField kubecost.FilterField) kubecost.AllocationFilter {

+ 24 - 1
pkg/util/filterutil/allocationfilters_test.go

@@ -154,6 +154,26 @@ func TestFiltersFromParamsV1(t *testing.T) {
 				}),
 			},
 		},
+		{
+			name: "single department",
+			qp: map[string]string{
+				"filterDepartments": "pa-1",
+			},
+			shouldMatch: []kubecost.Allocation{
+				allocGenerator(kubecost.AllocationProperties{
+					Labels: map[string]string{
+						"internal-product-umbrella": "pa-1",
+					},
+				}),
+			},
+			shouldNotMatch: []kubecost.Allocation{
+				allocGenerator(kubecost.AllocationProperties{
+					Labels: map[string]string{
+						"internal-product-umbrella": "ps-N",
+					},
+				}),
+			},
+		},
 		{
 			name: "single label",
 			qp: map[string]string{
@@ -264,7 +284,10 @@ func TestFiltersFromParamsV1(t *testing.T) {
 			}
 			qpMapper := mapper.NewMapper(qpMap)
 
-			filter := FiltersFromParamsV1(qpMapper)
+			labelConfig := kubecost.LabelConfig{}
+			labelConfig.DepartmentLabel = "internal-product-umbrella"
+
+			filter := FiltersFromParamsV1(qpMapper, &labelConfig)
 			for _, alloc := range c.shouldMatch {
 				if !filter.Matches(&alloc) {
 					t.Errorf("should have matched: %s", alloc.Name)