I am new to Go. I have 2 identical json nested structures that are each populated with the output of 2 api calls. One call fetches cpu and the other memory metrics. I can unmarshal each of them individually and print out the project name and values of cpu and memory, albeit in 2 separate code blocks. The problem I am having is that I would like to print out both cpu and memory metrics on the same line, next to their project name.
Here is the code I am using to print out the CPU metrics by Project. It creates a nice CSV formatted output:
// CPU Metrics
// Loop through the data for the Month, Project, CPU requests, and CPU Usage
fmt.Println("Month, Project, CPU Request(Core hours), CPU Usage(Core hours)\n")
for _, value_cpu := range rh_values_cpu.Data {
for _, val_cpu := range value_cpu.Projects {
str := val_cpu.Project
s := strings.Contains(str, "openshift")
if s == true {
continue
}
fmt.Printf("%s, %s, ", value_cpu.Date, val_cpu.Project)
for _, v_cpu := range val_cpu.Values {
fmt.Printf("%.1f, %.1f\n", v_cpu.Request.Value, v_cpu.Usage.Value)
}
}
}
I have similar code for the memory metrics which also works fine.
Here is the code I am using to loop through the two json structures. I suspect that I'm not using the nested loops properly or need to solve the problem differently.
// CPU & Memory Metrics
// Loop through the data for the Month, Project, CPU requests, CPU Usage, Memory requests, and Memory Usage
fmt.Println("Month, Project, CPU Request(Core hours), CPU Usage(Core hours) Memory Request(mBytes), Memory Usage(mBytes)\n")
for _, value_cpu := range rh_values_cpu.Data {
for _, value_mem := range rh_values_MEM.Data {
for _, val_cpu := range value_cpu.Projects {
for _, val_mem := range value_mem.Projects {
str := val_cpu.Project
s := strings.Contains(str, "openshift")
if s == true {
continue
}
fmt.Printf("%s, %s, ", value_cpu.Date, val_cpu.Project)
for _, v_cpu := range val_cpu.Values {
fmt.Printf("%.1f, %.1f ", v_cpu.Request.Value, v_cpu.Usage.Value)
for _,v_mem := range val_mem.Values {
fmt.Printf("%.1f, %.1f\n", v_mem.Request.Value, v_mem.Usage.Value)
}
}
}
}
}
}
And here is of one the json structures:
type RH_Output_MEM struct {
Meta struct {
Count int `json:"count"`
Others int `json:"others"`
Currency string `json:"currency"`
Filter struct {
Resolution string `json:"resolution"`
TimeScopeValue string `json:"time_scope_value"`
TimeScopeUnits string `json:"time_scope_units"`
Limit int `json:"limit"`
Offset int `json:"offset"`
} `json:"filter"`
GroupBy struct {
Project []string `json:"project"`
} `json:"group_by"`
OrderBy struct {
} `json:"order_by"`
Exclude struct {
} `json:"exclude"`
Total struct {
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Request struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"request"`
Limit struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"limit"`
Capacity struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"capacity"`
Infrastructure struct {
Raw struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"raw"`
Markup struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"markup"`
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Distributed struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"distributed"`
Total struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"total"`
} `json:"infrastructure"`
Supplementary struct {
Raw struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"raw"`
Markup struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"markup"`
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Distributed struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"distributed"`
Total struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"total"`
} `json:"supplementary"`
Cost struct {
Raw struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"raw"`
Markup struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"markup"`
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Distributed struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"distributed"`
Total struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"total"`
} `json:"cost"`
} `json:"total"`
} `json:"meta"`
Links struct {
First string `json:"first"`
Next string `json:"next"`
Previous interface{} `json:"previous"`
Last string `json:"last"`
} `json:"links"`
Data []struct {
Date string `json:"date"`
Projects []struct {
Project string `json:"project"`
Values []struct {
Date string `json:"date"`
Project string `json:"project"`
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Request struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"request"`
Limit struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"limit"`
Capacity struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"capacity"`
Classification string `json:"classification"`
SourceUUID []string `json:"source_uuid"`
Clusters []string `json:"clusters"`
Infrastructure struct {
Raw struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"raw"`
Markup struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"markup"`
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Distributed struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"distributed"`
Total struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"total"`
} `json:"infrastructure"`
Supplementary struct {
Raw struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"raw"`
Markup struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"markup"`
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Distributed struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"distributed"`
Total struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"total"`
} `json:"supplementary"`
Cost struct {
Raw struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"raw"`
Markup struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"markup"`
Usage struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"usage"`
Distributed struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"distributed"`
Total struct {
Value float64 `json:"value"`
Units string `json:"units"`
} `json:"total"`
} `json:"cost"`
} `json:"values"`
} `json:"projects"`
} `json:"data"`
}
And here is a snippet of the output I am getting when running the program. As you can see, the Date, Project, and inner loop (CPU metrics) repeats itself, while the outer loop (Memory metrics) runs:
I'm looking for an output where I have one line per project (Month, Project, CPU metrics, Memory metrics)
Month, Project, CPU Request(Core hours), CPU Usage(Core hours) Memory Request(mBytes), Memory Usage(mBytes)
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 4353.2
2022-12, amq-demo-streams, 0.0, 34.0, 1115.6, 1081.4
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 10675.9
2022-12, amq-demo-streams, 0.0, 34.0, 100.9, 284.0
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 70064.5
2022-12, amq-demo-streams, 0.0, 34.0, 773088.9, 427757.8
2022-12, amq-demo-streams, 0.0, 34.0, 9440.0, 11610.3
2022-12, amq-demo-streams, 0.0, 34.0, 9471.3, 11696.9
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 2455.2
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 3.3
2022-12, amq-demo-streams, 0.0, 34.0, 0.0, 0.0
2022-12, amq-demo-streams, 0.0, 34.0, -0.3, 0.0
2022-12, amq-demo-streams, 0.0, 34.0, 3785.0, 6610.4
2022-12, amq-demo-streams, 0.0, 34.0, 252.3, 1007.8
2022-12, amq-demo-streams, 0.0, 34.0, 757.0, 883.0
2022-12, amq-demo-streams, 0.0, 34.0, 1009.4, 1613.4
2022-12, amq-demo-streams, 0.0, 34.0, 378.5, 413.5
2022-12, amq-demo-streams, 0.0, 34.0, 908.4, 2856.8
2022-12, amq-demo-streams, 0.0, 34.0, 252.3, 248.7
2022-12, amq-demo-streams, 0.0, 34.0, 66873.8, 21035.3
2022-12, amq-demo-streams, 0.0, 34.0, 353.3, 611.9
2022-12, amq-demo-streams, 0.0, 34.0, 10203.6, 12418.3
2022-12, amq-demo-streams, 0.0, 34.0, 504.7, 398.3
2022-12, amq-demo-streams, 0.0, 34.0, 1135.5, 2248.5
2022-12, amq-demo-streams, 0.0, 34.0, 252.3, 610.6
2022-12, amq-demo-streams, 0.0, 34.0, 252.3, 370.6
CodePudding user response:
I allowed myself to use simpler struct definition. You can always adapt this code to your structs.
type Cpu struct {
Project string
Data []Data
}
type Memory struct {
Project string
Data []Data
}
type Data struct {
Date string
Projects []Project
}
type Project struct {
Project string
Values []struct {
Request float64
Value float64
}
}
func CSVOutput(cpu Cpu, mem Memory) error {
// Returns an error if cpu & memory's data are the same length
if len(cpu.Data) != len(mem.Data) {
return fmt.Errorf("cpu.Data and mem.Data don't have the same length")
}
// Printing CSV file header
fmt.Println("Month, Project, CPU Request(Core hours), CPU Usage(Core hours) Memory Request(mBytes), Memory Usage(mBytes)")
for i := range cpu.Data {
cpuData := cpu.Data[i]
memData := mem.Data[i]
// Using the format from Errorf to add context to the error
if len(cpuData.Projects) != len(memData.Projects) {
return fmt.Errorf("cpu.Data[%d].Projects and mem.Data[%d].Projects don't have the same length", i, i)
}
for j := range cpuData.Projects {
cpuProject := cpuData.Projects[j]
memProject := memData.Projects[j]
if len(cpuProject.Values) != len(memProject.Values) {
return fmt.Errorf("cpu.Data[%d].Projects[%d].Values and mem.Data[%d].Projects[%d].Values don't have the same length", i, j, i, j)
}
name := cpuProject.Project
date := cpuData.Date
// Continue if the cpu project concerns openshift
if strings.Contains(name, "openshift") {
continue
}
for k := range cpuProject.Values {
cpuValue := cpuProject.Values[k]
memValue := memProject.Values[k]
fmt.Printf("%s, %s, %.1f, %.1f, %.1f, %.1f", date, name, cpuValue.Request, cpuValue.Value, memValue.Request, memValue.Value)
}
}
}
return nil
}
This code only works if you receive as much cpu's data as memory's data.
If it isn't the case, you will have to find a way to link a certain cpu's data to its memory equivalent.
This issue can be furthermore discussed if you think the situation might show up.