|
@@ -25,20 +25,12 @@ const (
|
|
|
) +
|
|
) +
|
|
|
sum(avg(container_fs_limit_bytes{device!="tmpfs", id="/"} %s) by (instance) / 1024 / 1024 / 1024) * 0.04`
|
|
sum(avg(container_fs_limit_bytes{device!="tmpfs", id="/"} %s) by (instance) / 1024 / 1024 / 1024) * 0.04`
|
|
|
|
|
|
|
|
- queryTotal = `sum(
|
|
|
|
|
- avg(kube_node_status_capacity_cpu_cores %s) by (node) * avg(node_cpu_hourly_cost %s) by (node) * 730 +
|
|
|
|
|
- avg(node_gpu_hourly_cost %s) by (node) * 730
|
|
|
|
|
- ) +
|
|
|
|
|
-
|
|
|
|
|
|
|
+ queryTotal = `sum(avg(node_total_hourly_cost) by (node)) * 730 +
|
|
|
sum(
|
|
sum(
|
|
|
- avg(kube_node_status_capacity_memory_bytes %s) by (node) / 1024 / 1024 / 1024 * avg(node_ram_hourly_cost %s) by (node) * 730
|
|
|
|
|
|
|
+ avg(avg_over_time(pv_hourly_cost[1h])) by (persistentvolume) * 730
|
|
|
|
|
+ * avg(avg_over_time(kube_persistentvolume_capacity_bytes[1h])) by (persistentvolume) / 1024 / 1024 / 1024
|
|
|
) +
|
|
) +
|
|
|
-
|
|
|
|
|
- sum(
|
|
|
|
|
- avg(avg_over_time(pv_hourly_cost[%s] %s)) by (persistentvolume) * 730
|
|
|
|
|
- * avg(avg_over_time(kube_persistentvolume_capacity_bytes[%s] %s)) by (persistentvolume) / 1024 / 1024 / 1024
|
|
|
|
|
- ) +
|
|
|
|
|
- sum(avg(container_fs_limit_bytes{device!="tmpfs", id="/"} %s) by (instance) / 1024 / 1024 / 1024) * 0.04`
|
|
|
|
|
|
|
+ sum(avg(container_fs_limit_bytes{device!="tmpfs", id="/"}) by (instance) / 1024 / 1024 / 1024) * 0.04`
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
type Totals struct {
|
|
type Totals struct {
|
|
@@ -48,7 +40,7 @@ type Totals struct {
|
|
|
StorageCost [][]string `json:"storageCost"`
|
|
StorageCost [][]string `json:"storageCost"`
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func resultToTotal(qr interface{}) ([][]string, error) {
|
|
|
|
|
|
|
+func resultToTotals(qr interface{}) ([][]string, error) {
|
|
|
data, ok := qr.(map[string]interface{})["data"]
|
|
data, ok := qr.(map[string]interface{})["data"]
|
|
|
if !ok {
|
|
if !ok {
|
|
|
return nil, fmt.Errorf("Improperly formatted response from prometheus, response %+v has no data field", data)
|
|
return nil, fmt.Errorf("Improperly formatted response from prometheus, response %+v has no data field", data)
|
|
@@ -82,6 +74,95 @@ func resultToTotal(qr interface{}) ([][]string, error) {
|
|
|
return totals, nil
|
|
return totals, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+func resultToTotal(qr interface{}) ([][]string, error) {
|
|
|
|
|
+ data, ok := qr.(map[string]interface{})["data"]
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ return nil, fmt.Errorf("Improperly formatted response from prometheus, response %+v has no data field", data)
|
|
|
|
|
+ }
|
|
|
|
|
+ r, ok := data.(map[string]interface{})["result"]
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ return nil, fmt.Errorf("Improperly formatted data from prometheus, data has no result field")
|
|
|
|
|
+ }
|
|
|
|
|
+ results, ok := r.([]interface{})
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ return nil, fmt.Errorf("Improperly formatted results from prometheus, result field is not a slice")
|
|
|
|
|
+ }
|
|
|
|
|
+ val, ok := results[0].(map[string]interface{})["value"]
|
|
|
|
|
+ totals := [][]string{}
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ return nil, fmt.Errorf("Improperly formatted results from prometheus, value is not a field in the vector")
|
|
|
|
|
+ }
|
|
|
|
|
+ dataPoint, ok := val.([]interface{})
|
|
|
|
|
+ //log.Printf("%+v", dataPoint)
|
|
|
|
|
+ if !ok || len(dataPoint) != 2 {
|
|
|
|
|
+ return nil, fmt.Errorf("Improperly formatted datapoint from Prometheus")
|
|
|
|
|
+ }
|
|
|
|
|
+ d0 := fmt.Sprintf("%f", dataPoint[0].(float64))
|
|
|
|
|
+ toAppend := []string{
|
|
|
|
|
+ d0,
|
|
|
|
|
+ dataPoint[1].(string),
|
|
|
|
|
+ }
|
|
|
|
|
+ totals = append(totals, toAppend)
|
|
|
|
|
+ return totals, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// ClusterCostsOverTime gives the current full cluster costs averaged over a window of time.
|
|
|
|
|
+func ClusterCosts(cli prometheusClient.Client, windowString, offset string) (*Totals, error) {
|
|
|
|
|
+
|
|
|
|
|
+ qCores := fmt.Sprintf(queryClusterCores, offset, offset, offset)
|
|
|
|
|
+ qRAM := fmt.Sprintf(queryClusterRAM, offset, offset)
|
|
|
|
|
+ qStorage := fmt.Sprintf(queryStorage, windowString, offset, windowString, offset, offset)
|
|
|
|
|
+ qTotal := fmt.Sprintf(queryTotal)
|
|
|
|
|
+ log.Printf("%s", qTotal)
|
|
|
|
|
+
|
|
|
|
|
+ resultClusterCores, err := query(cli, qCores)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+ resultClusterRAM, err := query(cli, qRAM)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ resultStorage, err := query(cli, qStorage)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ resultTotal, err := query(cli, qTotal)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ coreTotal, err := resultToTotal(resultClusterCores)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ramTotal, err := resultToTotal(resultClusterRAM)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ storageTotal, err := resultToTotal(resultStorage)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ clusterTotal, err := resultToTotal(resultTotal)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return nil, err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return &Totals{
|
|
|
|
|
+ TotalCost: clusterTotal,
|
|
|
|
|
+ CPUCost: coreTotal,
|
|
|
|
|
+ MemCost: ramTotal,
|
|
|
|
|
+ StorageCost: storageTotal,
|
|
|
|
|
+ }, nil
|
|
|
|
|
+
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// ClusterCostsOverTime gives the full cluster costs over time
|
|
// ClusterCostsOverTime gives the full cluster costs over time
|
|
|
func ClusterCostsOverTime(cli prometheusClient.Client, startString, endString, windowString, offset string) (*Totals, error) {
|
|
func ClusterCostsOverTime(cli prometheusClient.Client, startString, endString, windowString, offset string) (*Totals, error) {
|
|
|
|
|
|
|
@@ -106,7 +187,7 @@ func ClusterCostsOverTime(cli prometheusClient.Client, startString, endString, w
|
|
|
qCores := fmt.Sprintf(queryClusterCores, offset, offset, offset)
|
|
qCores := fmt.Sprintf(queryClusterCores, offset, offset, offset)
|
|
|
qRAM := fmt.Sprintf(queryClusterRAM, offset, offset)
|
|
qRAM := fmt.Sprintf(queryClusterRAM, offset, offset)
|
|
|
qStorage := fmt.Sprintf(queryStorage, windowString, offset, windowString, offset, offset)
|
|
qStorage := fmt.Sprintf(queryStorage, windowString, offset, windowString, offset, offset)
|
|
|
- qTotal := fmt.Sprintf(queryTotal, offset, offset, offset, offset, offset, windowString, offset, windowString, offset, offset)
|
|
|
|
|
|
|
+ qTotal := fmt.Sprintf(queryTotal)
|
|
|
log.Printf("%s", qTotal)
|
|
log.Printf("%s", qTotal)
|
|
|
|
|
|
|
|
resultClusterCores, err := queryRange(cli, qCores, start, end, window)
|
|
resultClusterCores, err := queryRange(cli, qCores, start, end, window)
|
|
@@ -128,22 +209,22 @@ func ClusterCostsOverTime(cli prometheusClient.Client, startString, endString, w
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- coreTotal, err := resultToTotal(resultClusterCores)
|
|
|
|
|
|
|
+ coreTotal, err := resultToTotals(resultClusterCores)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- ramTotal, err := resultToTotal(resultClusterRAM)
|
|
|
|
|
|
|
+ ramTotal, err := resultToTotals(resultClusterRAM)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- storageTotal, err := resultToTotal(resultStorage)
|
|
|
|
|
|
|
+ storageTotal, err := resultToTotals(resultStorage)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- clusterTotal, err := resultToTotal(resultTotal)
|
|
|
|
|
|
|
+ clusterTotal, err := resultToTotals(resultTotal)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
return nil, err
|
|
return nil, err
|
|
|
}
|
|
}
|