mirror of
https://github.com/jesseduffield/lazydocker.git
synced 2026-07-25 08:31:03 +00:00
fix: save mem at runtime
This commit is contained in:
parent
612d34eb00
commit
bab0dfed81
2 changed files with 113 additions and 200 deletions
|
|
@ -18,35 +18,51 @@ import (
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
var SCHEMA_JSON = `{
|
// var SCHEMA_JSON = `{
|
||||||
"client_stats": {
|
// "client_stats": {
|
||||||
"cpu_stats": {
|
// "cpu_stats": {
|
||||||
"cpu_usage": {
|
// "cpu_usage": {
|
||||||
"total_usage": "nanoseconds",
|
// "total_usage": "nanoseconds",
|
||||||
"percpu_usage": []string{"nanoseconds"},
|
// "percpu_usage": ["nanoseconds"],
|
||||||
"usage_in_kernelmode": "nanoseconds",
|
// "usage_in_kernelmode": "nanoseconds",
|
||||||
"usage_in_usermode": "nanoseconds",
|
// "usage_in_usermode": "nanoseconds"
|
||||||
},
|
// },
|
||||||
"system_cpu_usage": "nanoseconds",
|
// "system_cpu_usage": "nanoseconds"
|
||||||
},
|
// },
|
||||||
"precpu_stats": {
|
// "precpu_stats": {
|
||||||
"cpu_usage": {
|
// "cpu_usage": {
|
||||||
"total_usage": "nanoseconds",
|
// "total_usage": "nanoseconds",
|
||||||
"percpu_usage": []string{"nanoseconds"},
|
// "percpu_usage": ["nanoseconds"],
|
||||||
"usage_in_kernelmode": "nanoseconds",
|
// "usage_in_kernelmode": "nanoseconds",
|
||||||
"usage_in_usermode": "nanoseconds",
|
// "usage_in_usermode": "nanoseconds"
|
||||||
},
|
// },
|
||||||
"system_cpu_usage": "nanoseconds",
|
// "system_cpu_usage": "nanoseconds"
|
||||||
},
|
// },
|
||||||
"memory_stats": {
|
// "memory_stats": {
|
||||||
"stats": {
|
// "stats": {
|
||||||
"hierarchical_memory_limit": "bytes",
|
// "hierarchical_memory_limit": "bytes",
|
||||||
"hierarchical_memsw_limit": "bytes",
|
// "hierarchical_memsw_limit": "bytes"
|
||||||
},
|
// },
|
||||||
"limit": "bytes",
|
// "limit": "bytes"
|
||||||
},
|
// }
|
||||||
},
|
// }
|
||||||
}`
|
// }`
|
||||||
|
|
||||||
|
var PATHS_TO_CONVERT_BIGMETRICS = []map[string]string{
|
||||||
|
{"client_stats.cpu_stats.cpu_usage.total_usage": "nanoseconds"},
|
||||||
|
{"client_stats.cpu_stats.cpu_usage.percpu_usage": "nanoseconds"},
|
||||||
|
{"client_stats.cpu_stats.cpu_usage.usage_in_kernelmode": "nanoseconds"},
|
||||||
|
{"client_stats.cpu_stats.cpu_usage.usage_in_usermode": "nanoseconds"},
|
||||||
|
{"client_stats.cpu_stats.system_cpu_usage": "nanoseconds"},
|
||||||
|
{"client_stats.precpu_stats.cpu_usage.total_usage": "nanoseconds"},
|
||||||
|
{"client_stats.precpu_stats.cpu_usage.percpu_usage": "nanoseconds"},
|
||||||
|
{"client_stats.precpu_stats.cpu_usage.usage_in_kernelmode": "nanoseconds"},
|
||||||
|
{"client_stats.precpu_stats.cpu_usage.usage_in_usermode": "nanoseconds"},
|
||||||
|
{"client_stats.precpu_stats.system_cpu_usage": "nanoseconds"},
|
||||||
|
{"client_stats.memory_stats.limit": "bytes"},
|
||||||
|
{"client_stats.memory_stats.stats.hierarchical_memory_limit": "bytes"},
|
||||||
|
{"client_stats.memory_stats.stats.hierarchical_memsw_limit": "bytes"},
|
||||||
|
}
|
||||||
|
|
||||||
func RenderStats(userConfig *config.UserConfig, container *commands.Container, viewWidth int) (string, error) {
|
func RenderStats(userConfig *config.UserConfig, container *commands.Container, viewWidth int) (string, error) {
|
||||||
stats, ok := container.GetLastStats()
|
stats, ok := container.GetLastStats()
|
||||||
|
|
@ -79,7 +95,7 @@ func RenderStats(userConfig *config.UserConfig, container *commands.Container, v
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
lookupAndConvertBigMetric(&statsMap)
|
convertBigMetricFromSchema(&statsMap)
|
||||||
|
|
||||||
originalStats, err := utils.MarshalIntoYaml(statsMap)
|
originalStats, err := utils.MarshalIntoYaml(statsMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -189,56 +205,71 @@ func getFloat(unk interface{}) (float64, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func lookupAndConvertBigMetric(data *map[string]interface{}) error {
|
func convertBigMetricFromSchema(data *map[string]interface{}) error {
|
||||||
var schema map[string]interface{}
|
// switch value := schema.(type) {
|
||||||
|
// case map[string]interface{}:
|
||||||
// Unmarshal
|
// for key, val := range value {
|
||||||
err := json.Unmarshal([]byte(SCHEMA_JSON), &schema)
|
// path = fmt.Sprintf("%s.%s", path, key)
|
||||||
if err != nil {
|
// if err := convertBigMetricFromSchema(data, path, val); err != nil {
|
||||||
fmt.Println("Error:", err)
|
// return err
|
||||||
return err
|
// }
|
||||||
}
|
// }
|
||||||
|
// case []string:
|
||||||
err = convertBigMetricFromSchema(data, "", schema)
|
// // use path to translate from []int to []string
|
||||||
if err != nil {
|
// fmt.Println("Converting []int64 to []string for path:", path)
|
||||||
return err
|
// metric, err := lookup.LookupString(data, strings.TrimPrefix(path, "."))
|
||||||
}
|
// if err != nil {
|
||||||
return nil
|
// return err
|
||||||
}
|
// }
|
||||||
|
// if reflect.TypeOf(metric.Interface()) != reflect.TypeOf([]int64{}) {
|
||||||
func convertBigMetricFromSchema(data *map[string]interface{}, path string, schema interface{}) error {
|
// return fmt.Errorf("Can't convert non []int64 %v to []string", reflect.TypeOf(metric.Interface()))
|
||||||
switch value := schema.(type) {
|
// } else {
|
||||||
case map[string]interface{}:
|
// longIntSlice := metric.Interface().([]int64)
|
||||||
for key, val := range value {
|
// formattedMetric := lo.Map(longIntSlice, func(val int64, index int) string {
|
||||||
path = fmt.Sprintf("%s.%s", path, key)
|
// return utils.FormatBigMetric(val, value[index])
|
||||||
return convertBigMetricFromSchema(data, path, val)
|
// })
|
||||||
}
|
// return utils.SetObjectFieldByPath(data, path, formattedMetric)
|
||||||
case []string:
|
// }
|
||||||
// use path to translate from []int to []string
|
// case string:
|
||||||
metric, err := lookup.LookupString(data, strings.TrimPrefix(path, "."))
|
// // use path to translate from int/int64 to string
|
||||||
if err != nil {
|
// fmt.Println("Converting int64 to string for path:", path)
|
||||||
return err
|
// metric, err := lookup.LookupString(data, strings.TrimPrefix(path, "."))
|
||||||
}
|
// if err != nil {
|
||||||
if reflect.TypeOf(metric.Interface()) != reflect.TypeOf([]int64{}) {
|
// return err
|
||||||
return fmt.Errorf("Can't convert %v to []int64", reflect.TypeOf(metric.Interface()))
|
// }
|
||||||
} else {
|
// if reflect.TypeOf(metric.Interface()) != reflect.TypeOf(int64(0)) {
|
||||||
longIntSlice := metric.Interface().([]int64)
|
// return fmt.Errorf("Can't convert non int64 %v to string", reflect.TypeOf(metric.Interface()))
|
||||||
formattedMetric := lo.Map(longIntSlice, func(val int64, index int) string {
|
// } else {
|
||||||
return utils.FormatBigMetric(val, value[index])
|
// formattedMetric := utils.FormatBigMetric(metric.Interface().(int64), value)
|
||||||
})
|
// return utils.SetObjectFieldByPath(data, path, formattedMetric)
|
||||||
return utils.SetObjectFieldByPath(data, path, formattedMetric)
|
// }
|
||||||
}
|
// }
|
||||||
case string:
|
// return nil
|
||||||
// use path to translate from int/int64 to string
|
for _, pathToConvert := range PATHS_TO_CONVERT_BIGMETRICS {
|
||||||
metric, err := lookup.LookupString(data, strings.TrimPrefix(path, "."))
|
for path, metricType := range pathToConvert {
|
||||||
if err != nil {
|
metric, err := lookup.LookupString(data, path)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
if err == lookup.ErrKeyNotFound {
|
||||||
if reflect.TypeOf(metric.Interface()) != reflect.TypeOf(int64(0)) {
|
continue
|
||||||
return fmt.Errorf("Can't convert %v to int64", reflect.TypeOf(metric.Interface()))
|
}
|
||||||
} else {
|
return err
|
||||||
formattedMetric := utils.FormatBigMetric(metric.Interface().(int64), value)
|
}
|
||||||
return utils.SetObjectFieldByPath(data, path, formattedMetric)
|
if reflect.TypeOf(metric.Interface()) == reflect.TypeOf(int64(0)) {
|
||||||
|
formattedMetric := utils.FormatBigMetric(metric.Interface().(int64), 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)
|
||||||
|
formattedMetric := lo.Map(longIntSlice, func(val int64, index int) string {
|
||||||
|
return utils.FormatBigMetric(val, metricType)
|
||||||
|
})
|
||||||
|
err = utils.SetObjectFieldByPath(data, fmt.Sprintf(".%s", path), formattedMetric)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
package presentation
|
package presentation
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
@ -12,17 +10,10 @@ func TestConvertBigMetricFromSchema(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
name string
|
name string
|
||||||
data map[string]interface{}
|
data map[string]interface{}
|
||||||
path string
|
|
||||||
schema interface{}
|
|
||||||
expected map[string]interface{}
|
expected map[string]interface{}
|
||||||
expectedErr string
|
expectedErr string
|
||||||
}
|
}
|
||||||
|
|
||||||
var schema map[string]interface{}
|
|
||||||
if err := json.Unmarshal([]byte(SCHEMA_JSON), &schema); err != nil {
|
|
||||||
fmt.Println("Error unmarshaling SCHEMA_JSON:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
name: "string schema: converts int64 bytes",
|
name: "string schema: converts int64 bytes",
|
||||||
|
|
@ -33,8 +24,6 @@ func TestConvertBigMetricFromSchema(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
path: "",
|
|
||||||
schema: schema,
|
|
||||||
expected: map[string]interface{}{
|
expected: map[string]interface{}{
|
||||||
"client_stats": map[string]interface{}{
|
"client_stats": map[string]interface{}{
|
||||||
"memory_stats": map[string]interface{}{
|
"memory_stats": map[string]interface{}{
|
||||||
|
|
@ -58,14 +47,12 @@ func TestConvertBigMetricFromSchema(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
path: "",
|
|
||||||
schema: schema,
|
|
||||||
expected: map[string]interface{}{
|
expected: map[string]interface{}{
|
||||||
"client_stats": map[string]interface{}{
|
"client_stats": map[string]interface{}{
|
||||||
"cpu_stats": map[string]interface{}{
|
"cpu_stats": map[string]interface{}{
|
||||||
"cpu_usage": map[string]interface{}{
|
"cpu_usage": map[string]interface{}{
|
||||||
"total_usage": "100.000 s",
|
"total_usage": "100.000 s",
|
||||||
"percpu_usage": []string{"50.000 ms", "50.000 ms"},
|
"percpu_usage": []string{"50.000 s", "50.000 s"},
|
||||||
"usage_in_kernelmode": "200.000 µs",
|
"usage_in_kernelmode": "200.000 µs",
|
||||||
"usage_in_usermode": "200.000 µs",
|
"usage_in_usermode": "200.000 µs",
|
||||||
},
|
},
|
||||||
|
|
@ -74,117 +61,12 @@ func TestConvertBigMetricFromSchema(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "direct string schema: converts int64 bytes at path",
|
|
||||||
data: map[string]interface{}{
|
|
||||||
"limit": int64(2048),
|
|
||||||
},
|
|
||||||
path: ".limit",
|
|
||||||
schema: "bytes",
|
|
||||||
expected: map[string]interface{}{
|
|
||||||
"limit": "2.000 KB",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "direct string schema: converts int64 nanoseconds at path",
|
|
||||||
data: map[string]interface{}{
|
|
||||||
"cpu": int64(1500000),
|
|
||||||
},
|
|
||||||
path: ".cpu",
|
|
||||||
schema: "nanoseconds",
|
|
||||||
expected: map[string]interface{}{
|
|
||||||
"cpu": "1.500 ms",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "direct string schema: keeps small bytes value without unit suffix",
|
|
||||||
data: map[string]interface{}{
|
|
||||||
"limit": int64(500),
|
|
||||||
},
|
|
||||||
path: ".limit",
|
|
||||||
schema: "bytes",
|
|
||||||
expected: map[string]interface{}{
|
|
||||||
"limit": "500",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "direct []string schema: converts []int64 at path",
|
|
||||||
data: map[string]interface{}{
|
|
||||||
"percpu_usage": []int64{1500000, 2500000000},
|
|
||||||
},
|
|
||||||
path: ".percpu_usage",
|
|
||||||
schema: []string{"nanoseconds", "nanoseconds"},
|
|
||||||
expected: map[string]interface{}{
|
|
||||||
"percpu_usage": []string{"1.500 ms", "2.500 s"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "map schema: recurses into single-key nested map",
|
|
||||||
data: map[string]interface{}{
|
|
||||||
"outer": map[string]interface{}{
|
|
||||||
"inner": int64(4096),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
path: "",
|
|
||||||
schema: map[string]interface{}{
|
|
||||||
"outer": map[string]interface{}{
|
|
||||||
"inner": "bytes",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
expected: map[string]interface{}{
|
|
||||||
"outer": map[string]interface{}{
|
|
||||||
"inner": "4.000 KB",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "unknown schema type is a no-op",
|
|
||||||
data: map[string]interface{}{
|
|
||||||
"limit": int64(2048),
|
|
||||||
},
|
|
||||||
path: ".limit",
|
|
||||||
schema: 42,
|
|
||||||
expected: map[string]interface{}{
|
|
||||||
"limit": int64(2048),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "string schema: errors when value at path is not int64",
|
|
||||||
data: map[string]interface{}{
|
|
||||||
"limit": "not a number",
|
|
||||||
},
|
|
||||||
path: ".limit",
|
|
||||||
schema: "bytes",
|
|
||||||
expected: map[string]interface{}{"limit": "not a number"},
|
|
||||||
expectedErr: "Can't convert string to int64",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "[]string schema: errors when value at path is not []int64",
|
|
||||||
data: map[string]interface{}{
|
|
||||||
"percpu_usage": []string{"a", "b"},
|
|
||||||
},
|
|
||||||
path: ".percpu_usage",
|
|
||||||
schema: []string{"nanoseconds", "nanoseconds"},
|
|
||||||
expected: map[string]interface{}{"percpu_usage": []string{"a", "b"}},
|
|
||||||
expectedErr: "Can't convert []string to []int64",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "string schema: errors when path does not exist in data",
|
|
||||||
data: map[string]interface{}{},
|
|
||||||
path: ".missing",
|
|
||||||
schema: "bytes",
|
|
||||||
expected: map[string]interface{}{},
|
|
||||||
expectedErr: "Unable to find the key",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
t.Run(s.name, func(t *testing.T) {
|
t.Run(s.name, func(t *testing.T) {
|
||||||
if m, ok := s.schema.(map[string]interface{}); ok && m == nil {
|
|
||||||
t.Skip("scenario depends on SCHEMA_JSON which failed to parse")
|
|
||||||
}
|
|
||||||
data := s.data
|
data := s.data
|
||||||
err := convertBigMetricFromSchema(&data, s.path, s.schema)
|
err := convertBigMetricFromSchema(&data)
|
||||||
if s.expectedErr != "" {
|
if s.expectedErr != "" {
|
||||||
assert.EqualError(t, err, s.expectedErr)
|
assert.EqualError(t, err, s.expectedErr)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue