/
queryservice_helper.go
149 lines (127 loc) · 4.11 KB
/
queryservice_helper.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package cloudcost
import (
"encoding/csv"
"fmt"
"net/http"
"github.com/opencost/opencost/core/pkg/filter"
"github.com/opencost/opencost/core/pkg/filter/cloudcost"
"github.com/opencost/opencost/core/pkg/opencost"
"github.com/opencost/opencost/core/pkg/util/httputil"
)
func ParseCloudCostRequest(qp httputil.QueryParams) (*QueryRequest, error) {
windowStr := qp.Get("window", "")
if windowStr == "" {
return nil, fmt.Errorf("missing require window param")
}
window, err := opencost.ParseWindowUTC(windowStr)
if err != nil {
return nil, fmt.Errorf("invalid window parameter: %w", err)
}
if window.IsOpen() {
return nil, fmt.Errorf("invalid window parameter: %s", window.String())
}
aggregateByRaw := qp.GetList("aggregate", ",")
var aggregateBy []string
for _, aggBy := range aggregateByRaw {
prop, err := opencost.ParseCloudCostProperty(aggBy)
if err != nil {
return nil, fmt.Errorf("error parsing aggregate by %v", err)
}
aggregateBy = append(aggregateBy, string(prop))
}
// if we're aggregating by nothing (aka `item` on the frontend) then aggregate by all
if len(aggregateBy) == 0 {
aggregateBy = []string{opencost.CloudCostInvoiceEntityIDProp, opencost.CloudCostAccountIDProp, opencost.CloudCostProviderProp, opencost.CloudCostProviderIDProp, opencost.CloudCostCategoryProp, opencost.CloudCostServiceProp}
}
accumulate := opencost.ParseAccumulate(qp.Get("accumulate", ""))
var filter filter.Filter
filterString := qp.Get("filter", "")
if filterString != "" {
parser := cloudcost.NewCloudCostFilterParser()
filter, err = parser.Parse(filterString)
if err != nil {
return nil, fmt.Errorf("Parsing 'filter' parameter: %s", err)
}
}
opts := &QueryRequest{
Start: *window.Start(),
End: *window.End(),
AggregateBy: aggregateBy,
Accumulate: accumulate,
Filter: filter,
}
return opts, nil
}
func parseCloudCostViewRequest(qp httputil.QueryParams) (*ViewQueryRequest, error) {
qr, err := ParseCloudCostRequest(qp)
if err != nil {
return nil, err
}
// parse cost metric
costMetricName, err := opencost.ParseCostMetricName(qp.Get("costMetric", string(opencost.CostMetricAmortizedNetCost)))
if err != nil {
return nil, fmt.Errorf("error parsing 'costMetric': %w", err)
}
limit := qp.GetInt("limit", 0)
if limit < 0 {
return nil, fmt.Errorf("invalid value for limit %d", limit)
}
offset := qp.GetInt("offset", 0)
if offset < 0 {
return nil, fmt.Errorf("invalid value for offset %d", offset)
}
// parse order
order, err := ParseSortDirection(qp.Get("sortByOrder", "desc"))
if err != nil {
return nil, fmt.Errorf("error parsing 'sortByOrder: %w", err)
}
sortColumn, err := ParseSortField(qp.Get("sortBy", "cost"))
if err != nil {
return nil, fmt.Errorf("error parsing 'sortBy': %w", err)
}
return &ViewQueryRequest{
QueryRequest: *qr,
CostMetricName: costMetricName,
ChartItemsLength: DefaultChartItemsLength,
Limit: limit,
Offset: offset,
SortDirection: order,
SortColumn: sortColumn,
}, nil
}
// CloudCostViewTableRowsToCSV takes the csv writer and writes the ViewTableRows into the writer.
func CloudCostViewTableRowsToCSV(writer *csv.Writer, ctr ViewTableRows, window string) error {
defer writer.Flush()
// Write the column headers
headers := []string{
"Name",
"K8s Utilization",
"Total",
"Window",
}
err := writer.Write(headers)
if err != nil {
return fmt.Errorf("CloudCostViewTableRowsToCSV: failed to convert ViewTableRows to csv with error: %w", err)
}
// Write one row per entry in the ViewTableRows
for _, row := range ctr {
err = writer.Write([]string{
row.Name,
fmt.Sprintf("%.3f", row.KubernetesPercent),
fmt.Sprintf("%.3f", row.Cost),
window,
})
if err != nil {
return fmt.Errorf("CloudCostViewTableRowsToCSV: failed to convert ViewTableRows to csv with error: %w", err)
}
}
return nil
}
func writeCloudCostViewTableRowsAsCSV(w http.ResponseWriter, ctr ViewTableRows, window string) {
writer := csv.NewWriter(w)
err := CloudCostViewTableRowsToCSV(writer, ctr, window)
if err != nil {
protocol.WriteError(w, protocol.InternalServerError(err.Error()))
return
}
}