@@ -19,16 +19,28 @@ const (
1919 ColResourceUnit = "resource unit"
2020 ColMoDiffResource = "Δ qty"
2121 ColMoDiffCost = "Δ cost/mo"
22+ ColMoResource = "qty"
23+ ColMoCost = "cost/mo"
2224 ColCostPerUnit = "cost per unit"
2325 ColPctChange = "% change"
2426)
2527
26- type PredictDisplayOptions struct {}
28+ type PredictDisplayOptions struct {
29+ // ShowTotal determines if "After" cost info will be shown alongside the
30+ // diff
31+ ShowTotal bool
32+
33+ // HideDiff will disable diff information if true
34+ HideDiff bool
35+ }
2736
2837func AddPredictDisplayOptionsFlags (cmd * cobra.Command , options * PredictDisplayOptions ) {
2938}
3039
3140func (o * PredictDisplayOptions ) Validate () error {
41+ if ! o .ShowTotal && o .HideDiff {
42+ return fmt .Errorf ("ShowTotal and HideDiff cannot be set such that no data will be shown" )
43+ }
3244 return nil
3345}
3446
@@ -42,9 +54,6 @@ func WritePredictionTable(out io.Writer, rowData []query.SpecCostDiff, currencyC
4254// the decimal places.
4355func fmtResourceFloat (x float64 ) string {
4456 s := fmt .Sprintf ("%.2f" , x )
45- if x > 0 {
46- s = fmt .Sprintf ("+%s" , s )
47- }
4857
4958 // If formatted float ends in .000, remove
5059 s = strings .TrimRight (s , "0" )
@@ -80,9 +89,6 @@ func fmtResourceCostFloat(x float64) string {
8089
8190func fmtOverallCostFloat (x float64 ) string {
8291 s := fmt .Sprintf ("%.2f" , x )
83- if x > 0 {
84- s = fmt .Sprintf ("+%s" , s )
85- }
8692 return s
8793}
8894
@@ -114,8 +120,9 @@ func MakePredictionTable(specDiffs []query.SpecCostDiff, currencyCode string, op
114120 WidthMaxEnforcer : text .WrapSoft ,
115121 },
116122 {
117- Name : ColMoDiffResource ,
118- Align : text .AlignRight ,
123+ Name : ColMoResource ,
124+ Hidden : ! opts .ShowTotal ,
125+ Align : text .AlignRight ,
119126 Transformer : func (val interface {}) string {
120127 if f , ok := val .(float64 ); ok {
121128 return fmtResourceFloat (f )
@@ -126,6 +133,24 @@ func MakePredictionTable(specDiffs []query.SpecCostDiff, currencyCode string, op
126133 return "invalid value"
127134 },
128135 },
136+ {
137+ Name : ColMoDiffResource ,
138+ Hidden : opts .HideDiff ,
139+ Align : text .AlignRight ,
140+ Transformer : func (val interface {}) string {
141+ if f , ok := val .(float64 ); ok {
142+ s := fmtResourceFloat (f )
143+ if f > 0 {
144+ s = fmt .Sprintf ("+%s" , s )
145+ }
146+ return s
147+ }
148+ if s , ok := val .(string ); ok {
149+ return s
150+ }
151+ return "invalid value"
152+ },
153+ },
129154 {
130155 Name : ColResourceUnit ,
131156 Align : text .AlignLeft ,
@@ -144,8 +169,9 @@ func MakePredictionTable(specDiffs []query.SpecCostDiff, currencyCode string, op
144169 },
145170 },
146171 {
147- Name : ColMoDiffCost ,
148- Align : text .AlignRight ,
172+ Name : ColMoCost ,
173+ Hidden : ! opts .ShowTotal ,
174+ Align : text .AlignRight ,
149175 Transformer : func (val interface {}) string {
150176 if f , ok := val .(float64 ); ok {
151177 return fmt .Sprintf ("%s %s" , fmtOverallCostFloat (f ), currencyCode )
@@ -166,8 +192,40 @@ func MakePredictionTable(specDiffs []query.SpecCostDiff, currencyCode string, op
166192 },
167193 },
168194 {
169- Name : ColPctChange ,
170- Align : text .AlignRight ,
195+ Name : ColMoDiffCost ,
196+ Hidden : opts .HideDiff ,
197+ Align : text .AlignRight ,
198+ Transformer : func (val interface {}) string {
199+ if f , ok := val .(float64 ); ok {
200+ s := fmt .Sprintf ("%s %s" , fmtOverallCostFloat (f ), currencyCode )
201+ if f > 0 {
202+ s = fmt .Sprintf ("+%s" , s )
203+ }
204+ return s
205+ }
206+ if s , ok := val .(string ); ok {
207+ return s
208+ }
209+ return "invalid value"
210+ },
211+ TransformerFooter : func (val interface {}) string {
212+ if f , ok := val .(float64 ); ok {
213+ s := fmt .Sprintf ("%s %s" , fmtOverallCostFloat (f ), currencyCode )
214+ if f > 0 {
215+ s = fmt .Sprintf ("+%s" , s )
216+ }
217+ return s
218+ }
219+ if s , ok := val .(string ); ok {
220+ return s
221+ }
222+ return "invalid value"
223+ },
224+ },
225+ {
226+ Name : ColPctChange ,
227+ Hidden : opts .HideDiff ,
228+ Align : text .AlignRight ,
171229 Transformer : func (val interface {}) string {
172230 if f , ok := val .(float64 ); ok {
173231 prefix := ""
@@ -186,89 +244,110 @@ func MakePredictionTable(specDiffs []query.SpecCostDiff, currencyCode string, op
186244
187245 t .AppendHeader (table.Row {
188246 ColObject ,
247+ ColMoResource ,
189248 ColMoDiffResource ,
190249 ColResourceUnit ,
191250 ColCostPerUnit ,
251+ ColMoCost ,
192252 ColMoDiffCost ,
193253 ColPctChange ,
194254 })
195255
196256 totalCostImpact := 0.0
257+ totalCostNew := 0.0
197258 for _ , specData := range specDiffs {
198259 totalCostImpact += specData .CostChange .TotalMonthlyRate
260+ totalCostNew += specData .CostAfter .TotalMonthlyRate
199261
200262 workloadName := fmt .Sprintf ("%s %s %s" , specData .Namespace , specData .ControllerKind , specData .ControllerName )
201263
202264 // Don't show resource if there is no cost data before or after
203265 if ! (specData .CostBefore .CPUMonthlyRate == 0 && specData .CostAfter .CPUMonthlyRate == 0 ) {
204- cpuUnits := "CPU cores"
205- avgCPUInUnits := specData .CostChange .MonthlyCPUCoreHours / timeutil .HoursPerMonth
206- if avgCPUInUnits < 1 {
207- cpuUnits = "CPU millicores"
208- avgCPUInUnits = specData .CostChange .MonthlyCPUCoreHours / timeutil .HoursPerMonth * 1000
266+ units := "CPU cores"
267+ avgUnitsNew := specData .CostAfter .MonthlyCPUCoreHours / timeutil .HoursPerMonth
268+ avgUnitsDiff := specData .CostChange .MonthlyCPUCoreHours / timeutil .HoursPerMonth
269+ factor := 1.0
270+ if avgUnitsNew * factor < 1 {
271+ units = "CPU millicores"
272+ factor = 1000
209273 }
210- costPerUnit := specData .CostChange .CPUMonthlyRate / avgCPUInUnits
211- cpuRow := table.Row {
274+ avgUnitsDiff *= factor
275+ avgUnitsNew *= factor
276+ costPerUnit := specData .CostChange .CPUMonthlyRate / avgUnitsDiff
277+ row := table.Row {
212278 workloadName ,
213- avgCPUInUnits ,
214- cpuUnits ,
279+ avgUnitsNew ,
280+ avgUnitsDiff ,
281+ units ,
215282 costPerUnit ,
283+ specData .CostAfter .CPUMonthlyRate ,
216284 specData .CostChange .CPUMonthlyRate ,
217285 }
218286 if specData .CostBefore .CPUMonthlyRate != 0 {
219- cpuRow = append (cpuRow , specData .CostChange .CPUMonthlyRate / specData .CostBefore .CPUMonthlyRate * 100 )
287+ row = append (row , specData .CostChange .CPUMonthlyRate / specData .CostBefore .CPUMonthlyRate * 100 )
220288 }
221- t .AppendRow (cpuRow )
289+ t .AppendRow (row )
222290 }
223291
224292 if ! (specData .CostBefore .RAMMonthlyRate == 0 && specData .CostAfter .RAMMonthlyRate == 0 ) {
225-
226- ramUnits := "RAM GiB"
227- ramUnitDivisor := 1024 * 1024 * 1024.0
228- avgRAMInUnits := specData .CostChange .MonthlyRAMByteHours / ramUnitDivisor / timeutil .HoursPerMonth
229- // If < 1 GiB, convert to MiB
230- if avgRAMInUnits < 1 {
231- ramUnits = "RAM MiB"
232- ramUnitDivisor = 1024 * 1024.0
233- avgRAMInUnits = specData .CostChange .MonthlyRAMByteHours / ramUnitDivisor / timeutil .HoursPerMonth
293+ units := "RAM GiB"
294+ avgUnitsNew := specData .CostAfter .MonthlyRAMByteHours / timeutil .HoursPerMonth
295+ avgUnitsDiff := specData .CostChange .MonthlyRAMByteHours / timeutil .HoursPerMonth
296+ factor := 1.0 / (1024 * 1024 * 1024 )
297+ if avgUnitsNew * factor < 1 {
298+ units = "RAM MiB"
299+ factor = 1.0 / (1024 * 1024 )
234300 }
235- costPerUnit := specData .CostChange .RAMMonthlyRate / avgRAMInUnits
236- ramRow := table.Row {
301+ avgUnitsDiff *= factor
302+ avgUnitsNew *= factor
303+ costPerUnit := specData .CostChange .RAMMonthlyRate / avgUnitsDiff
304+ row := table.Row {
237305 workloadName ,
238- avgRAMInUnits ,
239- ramUnits ,
306+ avgUnitsNew ,
307+ avgUnitsDiff ,
308+ units ,
240309 costPerUnit ,
310+ specData .CostAfter .RAMMonthlyRate ,
241311 specData .CostChange .RAMMonthlyRate ,
242312 }
243313 if specData .CostBefore .RAMMonthlyRate != 0 {
244- ramRow = append (ramRow , specData .CostChange .RAMMonthlyRate / specData .CostBefore .RAMMonthlyRate * 100 )
314+ row = append (row , specData .CostChange .RAMMonthlyRate / specData .CostBefore .RAMMonthlyRate * 100 )
245315 }
246- t .AppendRow (ramRow )
316+ t .AppendRow (row )
247317 }
248318
249319 if ! (specData .CostBefore .GPUMonthlyRate == 0 && specData .CostAfter .GPUMonthlyRate == 0 ) {
250- avgGPUs := specData .CostChange .MonthlyGPUHours / timeutil .HoursPerMonth
251- costPerGPU := specData .CostChange .GPUMonthlyRate / avgGPUs
252- gpuRow := table.Row {
320+ units := "GPUs"
321+ avgUnitsNew := specData .CostAfter .MonthlyGPUHours / timeutil .HoursPerMonth
322+ avgUnitsDiff := specData .CostChange .MonthlyGPUHours / timeutil .HoursPerMonth
323+ factor := 1.0
324+ avgUnitsDiff *= factor
325+ avgUnitsNew *= factor
326+ costPerUnit := specData .CostChange .GPUMonthlyRate / avgUnitsDiff
327+ row := table.Row {
253328 workloadName ,
254- avgGPUs ,
255- "GPUs" ,
256- costPerGPU ,
329+ avgUnitsNew ,
330+ avgUnitsDiff ,
331+ units ,
332+ costPerUnit ,
333+ specData .CostAfter .GPUMonthlyRate ,
257334 specData .CostChange .GPUMonthlyRate ,
258335 }
259336 if specData .CostBefore .GPUMonthlyRate != 0 {
260- gpuRow = append (gpuRow , specData .CostChange .GPUMonthlyRate / specData .CostBefore .GPUMonthlyRate * 100 )
337+ row = append (row , specData .CostChange .GPUMonthlyRate / specData .CostBefore .GPUMonthlyRate * 100 )
261338 }
262- t .AppendRow (gpuRow )
339+ t .AppendRow (row )
263340 }
264341 t .AppendSeparator ()
265342 }
266343
267344 t .AppendFooter (table.Row {
268- "Total monthly cost change" ,
345+ "Total monthly cost" ,
346+ "" ,
269347 "" ,
270348 "" ,
271349 "" ,
350+ totalCostNew ,
272351 totalCostImpact ,
273352 })
274353
0 commit comments