1
2
3 package costs
4
5 import (
6 "fmt"
7 "math"
8 "os"
9 "strings"
10 "text/tabwriter"
11
12 "edge-infra.dev/pkg/edge/monitoring/billman/edgesql"
13 )
14
15
16
17 type MetricData struct {
18 Samples int64
19 Cost float64
20 }
21
22
23
24
25 type LogContainerData struct {
26 Bytes int64
27 Cost float64
28 }
29
30
31
32
33 type LogNodeData struct {
34 Bytes int64
35 Cost float64
36 }
37
38
39
40
41 type LogData struct {
42 Bytes int64
43 Cost float64
44 }
45
46
47
48 type Options struct {
49 Output string
50 ShowDisclaimer bool
51 ShowHeader bool
52 }
53
54
55
56 type Billing struct {
57 Cluster edgesql.EdgeCluster
58 LogContainerData
59 LogNodeData
60 LogData
61 MetricData
62 Options
63 DisplayType string
64 }
65
66
67
68
69
70
71 func LogCosts(bytes int64, rate float64) float64 {
72 gb := float64(bytes) / 1024 / 1024 / 1024
73 dollars := rate * gb
74 cost := math.Ceil(dollars*100) / 100
75 return cost
76 }
77
78
79
80
81
82
83
84
85
86
87
88
89
90 func MetricCosts(samples int64, rate float64) float64 {
91 millionSamples := float64(samples) / 1000000
92 dollars := rate * millionSamples
93 cost := math.Ceil(dollars*100) / 100
94 return cost
95 }
96
97 func printDisclamer() {
98 fmt.Fprintln(os.Stderr, `
99 ******************
100 * Note: The results shown below are only estimates.
101 * Please read our billing disclaimer at https://docs.edge-infra.dev/edge/observability/Billing/billing/
102 ******************`)
103 }
104
105
106
107
108
109 func PrintClusterCosts(b Billing) {
110 if b.Options.ShowDisclaimer {
111 printDisclamer()
112 }
113
114 w := tabwriter.NewWriter(os.Stdout, 5, 2, 2, ' ', 0)
115
116 baseCols := []string{"CLUSTER_NAME", "CLUSTER_EDGE_ID", "BANNER_NAME", "PROJECT_ID"}
117 baseFmt := []string{"%s", "%s", "%s", "%s"}
118
119 logCols := []string{"LOG_CONTAINER_BYTES", "LOG_CONTAINER_COST", "LOG_NODE_BYTES", "LOG_NODE_COST", "LOG_BYTES", "LOG_COST"}
120 logFmt := []string{"%d", "%.2f", "%d", "%.2f", "%d", "%.2f"}
121
122 metricCols := []string{"METRIC_SAMPLES", "METRIC_COST"}
123 metricFmt := []string{"%d", "%.2f"}
124
125 totalCol := []string{"TOTAL"}
126 totalFmt := []string{"%.2f\n"}
127
128 var separator string
129 switch b.Options.Output {
130 case "csv":
131 separator = ","
132 default:
133 separator = "\t"
134 }
135
136 switch b.DisplayType {
137 case "all":
138 c := append(append(append(baseCols, logCols...), metricCols...), totalCol...)
139 cFmt := append(append(append(baseFmt, logFmt...), metricFmt...), totalFmt...)
140 cols := fmt.Sprint(strings.Join(c, separator))
141 fmtStr := fmt.Sprint(strings.Join(cFmt, separator))
142 if b.Options.ShowHeader {
143 fmt.Fprintln(w, cols)
144 }
145 fmt.Fprintf(w, fmtStr,
146 b.Cluster.ClusterName,
147 b.Cluster.ClusterEdgeID,
148 b.Cluster.BannerName,
149 b.Cluster.ProjectID,
150 b.LogContainerData.Bytes,
151 b.LogContainerData.Cost,
152 b.LogNodeData.Bytes,
153 b.LogNodeData.Cost,
154 b.LogData.Bytes,
155 b.LogData.Cost,
156 b.MetricData.Samples,
157 b.MetricData.Cost,
158 b.LogData.Cost+b.MetricData.Cost,
159 )
160 case "logs":
161 c := append(baseCols, logCols...)
162 cFmt := append(baseFmt, logFmt...)
163 cols := fmt.Sprint(strings.Join(c, separator))
164 fmtStr := fmt.Sprint(strings.Join(cFmt, separator))
165 if b.ShowHeader {
166 fmt.Fprintln(w, cols)
167 }
168 fmt.Fprintf(w, fmtStr+"\n",
169 b.Cluster.ClusterName,
170 b.Cluster.ClusterEdgeID,
171 b.Cluster.BannerName,
172 b.Cluster.ProjectID,
173 b.LogContainerData.Bytes,
174 b.LogContainerData.Cost,
175 b.LogNodeData.Bytes,
176 b.LogNodeData.Cost,
177 b.LogData.Bytes,
178 b.LogData.Cost,
179 )
180 case "metrics":
181 c := append(baseCols, metricCols...)
182 cFmt := append(baseFmt, metricFmt...)
183 cols := fmt.Sprint(strings.Join(c, separator))
184 fmtStr := fmt.Sprint(strings.Join(cFmt, separator))
185 if b.Options.ShowHeader {
186 fmt.Fprintln(w, cols)
187 }
188 fmt.Fprintf(w, fmtStr+"\n",
189 b.Cluster.ClusterName,
190 b.Cluster.ClusterEdgeID,
191 b.Cluster.BannerName,
192 b.Cluster.ProjectID,
193 b.MetricData.Samples,
194 b.MetricData.Cost,
195 )
196 }
197
198 w.Flush()
199 }
200
View as plain text