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

Allocation ETL: on-demand external cost; implement Properties.AggregationStrings

Niko Kovacevic 5 лет назад
Родитель
Сommit
ff30e4d66a
2 измененных файлов с 106 добавлено и 2 удалено
  1. 52 2
      pkg/kubecost/properties.go
  2. 54 0
      pkg/kubecost/properties_test.go

+ 52 - 2
pkg/kubecost/properties.go

@@ -234,10 +234,60 @@ func (p *Properties) String() string {
 	return fmt.Sprintf("{%s}", strings.Join(strs, "; "))
 }
 
-// TODO niko/allocation-etl
+// AggregationStrings converts a Properties object into a slice of strings
+// representing a request to aggregate by certain properties.
+// NOTE: today, the ordering of the properties *has to match the ordering
+// of the allocaiton function generateKey*
 func (p *Properties) AggregationStrings() []string {
+	if p == nil {
+		return []string{}
+	}
 
-	return []string{}
+	aggStrs := []string{}
+	if p.HasCluster() {
+		aggStrs = append(aggStrs, ClusterProp.String())
+	}
+	if p.HasNode() {
+		aggStrs = append(aggStrs, NodeProp.String())
+	}
+	if p.HasNamespace() {
+		aggStrs = append(aggStrs, NamespaceProp.String())
+	}
+	if p.HasControllerKind() {
+		aggStrs = append(aggStrs, ControllerKindProp.String())
+	}
+	if p.HasController() {
+		aggStrs = append(aggStrs, ControllerProp.String())
+	}
+	if p.HasPod() {
+		aggStrs = append(aggStrs, PodProp.String())
+	}
+	if p.HasContainer() {
+		aggStrs = append(aggStrs, ContainerProp.String())
+	}
+	if p.HasService() {
+		aggStrs = append(aggStrs, ServiceProp.String())
+	}
+	if p.HasLabel() {
+		// e.g. expect format map[string]string{
+		// 	 "env":""
+		// 	 "app":"",
+		// }
+		// for aggregating by "label:app,label:env"
+		labels, _ := p.GetLabels()
+		labelAggStrs := []string{}
+		for labelName := range labels {
+			labelAggStrs = append(labelAggStrs, fmt.Sprintf("label:%s", labelName))
+		}
+		if len(labelAggStrs) > 0 {
+			// Enforce alphabetical ordering, then append to aggStrs
+			sort.Strings(labelAggStrs)
+			for _, labelName := range labelAggStrs {
+				aggStrs = append(aggStrs, labelName)
+			}
+		}
+	}
+	return aggStrs
 }
 
 func (p *Properties) Get(prop Property) (string, error) {

+ 54 - 0
pkg/kubecost/properties_test.go

@@ -1,11 +1,65 @@
 package kubecost
 
+import "testing"
+
 // TODO niko/etl
 // func TestParseProperty(t *testing.T) {}
 
 // TODO niko/etl
 // func TestProperty_String(t *testing.T) {}
 
+func TestProperties_AggregationString(t *testing.T) {
+	var props *Properties
+	var aggStrs []string
+
+	// nil Properties should produce and empty slice
+	aggStrs = props.AggregationStrings()
+	if aggStrs == nil || len(aggStrs) > 0 {
+		t.Fatalf("expected empty slice; got %v", aggStrs)
+	}
+
+	// empty Properties should product an empty slice
+	props = &Properties{}
+	aggStrs = props.AggregationStrings()
+	if aggStrs == nil || len(aggStrs) > 0 {
+		t.Fatalf("expected empty slice; got %v", aggStrs)
+	}
+
+	// Properties with single, simple property set
+	props = &Properties{}
+	props.SetNamespace("")
+	aggStrs = props.AggregationStrings()
+	if len(aggStrs) != 1 || aggStrs[0] != "namespace" {
+		t.Fatalf("expected [\"namespace\"]; got %v", aggStrs)
+	}
+
+	// Properties with mutiple properties, including labels
+	// Note: order matters!
+	props = &Properties{}
+	props.SetNamespace("")
+	props.SetLabels(map[string]string{
+		"env": "",
+		"app": "",
+	})
+	props.SetCluster("")
+	aggStrs = props.AggregationStrings()
+	if len(aggStrs) != 4 {
+		t.Fatalf("expected length %d; got lenfth %d", 4, len(aggStrs))
+	}
+	if aggStrs[0] != "cluster" {
+		t.Fatalf("expected aggStrs[0] == \"%s\"; got \"%s\"", "cluster", aggStrs[0])
+	}
+	if aggStrs[1] != "namespace" {
+		t.Fatalf("expected aggStrs[1] == \"%s\"; got \"%s\"", "namespace", aggStrs[1])
+	}
+	if aggStrs[2] != "label:app" {
+		t.Fatalf("expected aggStrs[2] == \"%s\"; got \"%s\"", "label:app", aggStrs[2])
+	}
+	if aggStrs[3] != "label:env" {
+		t.Fatalf("expected aggStrs[3] == \"%s\"; got \"%s\"", "label:env", aggStrs[3])
+	}
+}
+
 // TODO niko/etl
 // func TestProperties_Clone(t *testing.T) {}