فهرست منبع

fix errors, port over allocation and asset props enhancements

Signed-off-by: Matt Bolt <mbolt35@gmail.com>
Matt Bolt 2 سال پیش
والد
کامیت
a8368fec66
3فایلهای تغییر یافته به همراه150 افزوده شده و 28 حذف شده
  1. 106 24
      core/pkg/kubecost/allocationprops.go
  2. 39 0
      core/pkg/kubecost/assetprops.go
  3. 5 4
      pkg/costmodel/aggregation.go

+ 106 - 24
core/pkg/kubecost/allocationprops.go

@@ -9,31 +9,113 @@ import (
 	"github.com/opencost/opencost/core/pkg/util/promutil"
 )
 
+// AllocationProperty represents a specific property on an allocation, which
+// provides utility for extracting custom property metadata.
+type AllocationProperty string
+
+// IsLabel returns true if the allocation property has a label prefix
+func (apt *AllocationProperty) IsLabel() bool {
+	return strings.HasPrefix(string(*apt), "label:")
+}
+
+// GetLabel returns the label string associated with the label property if it exists.
+// Otherwise, empty string is returned.
+func (apt *AllocationProperty) GetLabel() string {
+	if apt.IsLabel() {
+		return strings.TrimSpace(strings.TrimPrefix(string(*apt), "label:"))
+	}
+	return ""
+}
+
+// IsAnnotation returns true if the allocation property has an annotation prefix
+func (apt *AllocationProperty) IsAnnotation() bool {
+	return strings.HasPrefix(string(*apt), "annotation:")
+}
+
+// GetAnnotation returns the annotation string associated with the property if it exists.
+// Otherwise, empty string is returned.
+func (apt *AllocationProperty) GetAnnotation() string {
+	if apt.IsAnnotation() {
+		return strings.TrimSpace(strings.TrimPrefix(string(*apt), "annotation:"))
+	}
+	return ""
+}
+
+// IsAliasedLabel returns true if the allocation property corresponds to an aliased label
+func (apt *AllocationProperty) IsAliasedLabel() bool {
+	if apt == nil {
+		return false
+	}
+
+	return *apt == AllocationDepartmentProp ||
+		*apt == AllocationEnvironmentProp ||
+		*apt == AllocationOwnerProp ||
+		*apt == AllocationProductProp ||
+		*apt == AllocationTeamProp
+}
+
+// GetAliasedLabelDefault returns the corresponding default aliased label name
+func (apt *AllocationProperty) GetAliasedLabelDefault() string {
+	switch *apt {
+	case AllocationDepartmentProp:
+		return "department"
+	case AllocationEnvironmentProp:
+		return "env"
+	case AllocationOwnerProp:
+		return "owner"
+	case AllocationProductProp:
+		return "app"
+	case AllocationTeamProp:
+		return "team"
+	default:
+		return ""
+	}
+}
+
 const (
-	AllocationNilProp            string = ""
-	AllocationClusterProp        string = "cluster"
-	AllocationNodeProp           string = "node"
-	AllocationContainerProp      string = "container"
-	AllocationControllerProp     string = "controller"
-	AllocationControllerKindProp string = "controllerKind"
-	AllocationNamespaceProp      string = "namespace"
-	AllocationPodProp            string = "pod"
-	AllocationProviderIDProp     string = "providerID"
-	AllocationServiceProp        string = "service"
-	AllocationLabelProp          string = "label"
-	AllocationAnnotationProp     string = "annotation"
-	AllocationDeploymentProp     string = "deployment"
-	AllocationStatefulSetProp    string = "statefulset"
-	AllocationDaemonSetProp      string = "daemonset"
-	AllocationJobProp            string = "job"
-	AllocationDepartmentProp     string = "department"
-	AllocationEnvironmentProp    string = "environment"
-	AllocationOwnerProp          string = "owner"
-	AllocationProductProp        string = "product"
-	AllocationTeamProp           string = "team"
+	AllocationNilProp            AllocationProperty = ""
+	AllocationClusterProp                           = "cluster"
+	AllocationNodeProp                              = "node"
+	AllocationContainerProp                         = "container"
+	AllocationControllerProp                        = "controller"
+	AllocationControllerKindProp                    = "controllerKind"
+	AllocationNamespaceProp                         = "namespace"
+	AllocationPodProp                               = "pod"
+	AllocationProviderIDProp                        = "providerID"
+	AllocationServiceProp                           = "service"
+	AllocationLabelProp                             = "label"
+	AllocationAnnotationProp                        = "annotation"
+	AllocationDeploymentProp                        = "deployment"
+	AllocationStatefulSetProp                       = "statefulset"
+	AllocationDaemonSetProp                         = "daemonset"
+	AllocationJobProp                               = "job"
+	AllocationDepartmentProp                        = "department"
+	AllocationEnvironmentProp                       = "environment"
+	AllocationOwnerProp                             = "owner"
+	AllocationProductProp                           = "product"
+	AllocationTeamProp                              = "team"
 )
 
-func ParseProperty(text string) (string, error) {
+func ParseProperties(props []string) ([]AllocationProperty, error) {
+	properties := []AllocationProperty{}
+	added := make(map[AllocationProperty]struct{})
+
+	for _, prop := range props {
+		property, err := ParseProperty(prop)
+		if err != nil {
+			return nil, fmt.Errorf("Failed to parse property: %w", err)
+		}
+
+		if _, ok := added[property]; !ok {
+			added[property] = struct{}{}
+			properties = append(properties, property)
+		}
+	}
+
+	return properties, nil
+}
+
+func ParseProperty(text string) (AllocationProperty, error) {
 	switch strings.TrimSpace(strings.ToLower(text)) {
 	case "cluster":
 		return AllocationClusterProp, nil
@@ -79,12 +161,12 @@ func ParseProperty(text string) (string, error) {
 
 	if strings.HasPrefix(text, "label:") {
 		label := promutil.SanitizeLabelName(strings.TrimSpace(strings.TrimPrefix(text, "label:")))
-		return fmt.Sprintf("label:%s", label), nil
+		return AllocationProperty(fmt.Sprintf("label:%s", label)), nil
 	}
 
 	if strings.HasPrefix(text, "annotation:") {
 		annotation := promutil.SanitizeLabelName(strings.TrimSpace(strings.TrimPrefix(text, "annotation:")))
-		return fmt.Sprintf("annotation:%s", annotation), nil
+		return AllocationProperty(fmt.Sprintf("annotation:%s", annotation)), nil
 	}
 
 	return AllocationNilProp, fmt.Errorf("invalid allocation property: %s", text)

+ 39 - 0
core/pkg/kubecost/assetprops.go

@@ -3,6 +3,8 @@ package kubecost
 import (
 	"fmt"
 	"strings"
+
+	"github.com/opencost/opencost/core/pkg/util/promutil"
 )
 
 // AssetProperty is a kind of property belonging to an Asset
@@ -42,6 +44,9 @@ const (
 	// AssetTypeProp describes the type of the Asset
 	AssetTypeProp AssetProperty = "type"
 
+	// AssetLabelProp describes a single label within an Asset.
+	AssetLabelProp AssetProperty = "label"
+
 	// AssetDepartmentProp describes the department of the Asset
 	AssetDepartmentProp AssetProperty = "department"
 
@@ -58,6 +63,34 @@ const (
 	AssetTeamProp AssetProperty = "team"
 )
 
+// IsLabel returns true if the allocation property has a label prefix
+func (apt *AssetProperty) IsLabel() bool {
+	return strings.HasPrefix(string(*apt), "label:")
+}
+
+// GetLabel returns the label string associated with the label property if it exists.
+// Otherwise, empty string is returned.
+func (apt *AssetProperty) GetLabel() string {
+	if apt.IsLabel() {
+		return strings.TrimSpace(strings.TrimPrefix(string(*apt), "label:"))
+	}
+	return ""
+}
+
+func ParseAssetProperties(values []string) ([]AssetProperty, error) {
+	props := []AssetProperty{}
+
+	for _, value := range values {
+		p, err := ParseAssetProperty(value)
+		if err != nil {
+			return nil, err
+		}
+		props = append(props, p)
+	}
+
+	return props, nil
+}
+
 // ParseAssetProperty attempts to parse a string into an AssetProperty
 func ParseAssetProperty(text string) (AssetProperty, error) {
 	switch strings.TrimSpace(strings.ToLower(text)) {
@@ -90,6 +123,12 @@ func ParseAssetProperty(text string) (AssetProperty, error) {
 	case "team":
 		return AssetTeamProp, nil
 	}
+
+	if strings.HasPrefix(text, "label:") {
+		label := promutil.SanitizeLabelName(strings.TrimSpace(strings.TrimPrefix(text, "label:")))
+		return AssetProperty(fmt.Sprintf("label:%s", label)), nil
+	}
+
 	return AssetNilProp, fmt.Errorf("invalid asset property: %s", text)
 }
 

+ 5 - 4
pkg/costmodel/aggregation.go

@@ -20,6 +20,7 @@ import (
 	"github.com/opencost/opencost/core/pkg/util"
 	"github.com/opencost/opencost/core/pkg/util/httputil"
 	"github.com/opencost/opencost/core/pkg/util/json"
+	"github.com/opencost/opencost/core/pkg/util/promutil"
 	"github.com/opencost/opencost/core/pkg/util/timeutil"
 	"github.com/opencost/opencost/pkg/cloud/models"
 	"github.com/opencost/opencost/pkg/env"
@@ -170,7 +171,7 @@ func NewSharedResourceInfo(shareResources bool, sharedNamespaces []string, label
 	// the cardinality matches
 	if len(labelNames) == len(labelValues) {
 		for i := range labelNames {
-			cleanedLname := prom.SanitizeLabelName(strings.Trim(labelNames[i], " "))
+			cleanedLname := promutil.SanitizeLabelName(strings.Trim(labelNames[i], " "))
 			if values, ok := sr.LabelSelectors[cleanedLname]; ok {
 				values[strings.Trim(labelValues[i], " ")] = true
 			} else {
@@ -1258,7 +1259,7 @@ func (a *Accesses) ComputeAggregateCostModel(promClient prometheusClient.Client,
 			lTrim := strings.TrimSpace(l)
 			label := strings.Split(lTrim, "=")
 			if len(label) == 2 {
-				ln := prom.SanitizeLabelName(strings.TrimSpace(label[0]))
+				ln := promutil.SanitizeLabelName(strings.TrimSpace(label[0]))
 				lv := strings.TrimSpace(label[1])
 				labelValues[ln] = append(labelValues[ln], lv)
 			} else {
@@ -1307,7 +1308,7 @@ func (a *Accesses) ComputeAggregateCostModel(promClient prometheusClient.Client,
 			aTrim := strings.TrimSpace(annot)
 			annotation := strings.Split(aTrim, "=")
 			if len(annotation) == 2 {
-				an := prom.SanitizeLabelName(strings.TrimSpace(annotation[0]))
+				an := promutil.SanitizeLabelName(strings.TrimSpace(annotation[0]))
 				av := strings.TrimSpace(annotation[1])
 				annotationValues[an] = append(annotationValues[an], av)
 			} else {
@@ -1965,7 +1966,7 @@ func (a *Accesses) AggregateCostModelHandler(w http.ResponseWriter, r *http.Requ
 	if len(subfieldStr) > 0 {
 		s := strings.Split(r.URL.Query().Get("aggregationSubfield"), ",")
 		for _, rawLabel := range s {
-			subfields = append(subfields, prom.SanitizeLabelName(rawLabel))
+			subfields = append(subfields, promutil.SanitizeLabelName(rawLabel))
 		}
 	}