Niko Kovacevic 6 лет назад
Родитель
Сommit
98aaf23bd9
3 измененных файлов с 117 добавлено и 7 удалено
  1. 115 6
      costmodel/cluster.go
  2. 1 1
      costmodel/router.go
  3. 1 0
      go.sum

+ 115 - 6
costmodel/cluster.go

@@ -3,6 +3,7 @@ package costmodel
 import (
 	"fmt"
 	"os"
+	"sync"
 	"time"
 
 	costAnalyzerCloud "github.com/kubecost/cost-model/cloud"
@@ -120,10 +121,6 @@ func ClusterCostsForAllClusters(cli prometheusClient.Client, cloud costAnalyzerC
 	qRAM := fmt.Sprintf(queryClusterRAM, window, offset, window, offset)
 	qStorage := fmt.Sprintf(queryStorage, window, offset, window, offset, localStorageQuery)
 
-	klog.Infof("[Debug] qCores: %s", qCores)
-	klog.Infof("[Debug] qRAM: %s", qRAM)
-	klog.Infof("[Debug] qStorage: %s", qStorage)
-
 	klog.V(4).Infof("Running query %s", qCores)
 	resultClusterCores, err := Query(cli, qCores)
 	if err != nil {
@@ -180,8 +177,120 @@ func ClusterCostsForAllClusters(cli prometheusClient.Client, cloud costAnalyzerC
 	return toReturn, nil
 }
 
-// ClusterCosts gives the current full cluster costs averaged over a window of time.
-func ClusterCosts(cli prometheusClient.Client, cloud costAnalyzerCloud.Provider, windowString, offset string) (*Totals, error) {
+// TODO move this to a package-accessible helper struct
+type PromQueryContext struct {
+	client prometheusClient.Client
+	ec     *ErrorCollector
+	wg     *sync.WaitGroup
+}
+
+// TODO move this to a package-accessible helper function
+func AsyncPromQuery(query string, resultCh chan []*PromQueryResult, ctx PromQueryContext) {
+	if ctx.wg != nil {
+		defer ctx.wg.Done()
+	}
+
+	raw, promErr := Query(ctx.client, query)
+	ctx.ec.Report(promErr)
+
+	results, parseErr := NewQueryResults(raw)
+	ctx.ec.Report(parseErr)
+
+	resultCh <- results
+}
+
+type ClusterCosts struct {
+	CPU     float64 `json:"cpu"`
+	RAM     float64 `json:"ram"`
+	Storage float64 `json:"storage"`
+	Total   float64 `json:"total"`
+}
+
+// CumulativeClusterCostsForAllClusters gives the cumulative cluster costs summed over a window of time for all clusters.
+func CumulativeClusterCostsForAllClusters(cli prometheusClient.Client, cloud costAnalyzerCloud.Provider, window, offset string) (map[string]ClusterCosts, error) {
+	const fmtQueryTotalCPU = `sum(
+		sum(sum_over_time(kube_node_status_capacity_cpu_cores[%s:1h]%s)) by (node, cluster_id) *
+		avg(avg_over_time(node_cpu_hourly_cost[%s:1h]%s)) by (node, cluster_id)
+	)`
+
+	const fmtQueryTotalRAM = `sum(
+		sum(sum_over_time(kube_node_status_capacity_memory_bytes[%s:1h]%s) / 1024 / 1024 / 1024) by (node, cluster_id) *
+		avg(avg_over_time(node_ram_hourly_cost[%s:1h]%s)) by (node, cluster_id)
+	)`
+
+	const fmtQueryTotalStorage = `sum(
+		sum(sum_over_time(kube_persistentvolume_capacity_bytes[%s:1h]%s)) by (persistentvolume, cluster_id) / 1024 / 1024 / 1024 *
+		avg(avg_over_time(pv_hourly_cost[%s:1h]%s)) by (persistentvolume, cluster_id)
+	)`
+
+	// TODO local storage
+
+	if offset != "" {
+		offset = fmt.Sprintf("offset %s", offset)
+	}
+
+	queryTotalCPU := fmt.Sprintf(fmtQueryTotalCPU, window, offset, window, offset)
+	queryTotalRAM := fmt.Sprintf(fmtQueryTotalRAM, window, offset, window, offset)
+	queryTotalStorage := fmt.Sprintf(fmtQueryTotalStorage, window, offset, window, offset)
+	numQueries := 3
+
+	var ec ErrorCollector
+	var wg sync.WaitGroup
+	wg.Add(numQueries)
+	ctx := PromQueryContext{cli, &ec, &wg}
+
+	klog.Infof("[Debug] queryTotalCPU: %s", queryTotalCPU)
+	chTotalCPU := make(chan []*PromQueryResult)
+	go AsyncPromQuery(queryTotalCPU, chTotalCPU, ctx)
+
+	klog.Infof("[Debug] queryTotalRAM: %s", queryTotalRAM)
+	chTotalRAM := make(chan []*PromQueryResult)
+	go AsyncPromQuery(queryTotalRAM, chTotalRAM, ctx)
+
+	klog.Infof("[Debug] queryTotalStorage: %s", queryTotalStorage)
+	chTotalStorage := make(chan []*PromQueryResult)
+	go AsyncPromQuery(queryTotalStorage, chTotalStorage, ctx)
+
+	costsPerCluster := make(map[string]ClusterCosts)
+
+	// coreTotal, err := resultToTotal(resultClusterCores)
+	// if err != nil {
+	// 	return nil, fmt.Errorf("Error for query %s: %s", qCores, err.Error())
+	// }
+	// for clusterID, total := range coreTotal {
+	// 	if _, ok := toReturn[clusterID]; !ok {
+	// 		toReturn[clusterID] = &Totals{}
+	// 	}
+	// 	toReturn[clusterID].CPUCost = total
+	// }
+
+	// ramTotal, err := resultToTotal(resultClusterRAM)
+	// if err != nil {
+	// 	return nil, fmt.Errorf("Error for query %s: %s", qRAM, err.Error())
+	// }
+	// for clusterID, total := range ramTotal {
+	// 	if _, ok := toReturn[clusterID]; !ok {
+	// 		toReturn[clusterID] = &Totals{}
+	// 	}
+	// 	toReturn[clusterID].MemCost = total
+	// }
+
+	// storageTotal, err := resultToTotal(resultStorage)
+	// if err != nil {
+	// 	return nil, fmt.Errorf("Error for query %s: %s", qStorage, err.Error())
+	// }
+	// for clusterID, total := range storageTotal {
+	// 	if _, ok := toReturn[clusterID]; !ok {
+	// 		toReturn[clusterID] = &Totals{}
+	// 	}
+	// 	toReturn[clusterID].StorageCost = total
+	// }
+
+	return costsPerCluster, nil
+}
+
+// ComputeClusterCosts gives the current full cluster costs averaged over a window of time.
+func ComputeClusterCosts(cli prometheusClient.Client, cloud costAnalyzerCloud.Provider, windowString, offset string) (*Totals, error) {
 	// turn offsets of the format "[0-9+]h" into the format "offset [0-9+]h" for use in query templatess
 	if offset != "" {
 		offset = fmt.Sprintf("offset %s", offset)

+ 1 - 1
costmodel/router.go

@@ -317,7 +317,7 @@ func (a *Accesses) ClusterCosts(w http.ResponseWriter, r *http.Request, ps httpr
 	window := r.URL.Query().Get("window")
 	offset := r.URL.Query().Get("offset")
 
-	data, err := ClusterCosts(a.PrometheusClient, a.Cloud, window, offset)
+	data, err := ComputeClusterCosts(a.PrometheusClient, a.Cloud, window, offset)
 	w.Write(WrapData(data, err))
 }
 

+ 1 - 0
go.sum

@@ -23,6 +23,7 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
 github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 github.com/aws/aws-sdk-go v1.19.10 h1:WHIaUrU98WsWIXxlxeMCmbuB5HowxuUnk8eBH4iGl/g=
 github.com/aws/aws-sdk-go v1.19.10/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/aws/aws-sdk-go v1.28.9 h1:grIuBQc+p3dTRXerh5+2OxSuWFi0iXuxbFdTSg0jaW0=
 github.com/aws/aws-sdk-go v1.28.9/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=