mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-25 08:31:03 +00:00
fix: handle float64 chosen by json and invalid metric
This commit is contained in:
parent
1ab3b8746d
commit
a2659b15aa
2 changed files with 166 additions and 51 deletions
|
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -19,36 +18,6 @@ import (
|
|||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
// var SCHEMA_JSON = `{
|
||||
// "client_stats": {
|
||||
// "cpu_stats": {
|
||||
// "cpu_usage": {
|
||||
// "total_usage": "nanoseconds",
|
||||
// "percpu_usage": ["nanoseconds"],
|
||||
// "usage_in_kernelmode": "nanoseconds",
|
||||
// "usage_in_usermode": "nanoseconds"
|
||||
// },
|
||||
// "system_cpu_usage": "nanoseconds"
|
||||
// },
|
||||
// "precpu_stats": {
|
||||
// "cpu_usage": {
|
||||
// "total_usage": "nanoseconds",
|
||||
// "percpu_usage": ["nanoseconds"],
|
||||
// "usage_in_kernelmode": "nanoseconds",
|
||||
// "usage_in_usermode": "nanoseconds"
|
||||
// },
|
||||
// "system_cpu_usage": "nanoseconds"
|
||||
// },
|
||||
// "memory_stats": {
|
||||
// "stats": {
|
||||
// "hierarchical_memory_limit": "bytes",
|
||||
// "hierarchical_memsw_limit": "bytes"
|
||||
// },
|
||||
// "limit": "bytes"
|
||||
// }
|
||||
// }
|
||||
// }`
|
||||
|
||||
var PATHS_TO_CONVERT_BIGMETRICS = []map[string]string{
|
||||
{"ClientStats.cpu_stats.cpu_usage.total_usage": "nanoseconds"},
|
||||
{"ClientStats.cpu_stats.cpu_usage.percpu_usage": "nanoseconds"},
|
||||
|
|
@ -92,19 +61,14 @@ func RenderStats(userConfig *config.UserConfig, container *commands.Container, v
|
|||
|
||||
var statsMap map[string]interface{}
|
||||
err = json.Unmarshal(statsJsonBytes, &statsMap)
|
||||
b, _ := json.MarshalIndent(statsMap, "", " ")
|
||||
_ = os.WriteFile("in.json", b, 0644)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// err = convertBigMetricFromSchema(&statsMap)
|
||||
// b, _ = json.MarshalIndent(statsMap, "", " ")
|
||||
// _ = os.WriteFile("out.json", b, 0644)
|
||||
// if err != nil {
|
||||
// _ = os.WriteFile("out", []byte(err.Error()), 0644)
|
||||
// return "", err
|
||||
// }
|
||||
err = convertBigMetricFromSchema(&statsMap)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
originalStats, err := utils.MarshalIntoYaml(statsMap)
|
||||
if err != nil {
|
||||
|
|
@ -264,14 +228,19 @@ func convertBigMetricFromSchema(data *map[string]interface{}) error {
|
|||
}
|
||||
return err
|
||||
}
|
||||
if reflect.TypeOf(metric.Interface()) == reflect.TypeOf(int64(0)) {
|
||||
formattedMetric := utils.FormatBigMetric(metric.Interface().(int64), metricType)
|
||||
if !metric.IsValid() {
|
||||
continue
|
||||
}
|
||||
if reflect.TypeOf(metric.Interface()) == reflect.TypeOf(float64(0)) {
|
||||
formattedMetric := utils.FormatBigMetric(int64(metric.Interface().(float64)), metricType)
|
||||
err = utils.SetObjectFieldByPath(data, fmt.Sprintf(".%s", path), formattedMetric)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if reflect.TypeOf(metric.Interface()) == reflect.TypeOf([]int64{}) {
|
||||
longIntSlice := metric.Interface().([]int64)
|
||||
} else if reflect.TypeOf(metric.Interface()) == reflect.TypeOf([]float64{}) {
|
||||
longIntSlice := lo.Map(metric.Interface().([]float64), func(val float64, index int) int64 {
|
||||
return int64(val)
|
||||
})
|
||||
formattedMetric := lo.Map(longIntSlice, func(val int64, index int) string {
|
||||
return utils.FormatBigMetric(val, metricType)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package presentation
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -14,13 +16,44 @@ func TestConvertBigMetricFromSchema(t *testing.T) {
|
|||
expectedErr string
|
||||
}
|
||||
|
||||
inJSONBytes, err := os.ReadFile("testdata/stats.json")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read testdata/stats.json: %v", err)
|
||||
}
|
||||
loadInJSON := func() map[string]interface{} {
|
||||
var m map[string]interface{}
|
||||
if err := json.Unmarshal(inJSONBytes, &m); err != nil {
|
||||
t.Fatalf("failed to unmarshal testdata/stats.json: %v", err)
|
||||
}
|
||||
return m
|
||||
}
|
||||
setNested := func(m map[string]interface{}, keys []string, val interface{}) {
|
||||
for _, k := range keys[:len(keys)-1] {
|
||||
m = m[k].(map[string]interface{})
|
||||
}
|
||||
m[keys[len(keys)-1]] = val
|
||||
}
|
||||
|
||||
inJSONExpected := loadInJSON()
|
||||
setNested(inJSONExpected, []string{"ClientStats", "memory_stats", "limit"}, "1.911GB")
|
||||
setNested(inJSONExpected, []string{"ClientStats", "memory_stats", "stats", "hierarchical_memory_limit"}, "0")
|
||||
setNested(inJSONExpected, []string{"ClientStats", "memory_stats", "stats", "hierarchical_memsw_limit"}, "0")
|
||||
setNested(inJSONExpected, []string{"ClientStats", "cpu_stats", "cpu_usage", "total_usage"}, "30.283ms")
|
||||
setNested(inJSONExpected, []string{"ClientStats", "cpu_stats", "cpu_usage", "usage_in_kernelmode"}, "15.664ms")
|
||||
setNested(inJSONExpected, []string{"ClientStats", "cpu_stats", "cpu_usage", "usage_in_usermode"}, "14.619ms")
|
||||
setNested(inJSONExpected, []string{"ClientStats", "cpu_stats", "system_cpu_usage"}, "25.994h")
|
||||
setNested(inJSONExpected, []string{"ClientStats", "precpu_stats", "cpu_usage", "total_usage"}, "30.283ms")
|
||||
setNested(inJSONExpected, []string{"ClientStats", "precpu_stats", "cpu_usage", "usage_in_kernelmode"}, "15.664ms")
|
||||
setNested(inJSONExpected, []string{"ClientStats", "precpu_stats", "cpu_usage", "usage_in_usermode"}, "14.619ms")
|
||||
setNested(inJSONExpected, []string{"ClientStats", "precpu_stats", "system_cpu_usage"}, "25.994h")
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
name: "string schema: converts int64 bytes",
|
||||
name: "string schema: converts float64 bytes",
|
||||
data: map[string]interface{}{
|
||||
"ClientStats": map[string]interface{}{
|
||||
"memory_stats": map[string]interface{}{
|
||||
"limit": int64(2048),
|
||||
"limit": float64(2048),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -38,12 +71,12 @@ func TestConvertBigMetricFromSchema(t *testing.T) {
|
|||
"ClientStats": map[string]interface{}{
|
||||
"cpu_stats": map[string]interface{}{
|
||||
"cpu_usage": map[string]interface{}{
|
||||
"total_usage": int64(100000000000),
|
||||
"percpu_usage": []int64{50000000000, 50000000000},
|
||||
"usage_in_kernelmode": int64(200000),
|
||||
"usage_in_usermode": int64(200000),
|
||||
"total_usage": float64(100000000000),
|
||||
"percpu_usage": []float64{50000000000, 50000000000},
|
||||
"usage_in_kernelmode": float64(200000),
|
||||
"usage_in_usermode": float64(200000),
|
||||
},
|
||||
"system_cpu_usage": int64(100000000000),
|
||||
"system_cpu_usage": float64(100000000000),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -61,6 +94,119 @@ func TestConvertBigMetricFromSchema(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty data is a no-op",
|
||||
data: map[string]interface{}{},
|
||||
expected: map[string]interface{}{},
|
||||
},
|
||||
{
|
||||
name: "bytes below unit threshold stays as raw number",
|
||||
data: map[string]interface{}{
|
||||
"ClientStats": map[string]interface{}{
|
||||
"memory_stats": map[string]interface{}{
|
||||
"limit": float64(500),
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"ClientStats": map[string]interface{}{
|
||||
"memory_stats": map[string]interface{}{
|
||||
"limit": "500",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "bytes at MB scale",
|
||||
data: map[string]interface{}{
|
||||
"ClientStats": map[string]interface{}{
|
||||
"memory_stats": map[string]interface{}{
|
||||
"limit": float64(1500000),
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"ClientStats": map[string]interface{}{
|
||||
"memory_stats": map[string]interface{}{
|
||||
"limit": "1.431MB",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "converts deeply nested hierarchical memory limits",
|
||||
data: map[string]interface{}{
|
||||
"ClientStats": map[string]interface{}{
|
||||
"memory_stats": map[string]interface{}{
|
||||
"stats": map[string]interface{}{
|
||||
"hierarchical_memory_limit": float64(1073741824),
|
||||
"hierarchical_memsw_limit": float64(2147483648),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"ClientStats": map[string]interface{}{
|
||||
"memory_stats": map[string]interface{}{
|
||||
"stats": map[string]interface{}{
|
||||
"hierarchical_memory_limit": "1.000GB",
|
||||
"hierarchical_memsw_limit": "2.000GB",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "converts precpu_stats branch",
|
||||
data: map[string]interface{}{
|
||||
"ClientStats": map[string]interface{}{
|
||||
"precpu_stats": map[string]interface{}{
|
||||
"cpu_usage": map[string]interface{}{
|
||||
"total_usage": float64(1500000),
|
||||
"percpu_usage": []float64{1000, 2000000000},
|
||||
},
|
||||
"system_cpu_usage": float64(1500000000000),
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"ClientStats": map[string]interface{}{
|
||||
"precpu_stats": map[string]interface{}{
|
||||
"cpu_usage": map[string]interface{}{
|
||||
"total_usage": "1.500ms",
|
||||
"percpu_usage": []string{"1.000µs", "2.000s"},
|
||||
},
|
||||
"system_cpu_usage": "1.500m",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "preserves fields not listed in PATHS_TO_CONVERT_BIGMETRICS",
|
||||
data: map[string]interface{}{
|
||||
"ClientStats": map[string]interface{}{
|
||||
"memory_stats": map[string]interface{}{
|
||||
"limit": float64(2048),
|
||||
"usage": float64(1024),
|
||||
},
|
||||
"name": "my-container",
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"ClientStats": map[string]interface{}{
|
||||
"memory_stats": map[string]interface{}{
|
||||
"limit": "2.000KB",
|
||||
"usage": float64(1024),
|
||||
},
|
||||
"name": "my-container",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "converts realistic docker stats loaded from testdata/stats.json",
|
||||
data: loadInJSON(),
|
||||
expected: inJSONExpected,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue