From c99b74b88f5d03c530f4fffed082e3ac2596f621 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Sat, 14 Sep 2024 00:20:17 +0300 Subject: [PATCH 01/15] [support/resources] Add package for collecting resources (CPU/memory) info --- support/resources/resources.go | 54 +++++++++++++ support/resources/resources_windows.go | 21 +++++ support/services/services.go | 2 +- support/support.go | 108 ++++++++++++++++++++++--- system/info.go | 2 +- 5 files changed, 174 insertions(+), 13 deletions(-) create mode 100644 support/resources/resources.go create mode 100644 support/resources/resources_windows.go diff --git a/support/resources/resources.go b/support/resources/resources.go new file mode 100644 index 00000000..4ab458aa --- /dev/null +++ b/support/resources/resources.go @@ -0,0 +1,54 @@ +//go:build linux +// +build linux + +// Package resources provides methods for collecting information about system +// resources (cpu/memory) +package resources + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "github.com/essentialkaos/ek/v13/support" + "github.com/essentialkaos/ek/v13/system" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +// Collect collects info about CPU and memory +func Collect(extMemInfo bool) *support.ResourcesInfo { + result := &support.ResourcesInfo{} + + cpuInfo, err1 := system.GetCPUInfo() + + if err1 == nil { + for _, p := range cpuInfo { + result.CPU = append(result.CPU, support.CPUInfo{ + Model: p.Model, + Cores: p.Cores, + Threads: p.Siblings / p.Cores, + }) + } + } + + memInfo, err2 := system.GetMemUsage() + + if err2 == nil { + result.MemTotal = memInfo.MemTotal + result.MemFree = memInfo.MemFree + result.MemUsed = memInfo.MemUsed + result.SwapTotal = memInfo.SwapTotal + result.SwapFree = memInfo.SwapFree + result.SwapUsed = memInfo.SwapUsed + } + + if err1 != nil && err2 != nil { + return nil + } + + return result +} diff --git a/support/resources/resources_windows.go b/support/resources/resources_windows.go new file mode 100644 index 00000000..cc4ba05a --- /dev/null +++ b/support/resources/resources_windows.go @@ -0,0 +1,21 @@ +// Package resources provides methods for collecting information about system +// resources (cpu/memory) +package resources + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "github.com/essentialkaos/ek/v13/support" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +// Collect collects info about CPU and memory +func Collect(extMemInfo bool) *support.ResourcesInfo { + return nil +} diff --git a/support/services/services.go b/support/services/services.go index 3066df9e..8a0e98f3 100644 --- a/support/services/services.go +++ b/support/services/services.go @@ -18,7 +18,7 @@ import ( // ////////////////////////////////////////////////////////////////////////////////// // -// Collect collect info about services +// Collect collects info about services func Collect(services ...string) []support.Service { var result []support.Service diff --git a/support/support.go b/support/support.go index 23ebf84d..3a197f13 100644 --- a/support/support.go +++ b/support/support.go @@ -100,17 +100,18 @@ type Info struct { Version string `json:"version"` Binary string `json:"binary"` - Build *BuildInfo `json:"build,omitempty"` - OS *OSInfo `json:"os,omitempty"` - System *SystemInfo `json:"system,omitempty"` - Network *NetworkInfo `json:"network,omitempty"` - FS []FSInfo `json:"fs,omitempty"` - Pkgs []Pkg `json:"pkgs,omitempty"` - Services []Service `json:"services,omitempty"` - Deps []Dep `json:"deps,omitempty"` - Apps []App `json:"apps,omitempty"` - Checks []Check `json:"checks,omitempty"` - Env []EnvVar `json:"env,omitempty"` + Build *BuildInfo `json:"build,omitempty"` + OS *OSInfo `json:"os,omitempty"` + System *SystemInfo `json:"system,omitempty"` + Network *NetworkInfo `json:"network,omitempty"` + Resources *ResourcesInfo `json:"resources,omitempty"` + FS []FSInfo `json:"fs,omitempty"` + Pkgs []Pkg `json:"pkgs,omitempty"` + Services []Service `json:"services,omitempty"` + Deps []Dep `json:"deps,omitempty"` + Apps []App `json:"apps,omitempty"` + Checks []Check `json:"checks,omitempty"` + Env []EnvVar `json:"env,omitempty"` } // BuildInfo contains information about binary @@ -165,6 +166,24 @@ type FSInfo struct { Free uint64 `json:"free,omitempty"` } +// ResourcesInfo contains information about system resources +type ResourcesInfo struct { + CPU []CPUInfo `json:"cpu"` + MemTotal uint64 `json:"mem_total,omitempty"` + MemFree uint64 `json:"mem_free,omitempty"` + MemUsed uint64 `json:"mem_used,omitempty"` + SwapTotal uint64 `json:"swap_total,omitempty"` + SwapFree uint64 `json:"swap_free,omitempty"` + SwapUsed uint64 `json:"swap_used,omitempty"` +} + +// CPUInfo contains info about CPU +type CPUInfo struct { + Model string `json:"model"` + Threads int `json:"threads"` + Cores int `json:"cores"` +} + // Service contains basic info about service type Service struct { Name string `json:"name"` @@ -343,6 +362,17 @@ func (i *Info) WithFS(info []FSInfo) *Info { return i } +// WithResources adds system resources information +func (i *Info) WithResources(info *ResourcesInfo) *Info { + if i == nil { + return nil + } + + i.Resources = info + + return i +} + // ////////////////////////////////////////////////////////////////////////////////// // // Print prints support info @@ -358,6 +388,7 @@ func (i *Info) Print() { i.printAppInfo() i.printOSInfo() + i.printResourcesInfo() i.printNetworkInfo() i.printFSInfo() i.printEnvVars() @@ -462,6 +493,61 @@ func (i *Info) printOSInfo() { } } +// printResourcesInfo prints resources info +func (i *Info) printResourcesInfo() { + if i.Resources == nil { + return + } + + fmtutil.Separator(false, "RESOURCES") + + if len(i.Resources.CPU) > 0 { + var procs, cores int + + for i, p := range i.Resources.CPU { + fmtc.Printf( + " {s-}%d.{!} %s {s}[ %dC × %dT → %d ]{!}\n", + i+1, p.Model, p.Cores, p.Threads, p.Cores*p.Threads, + ) + procs++ + cores += p.Cores * p.Threads + } + + fmtc.NewLine() + + format(10, true, + "Processors", fmtutil.PrettyNum(procs), + "Cores", fmtutil.PrettyNum(cores), + ) + + fmtc.NewLine() + } + + if i.Resources.MemTotal > 0 { + perc := (float64(i.Resources.MemUsed) / float64(i.Resources.MemTotal)) * 100.0 + + format(6, true, "Memory", fmtc.Sprintf( + "%s {s}/{!} %s {s-}(%s){!}", + fmtutil.PrettySize(i.Resources.MemUsed, " "), + fmtutil.PrettySize(i.Resources.MemTotal, " "), + fmtutil.PrettyPerc(perc), + )) + } + + if i.Resources.SwapTotal > 0 { + perc := (float64(i.Resources.SwapUsed) / float64(i.Resources.SwapTotal)) * 100.0 + + format(6, true, "Swap", fmtc.Sprintf( + "%s {s}/{!} %s {s-}(%s){!}", + fmtutil.PrettySize(i.Resources.SwapUsed, " "), + fmtutil.PrettySize(i.Resources.SwapTotal, " "), + fmtutil.PrettyPerc(perc), + )) + } else { + format(6, true, "Swap", "") + } +} + // printEnvVars prints environment variables func (i *Info) printEnvVars() { if len(i.Env) == 0 { diff --git a/system/info.go b/system/info.go index 744452da..c55921f3 100644 --- a/system/info.go +++ b/system/info.go @@ -58,7 +58,7 @@ type CPUUsage struct { // CPUInfo contains info about CPU type CPUInfo struct { - Vendor string `json:"vendor"` // Processor vandor name + Vendor string `json:"vendor"` // Processor vendor name Model string `json:"model"` // Common name of the processor Cores int `json:"cores"` // Number of cores Siblings int `json:"siblings"` // Total number of sibling CPUs on the same physical CPU From 6b1ea96a1e845ffa7c10eb1e5e54405e62790d61 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Sat, 14 Sep 2024 01:02:11 +0300 Subject: [PATCH 02/15] [system/sysctl] Add method 'All' to get all kernel parameters --- system/sysctl/example_test.go | 54 ++++++++++++++++++++++++++ system/sysctl/sysctl.go | 43 +++++++++++++++++++- system/sysctl/sysctl_darwin.go | 39 ++++++++++++++++++- system/sysctl/sysctl_linux.go | 34 +++++++++++++++- system/sysctl/sysctl_test.go | 71 +++++++++++++++++++++++++++++----- 5 files changed, 226 insertions(+), 15 deletions(-) diff --git a/system/sysctl/example_test.go b/system/sysctl/example_test.go index 3d1d093e..153a3cba 100644 --- a/system/sysctl/example_test.go +++ b/system/sysctl/example_test.go @@ -42,3 +42,57 @@ func ExampleGetI64() { fmt.Printf("File max: %d\n", paramValue) } + +func ExampleAll() { + params, err := All() + + if err != nil { + panic(err.Error()) + } + + for n, v := range params { + fmt.Printf("%s → %s\n", n, v) + } +} + +func ExampleParams_Get() { + params, err := All() + + if err != nil { + panic(err.Error()) + } + + fmt.Printf("Boot ID: %s\n", params.Get("kernel.random.boot_id")) +} + +func ExampleParams_GetI() { + params, err := All() + + if err != nil { + panic(err.Error()) + } + + paramValue, err := params.GetI("kernel.pty.max") + + if err != nil { + panic(err.Error()) + } + + fmt.Printf("PTY Max: %d\n", paramValue) +} + +func ExampleParams_GetI64() { + params, err := All() + + if err != nil { + panic(err.Error()) + } + + paramValue, err := params.GetI64("fs.file-max") + + if err != nil { + panic(err.Error()) + } + + fmt.Printf("File max: %d\n", paramValue) +} diff --git a/system/sysctl/sysctl.go b/system/sysctl/sysctl.go index c43cf59c..0905c73f 100644 --- a/system/sysctl/sysctl.go +++ b/system/sysctl/sysctl.go @@ -27,6 +27,16 @@ var procFS = "/proc/sys" // ////////////////////////////////////////////////////////////////////////////////// // +// Params contains all kernel parameters +type Params map[string]string + +// ////////////////////////////////////////////////////////////////////////////////// // + +// All returns all kernel parameters +func All() (Params, error) { + return getParams() +} + // Get returns kernel parameter value as a string func Get(param string) (string, error) { switch { @@ -36,7 +46,7 @@ func Get(param string) (string, error) { return "", fmt.Errorf("Invalid parameter name %q", param) } - return getKernelParam(param) + return getParam(param) } // GetI returns kernel parameter value as an int @@ -74,3 +84,34 @@ func GetI64(param string) (int64, error) { } // ////////////////////////////////////////////////////////////////////////////////// // + +// Get returns kernel parameter value as a string +func (p Params) Get(name string) string { + if len(p) == 0 { + return "" + } + + return p[name] +} + +// GetI returns kernel parameter value as an int +func (p Params) GetI(param string) (int, error) { + i, err := strconv.Atoi(p.Get(param)) + + if err != nil { + return 0, fmt.Errorf("Can't parse %q parameter as int: %w", param, err) + } + + return i, nil +} + +// GetI64 returns kernel parameter value as an int64 +func (p Params) GetI64(param string) (int64, error) { + i, err := strconv.ParseInt(p.Get(param), 10, 64) + + if err != nil { + return 0, fmt.Errorf("Can't parse %q parameter as int64: %w", param, err) + } + + return i, nil +} diff --git a/system/sysctl/sysctl_darwin.go b/system/sysctl/sysctl_darwin.go index c2418629..abf5828b 100644 --- a/system/sysctl/sysctl_darwin.go +++ b/system/sysctl/sysctl_darwin.go @@ -8,14 +8,49 @@ package sysctl // ////////////////////////////////////////////////////////////////////////////////// // import ( + "bytes" + "fmt" "os/exec" "strings" ) // ////////////////////////////////////////////////////////////////////////////////// // -// getKernelParam reads kernel parameter from procfs -func getKernelParam(param string) (string, error) { +// getParam reads kernel parameter from procfs +func getParam(param string) (string, error) { output, err := exec.Command(binary, "-n", param).Output() + + if err != nil { + return "", fmt.Errorf("Can't get kernel parameters from sysctl") + } + return strings.Trim(string(output), "\n\r"), err } + +// getParams reads all kernel parameters +func getParams() (Params, error) { + output, err := exec.Command(binary, "-a").Output() + + if err != nil { + return nil, fmt.Errorf("Can't get kernel parameters from sysctl") + } + + params := make(Params) + buf := bytes.NewBuffer(output) + + for { + line, err := buf.ReadString('\n') + + if err != nil { + break + } + + name, value, ok := strings.Cut(strings.Trim(line, "\n\r"), ": ") + + if ok { + params[name] = value + } + } + + return params, nil +} diff --git a/system/sysctl/sysctl_linux.go b/system/sysctl/sysctl_linux.go index 95c3c0c0..68100f75 100644 --- a/system/sysctl/sysctl_linux.go +++ b/system/sysctl/sysctl_linux.go @@ -8,8 +8,10 @@ package sysctl // ////////////////////////////////////////////////////////////////////////////////// // import ( + "bytes" "fmt" "os" + "os/exec" "strings" "github.com/essentialkaos/ek/v13/path" @@ -17,8 +19,8 @@ import ( // ////////////////////////////////////////////////////////////////////////////////// // -// getKernelParam reads kernel parameter from procfs -func getKernelParam(param string) (string, error) { +// getParam reads kernel parameter from procfs +func getParam(param string) (string, error) { p, err := os.ReadFile(path.Clean(path.Join( procFS, strings.ReplaceAll(param, ".", "/"), ))) @@ -29,3 +31,31 @@ func getKernelParam(param string) (string, error) { return strings.Trim(string(p), "\n\r"), nil } + +// getParams reads all kernel parameters +func getParams() (Params, error) { + output, err := exec.Command(binary, "-a").Output() + + if err != nil { + return nil, fmt.Errorf("Can't get kernel parameters from sysctl") + } + + params := make(Params) + buf := bytes.NewBuffer(output) + + for { + line, err := buf.ReadString('\n') + + if err != nil { + break + } + + name, value, ok := strings.Cut(strings.Trim(line, "\n\r"), " = ") + + if ok { + params[name] = value + } + } + + return params, nil +} diff --git a/system/sysctl/sysctl_test.go b/system/sysctl/sysctl_test.go index 4b9513ce..049e2536 100644 --- a/system/sysctl/sysctl_test.go +++ b/system/sysctl/sysctl_test.go @@ -28,7 +28,7 @@ var _ = Suite(&SysctlSuite{}) // ////////////////////////////////////////////////////////////////////////////////// // -func (s *SysctlSuite) TestBasic(c *C) { +func (s *SysctlSuite) TestOne(c *C) { if runtime.GOOS == "darwin" { vs, err := Get("kern.job_control") c.Assert(err, IsNil) @@ -40,19 +40,19 @@ func (s *SysctlSuite) TestBasic(c *C) { c.Assert(err, IsNil) c.Assert(vi64, Equals, int64(1)) } else { - vs, err := Get("net.ipv4.conf.all.forwarding") + vs, err := Get("vm.swappiness") c.Assert(err, IsNil) - c.Assert(vs, Equals, "1") - vi, err := GetI("net.ipv4.conf.all.forwarding") + c.Assert(vs, Not(Equals), "0") + vi, err := GetI("vm.swappiness") c.Assert(err, IsNil) - c.Assert(vi, Equals, int(1)) - vi64, err := GetI64("net.ipv4.conf.all.forwarding") + c.Assert(vi, Not(Equals), int(0)) + vi64, err := GetI64("vm.swappiness") c.Assert(err, IsNil) - c.Assert(vi64, Equals, int64(1)) + c.Assert(vi64, Not(Equals), int64(0)) } } -func (s *SysctlSuite) TestBasicErrors(c *C) { +func (s *SysctlSuite) TestOneErrors(c *C) { _, err := Get("") c.Assert(err, NotNil) c.Assert(err, ErrorMatches, `Kernel parameter name cannot be empty`) @@ -86,9 +86,7 @@ func (s *SysctlSuite) TestBasicErrors(c *C) { _, err = GetI64("kernel.random.boot_id") c.Assert(err, NotNil) } -} -func (s *SysctlSuite) TestOSErrors(c *C) { if runtime.GOOS == "darwin" { binary = "_unknown_" _, err := Get("kern.bootuuid") @@ -101,3 +99,56 @@ func (s *SysctlSuite) TestOSErrors(c *C) { procFS = "/proc/sys" } } + +func (s *SysctlSuite) TestAll(c *C) { + p, err := All() + + c.Assert(err, IsNil) + c.Assert(p, Not(HasLen), 0) + + if runtime.GOOS == "darwin" { + c.Assert(p.Get("kern.job_control"), Equals, "1") + vi, err := p.GetI("kern.job_control") + c.Assert(err, IsNil) + c.Assert(vi, Equals, int(1)) + vi64, err := p.GetI64("kern.job_control") + c.Assert(err, IsNil) + c.Assert(vi64, Equals, int64(1)) + } else { + c.Assert(p.Get("vm.swappiness"), Not(Equals), "") + vi, err := p.GetI("vm.swappiness") + c.Assert(err, IsNil) + c.Assert(vi, Not(Equals), int(0)) + vi64, err := p.GetI64("vm.swappiness") + c.Assert(err, IsNil) + c.Assert(vi64, Not(Equals), int64(0)) + } +} + +func (s *SysctlSuite) TestAllErrors(c *C) { + var p Params + + c.Assert(p.Get("test"), Equals, "") + + p, err := All() + + c.Assert(err, IsNil) + c.Assert(p, Not(HasLen), 0) + + if runtime.GOOS == "darwin" { + _, err = p.GetI("kern.bootuuid") + c.Assert(err, NotNil) + _, err = p.GetI64("kern.bootuuid") + c.Assert(err, NotNil) + } else { + _, err = p.GetI("kernel.random.boot_id") + c.Assert(err, NotNil) + _, err = p.GetI64("kernel.random.boot_id") + c.Assert(err, NotNil) + } + + binary = "_unknown_" + _, err = All() + c.Assert(err, NotNil) + binary = "sysctl" +} From 54d9d229ee92f3356a66e13ff9f1aa1634aec15c Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Sat, 14 Sep 2024 12:41:38 +0300 Subject: [PATCH 03/15] [system] Add macOS support for 'GetCPUInfo' --- system/info_cpu_darwin.go | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/system/info_cpu_darwin.go b/system/info_cpu_darwin.go index 5b0435df..49c3bb8b 100644 --- a/system/info_cpu_darwin.go +++ b/system/info_cpu_darwin.go @@ -9,6 +9,9 @@ package system import ( "time" + + "github.com/essentialkaos/ek/v13/strutil" + "github.com/essentialkaos/ek/v13/system/sysctl" ) // ////////////////////////////////////////////////////////////////////////////////// // @@ -31,8 +34,29 @@ func GetCPUStats() (*CPUStats, error) { return nil, nil } -// ❗ GetCPUInfo returns slice with info about CPUs +// ////////////////////////////////////////////////////////////////////////////////// // + +// GetCPUInfo returns slice with info about CPUs func GetCPUInfo() ([]*CPUInfo, error) { - panic("UNSUPPORTED") - return nil, nil + params, err := sysctl.All() + + if err != nil { + return nil, err + } + + p0cache, _ := params.GetI("hw.perflevel0.l2cachesize") + p1cache, _ := params.GetI("hw.perflevel1.l2cachesize") + + cores, _ := params.GetI("machdep.cpu.core_count") + threads, _ := params.GetI("machdep.cpu.thread_count") + + return []*CPUInfo{ + { + Vendor: strutil.Q(params.Get("machdep.cpu.vendor"), "Apple"), + Model: params.Get("machdep.cpu.brand_string"), + Cores: cores, + Siblings: threads, + CacheSize: uint64(p0cache + p1cache), + }, + }, nil } From fff6a76c28b9939188cdd8082a721ac3cc926ffb Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Sat, 14 Sep 2024 12:42:03 +0300 Subject: [PATCH 04/15] [support/resources] Code refactoring --- support/resources/resources.go | 2 +- support/resources/resources_windows.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/support/resources/resources.go b/support/resources/resources.go index 4ab458aa..7b2f5c86 100644 --- a/support/resources/resources.go +++ b/support/resources/resources.go @@ -20,7 +20,7 @@ import ( // ////////////////////////////////////////////////////////////////////////////////// // // Collect collects info about CPU and memory -func Collect(extMemInfo bool) *support.ResourcesInfo { +func Collect() *support.ResourcesInfo { result := &support.ResourcesInfo{} cpuInfo, err1 := system.GetCPUInfo() diff --git a/support/resources/resources_windows.go b/support/resources/resources_windows.go index cc4ba05a..435b5ead 100644 --- a/support/resources/resources_windows.go +++ b/support/resources/resources_windows.go @@ -16,6 +16,6 @@ import ( // ////////////////////////////////////////////////////////////////////////////////// // // Collect collects info about CPU and memory -func Collect(extMemInfo bool) *support.ResourcesInfo { +func Collect() *support.ResourcesInfo { return nil } From f1bf1a4b7cbce36465dddd5fda5d073d15f6c471 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Sat, 14 Sep 2024 23:32:54 +0300 Subject: [PATCH 05/15] [system] Add macOS support for 'GetMemUsage' --- system/info.go | 42 ++++++------- system/info_cpu_darwin.go | 3 +- system/info_memory_darwin.go | 114 ++++++++++++++++++++++++++++++++++- 3 files changed, 134 insertions(+), 25 deletions(-) diff --git a/system/info.go b/system/info.go index c55921f3..47a57f87 100644 --- a/system/info.go +++ b/system/info.go @@ -28,21 +28,21 @@ type LoadAvg struct { // MemUsage contains info about system memory usage type MemUsage struct { - MemTotal uint64 `json:"total"` // Total usable ram (i.e. physical ram minus a few reserved bits and the kernel binary code) - MemFree uint64 `json:"free"` // The sum of MemFree - (Buffers + Cached) - MemUsed uint64 `json:"used"` // MemTotal - MemFree - Buffers uint64 `json:"buffers"` // Relatively temporary storage for raw disk blocks shouldn't get tremendously large (20MB or so) - Cached uint64 `json:"cached"` // In-memory cache for files read from the disk (the pagecache). Doesn't include SwapCached - Active uint64 `json:"active"` // Memory that has been used more recently and usually not reclaimed unless absolutely necessary - Inactive uint64 `json:"inactive"` // Memory which has been less recently used. It is more eligible to be reclaimed for other purposes - SwapTotal uint64 `json:"swap_total"` // Total amount of swap space available - SwapFree uint64 `json:"swap_free"` // Memory which has been evicted from RAM, and is temporarily on the disk still also is in the swapfile - SwapUsed uint64 `json:"swap_used"` // SwapTotal - SwapFree - SwapCached uint64 `json:"swap_cached"` // Memory that once was swapped out, is swapped back in but - Dirty uint64 `json:"dirty"` // Memory which is waiting to get written back to the disk - Shmem uint64 `json:"shmem"` // Total used shared memory - Slab uint64 `json:"slab"` // In-kernel data structures cache - SReclaimable uint64 `json:"sreclaimable"` // The part of the Slab that might be reclaimed (such as caches) + MemTotal uint64 `json:"total"` // Total usable ram (i.e. physical ram minus a few reserved bits and the kernel binary code) + MemFree uint64 `json:"free"` // The sum of MemFree - (Buffers + Cached) + MemUsed uint64 `json:"used"` // MemTotal - MemFree + SwapTotal uint64 `json:"swap_total"` // Total amount of swap space available + SwapFree uint64 `json:"swap_free"` // Memory which has been evicted from RAM, and is temporarily on the disk still also is in the swapfile + SwapUsed uint64 `json:"swap_used"` // SwapTotal - SwapFree + Active uint64 `json:"active"` // Memory that has been used more recently and usually not reclaimed unless absolutely necessary + Inactive uint64 `json:"inactive"` // Memory which has been less recently used. It is more eligible to be reclaimed for other purposes + Buffers uint64 `json:"buffers,omitempty"` // Relatively temporary storage for raw disk blocks shouldn't get tremendously large (20MB or so) + Cached uint64 `json:"cached,omitempty"` // In-memory cache for files read from the disk (the pagecache). Doesn't include SwapCached + SwapCached uint64 `json:"swap_cached,omitempty"` // Memory that once was swapped out, is swapped back in but + Dirty uint64 `json:"dirty,omitempty"` // Memory which is waiting to get written back to the disk + Shmem uint64 `json:"shmem,omitempty"` // Total used shared memory + Slab uint64 `json:"slab,omitempty"` // In-kernel data structures cache + SReclaimable uint64 `json:"sreclaimable,omitempty"` // The part of the Slab that might be reclaimed (such as caches) } // CPUUsage contains info about CPU usage @@ -58,12 +58,12 @@ type CPUUsage struct { // CPUInfo contains info about CPU type CPUInfo struct { - Vendor string `json:"vendor"` // Processor vendor name - Model string `json:"model"` // Common name of the processor - Cores int `json:"cores"` // Number of cores - Siblings int `json:"siblings"` // Total number of sibling CPUs on the same physical CPU - CacheSize uint64 `json:"cache_size"` // Amount of level 2 memory cache available to the processor - Speed []float64 `json:"speed"` // Speed in megahertz for the processor + Vendor string `json:"vendor"` // Processor vendor name + Model string `json:"model"` // Common name of the processor + Cores int `json:"cores"` // Number of cores + Siblings int `json:"siblings"` // Total number of sibling CPUs on the same physical CPU + CacheSize uint64 `json:"cache_size"` // Amount of level 2 memory cache available to the processor + Speed []float64 `json:"speed,omitempty"` // Speed in megahertz for the processor } // CPUStats contains basic CPU stats diff --git a/system/info_cpu_darwin.go b/system/info_cpu_darwin.go index 49c3bb8b..40c746c1 100644 --- a/system/info_cpu_darwin.go +++ b/system/info_cpu_darwin.go @@ -8,6 +8,7 @@ package system // ////////////////////////////////////////////////////////////////////////////////// // import ( + "fmt" "time" "github.com/essentialkaos/ek/v13/strutil" @@ -41,7 +42,7 @@ func GetCPUInfo() ([]*CPUInfo, error) { params, err := sysctl.All() if err != nil { - return nil, err + return nil, fmt.Errorf("Can't get kernel parameters: %w", err) } p0cache, _ := params.GetI("hw.perflevel0.l2cachesize") diff --git a/system/info_memory_darwin.go b/system/info_memory_darwin.go index 058c6567..eaa69199 100644 --- a/system/info_memory_darwin.go +++ b/system/info_memory_darwin.go @@ -7,8 +7,116 @@ package system // // // ////////////////////////////////////////////////////////////////////////////////// // -// ❗ GetMemUsage returns memory usage info +import ( + "bytes" + "fmt" + "os/exec" + "strconv" + "strings" + + "github.com/essentialkaos/ek/v13/strutil" + "github.com/essentialkaos/ek/v13/system/sysctl" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +// GetMemUsage returns memory usage info func GetMemUsage() (*MemUsage, error) { - panic("UNSUPPORTED") - return nil, nil + params, err := sysctl.All() + + if err != nil { + return nil, fmt.Errorf("Can't get kernel parameters: %w", err) + } + + pagesize, err := params.GetI("hw.pagesize") + + if err != nil { + return nil, fmt.Errorf("Can't read page size: %w", err) + } + + totalMem, _ := params.GetI("hw.memsize_usable") + + info, err := calculateMemUsage(uint64(pagesize), uint64(totalMem)) + + if err != nil { + return nil, err + } + + appendSwapUsage(info, params) + + return info, nil +} + +// ////////////////////////////////////////////////////////////////////////////////// // + +// calculateMemUsage calculates memory usage using data from vm_stats +func calculateMemUsage(pageSize, totalMem uint64) (*MemUsage, error) { + output, err := exec.Command("vm_stat").Output() + + if err != nil { + return nil, fmt.Errorf("Can't get output from vm_stats") + } + + buf := bytes.NewBuffer(output) + + var free, active, inactive, speculative uint64 + + for i := 0; i < 5; i++ { + line, err := buf.ReadString('\n') + + if err != nil { + break + } + + _, v, _ := strings.Cut(line, ": ") + vu, _ := strconv.ParseUint(strings.Trim(v, " .\n\r"), 10, 64) + vu *= pageSize + + switch strutil.Substr(line, 0, 10) { + case "Pages free": + free = vu + case "Pages acti": + active = vu + case "Pages inac": + inactive = vu + case "Pages spec": + speculative = vu + } + } + + return &MemUsage{ + MemTotal: totalMem, + MemFree: free + speculative + inactive, + MemUsed: totalMem - (free + speculative + inactive), + Active: active, + Inactive: inactive, + }, nil +} + +// appendSwapUsage appends swap usage info +func appendSwapUsage(info *MemUsage, params sysctl.Params) { + swap := params["vm.swapusage"] + + if swap == "" { + return + } + + swap = strutil.SqueezeRepeats(swap, " ") + swap = strings.ReplaceAll(swap, " = ", "=") + swap = strings.ReplaceAll(swap, "M", "") + + for i := 0; i < 3; i++ { + l := strutil.ReadField(swap, i, false, ' ') + n, v, _ := strings.Cut(l, "=") + fv, _ := strconv.ParseFloat(v, 10) + + switch n { + case "total": + info.SwapTotal = uint64(fv * 1024 * 1024) + case "used": + info.SwapUsed = uint64(fv * 1024 * 1024) + case "free": + info.SwapFree = uint64(fv * 1024 * 1024) + } + } } From f93e1d41499d6f899da866b43fe11fa479cc391f Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Sat, 14 Sep 2024 23:38:54 +0300 Subject: [PATCH 06/15] [system] Improve macOS tests --- system/info_darwin_test.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/system/info_darwin_test.go b/system/info_darwin_test.go index 7110a71a..54be1679 100644 --- a/system/info_darwin_test.go +++ b/system/info_darwin_test.go @@ -68,3 +68,17 @@ func (s *SystemSuite) TestSystemInfo(c *C) { c.Assert(osInfo.Version, Not(Equals), "") c.Assert(osInfo.Build, Not(Equals), "") } + +func (s *SystemSuite) TestCPUInfo(c *C) { + info, err := GetCPUInfo() + + c.Assert(err, IsNil) + c.Assert(info, NotNil) +} + +func (s *SystemSuite) TestMemUsage(c *C) { + mem, err := GetMemUsage() + + c.Assert(err, IsNil) + c.Assert(mem, NotNil) +} From a3a2de16859fcb99c9e696cd55d4396e36ebf342 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Sun, 15 Sep 2024 00:00:50 +0300 Subject: [PATCH 07/15] [support] Add locale to default OS info output --- support/support.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/support/support.go b/support/support.go index 3a197f13..fe5e6c3f 100644 --- a/support/support.go +++ b/support/support.go @@ -476,6 +476,8 @@ func (i *Info) printOSInfo() { ) } + format(12, true, "Locale", os.Getenv("LANG")) + if i.System.ContainerEngine != "" { fmtc.NewLine() switch i.System.ContainerEngine { From 4df4dff5801ffa3cb063402d81683167a1ce78b6 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Sun, 15 Sep 2024 00:08:32 +0300 Subject: [PATCH 08/15] [support] Improve documentation --- support/support.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/support/support.go b/support/support.go index fe5e6c3f..d56c8fee 100644 --- a/support/support.go +++ b/support/support.go @@ -15,7 +15,8 @@ There are also some sub-packages to collect/parse additional information: - pkgs: Package for collecting information about installed packages - services: Package for collecting information about services - fs: Package for collecting information about the file system - - network: Package to collect information about the network + - network: Package for collecting information about the network + - resources: Package for collecting information about CPU and memory Example of collecting maximum information about the application and system: @@ -29,6 +30,7 @@ Example of collecting maximum information about the application and system: WithEnvVars("LANG", "PAGER", "SSH_CLIENT"). WithNetwork(network.Collect("https://cloudflare.com/cdn-cgi/trace")). WithFS(fs.Collect()). + WithResources(resources.Collect()). Print() Also, you can't encode data to JSON/GOB and send it to your server instead of printing @@ -44,6 +46,7 @@ it to the console. WithEnvVars("LANG", "PAGER", "SSH_CLIENT"). WithNetwork(network.Collect("https://cloudflare.com/cdn-cgi/trace")). WithFS(fs.Collect()) + WithResources(resources.Collect()) b, _ := json.Marshal(info) From 8ea55b9d1e49a704e3d6e79ab56ddac738dbcbbc Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Sun, 15 Sep 2024 00:14:11 +0300 Subject: [PATCH 09/15] Update changelog --- CHANGELOG.md | 8 ++++++++ version.go | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42f46261..6b0b3e3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ ## Changelog +### [13.5.0](https://kaos.sh/ek/13.5.0) + +- `[support/resources]` Added package for collecting info about CPU and memory +- `[system/sysctl]` Added method `All` to get all kernel parameters +- `[system]` Added macOS support for `GetCPUInfo` +- `[system]` Added macOS support for `GetMemUsage` +- `[support]` Added locale to default OS info output + ### [13.4.0](https://kaos.sh/ek/13.4.0) - `[system/sysctl]` Added new package for reading kernel parameters diff --git a/version.go b/version.go index c5519f42..3df16829 100644 --- a/version.go +++ b/version.go @@ -8,4 +8,4 @@ package ek // ////////////////////////////////////////////////////////////////////////////////// // // VERSION is current ek package version -const VERSION = "13.4.0" +const VERSION = "13.5.0" From 8b6e70d9dce17f98595dd364bd6f699f8608b46e Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Sun, 15 Sep 2024 14:24:49 +0300 Subject: [PATCH 10/15] [mathutil] Add methods 'IsInt', 'IsFloat', and 'IsNumber' --- CHANGELOG.md | 1 + mathutil/example_test.go | 30 +++++++++++++++++++++++ mathutil/mathutil.go | 51 +++++++++++++++++++++++++++++++++++++++ mathutil/mathutil_test.go | 9 +++++++ 4 files changed, 91 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b0b3e3f..c9bfd398 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - `[system]` Added macOS support for `GetCPUInfo` - `[system]` Added macOS support for `GetMemUsage` - `[support]` Added locale to default OS info output +- `[mathutil]` Added methods `IsInt`, `IsFloat`, and `IsNumber` ### [13.4.0](https://kaos.sh/ek/13.4.0) diff --git a/mathutil/example_test.go b/mathutil/example_test.go index 07ac5fd4..9cb5cd74 100644 --- a/mathutil/example_test.go +++ b/mathutil/example_test.go @@ -13,6 +13,36 @@ import ( // ////////////////////////////////////////////////////////////////////////////////// // +func ExampleIsNumber() { + fmt.Println(IsNumber("test")) + fmt.Println(IsNumber("746131")) + fmt.Println(IsNumber("-10.431")) + // Output: + // false + // true + // true +} + +func ExampleIsInt() { + fmt.Println(IsInt("test")) + fmt.Println(IsInt("746131")) + fmt.Println(IsInt("-194")) + // Output: + // false + // true + // true +} + +func ExampleIsFloat() { + fmt.Println(IsFloat("test")) + fmt.Println(IsFloat("74.6131")) + fmt.Println(IsFloat("-10.4")) + // Output: + // false + // true + // true +} + func ExampleBetween() { fmt.Println(Between(10, 1, 5)) fmt.Println(Between(-3, 1, 5)) diff --git a/mathutil/mathutil.go b/mathutil/mathutil.go index 13cc162b..6631ee96 100644 --- a/mathutil/mathutil.go +++ b/mathutil/mathutil.go @@ -31,6 +31,57 @@ type NumericNeg interface { // ////////////////////////////////////////////////////////////////////////////////// // +// IsInt returns true if given string contains int symbols. +// +// Note that this method does not validate the given value. +func IsInt(s string) bool { + if s == "" { + return false + } + + for _, r := range s { + switch r { + // - , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 + case 45, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57: + // continue + default: + return false + } + } + + return true +} + +// IsFloat returns true if given string contains float symbols. +// +// Note that this method does not validate the given value. +func IsFloat(s string) bool { + if s == "" { + return false + } + + for _, r := range s { + switch r { + // - , . , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 + case 45, 46, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57: + // continue + default: + return false + } + } + + return true +} + +// IsNumber returns true if given string contains number symbols (int or float). +// +// Note that this method does not validate the given value. +func IsNumber(s string) bool { + return IsInt(s) || IsFloat(s) +} + +// ////////////////////////////////////////////////////////////////////////////////// // + // Between returns value between min and max values func Between[N Numeric](val, min, max N) N { switch { diff --git a/mathutil/mathutil_test.go b/mathutil/mathutil_test.go index b286285c..e8da32a0 100644 --- a/mathutil/mathutil_test.go +++ b/mathutil/mathutil_test.go @@ -25,6 +25,15 @@ var _ = Suite(&MathUtilSuite{}) // ////////////////////////////////////////////////////////////////////////////////// // +func (s *MathUtilSuite) TestChecks(c *C) { + c.Assert(IsNumber("1234567890.1"), Equals, true) + c.Assert(IsNumber("1234567890"), Equals, true) + c.Assert(IsInt(""), Equals, false) + c.Assert(IsFloat(""), Equals, false) + c.Assert(IsInt("1234567890a"), Equals, false) + c.Assert(IsFloat("1234567890.1a"), Equals, false) +} + func (s *MathUtilSuite) TestBetween(c *C) { c.Assert(Between(5, 0, 100), Equals, 5) c.Assert(Between(10, 0, 5), Equals, 5) From 538253aafbf62e4c3eb8e70f21ddf01bb58c0aee Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Sun, 15 Sep 2024 14:30:33 +0300 Subject: [PATCH 11/15] [support/resources] Enable macOS --- support/resources/resources.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/support/resources/resources.go b/support/resources/resources.go index 7b2f5c86..fc7e6dee 100644 --- a/support/resources/resources.go +++ b/support/resources/resources.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux || darwin +// +build linux darwin // Package resources provides methods for collecting information about system // resources (cpu/memory) From d816f43044adcff8692496e0ccdaec1c88749b25 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Sun, 15 Sep 2024 15:39:59 +0300 Subject: [PATCH 12/15] [support/kernel] Add package for collecting OS kernel parameters --- CHANGELOG.md | 1 + support/kernel/kernel.go | 51 ++++++++++++++++++++++ support/kernel/kernel_windows.go | 18 ++++++++ support/support.go | 72 +++++++++++++++++++++++++------- 4 files changed, 126 insertions(+), 16 deletions(-) create mode 100644 support/kernel/kernel.go create mode 100644 support/kernel/kernel_windows.go diff --git a/CHANGELOG.md b/CHANGELOG.md index c9bfd398..77ecc58e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### [13.5.0](https://kaos.sh/ek/13.5.0) - `[support/resources]` Added package for collecting info about CPU and memory +- `[support/kernel]` Added package for collecting OS kernel parameters - `[system/sysctl]` Added method `All` to get all kernel parameters - `[system]` Added macOS support for `GetCPUInfo` - `[system]` Added macOS support for `GetMemUsage` diff --git a/support/kernel/kernel.go b/support/kernel/kernel.go new file mode 100644 index 00000000..d7f7b2f8 --- /dev/null +++ b/support/kernel/kernel.go @@ -0,0 +1,51 @@ +//go:build linux || darwin +// +build linux darwin + +// Package kernel provides methods for collecting information from OS kernel +package kernel + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import ( + "strings" + + "github.com/essentialkaos/ek/v13/strutil" + "github.com/essentialkaos/ek/v13/support" + "github.com/essentialkaos/ek/v13/system/sysctl" +) + +// ////////////////////////////////////////////////////////////////////////////////// // + +// Collect collects info from OS kernel +func Collect(params ...string) []support.KernelParam { + kernelParams, err := sysctl.All() + + if err != nil { + return nil + } + + var result []support.KernelParam + + for _, param := range params { + for k, v := range kernelParams { + if !strings.HasPrefix(k, param) { + continue + } + + value := strings.ReplaceAll(v, "\t", " ") + value = strutil.SqueezeRepeats(value, " ") + + result = append(result, support.KernelParam{ + Key: k, + Value: value, + }) + } + } + + return result +} diff --git a/support/kernel/kernel_windows.go b/support/kernel/kernel_windows.go new file mode 100644 index 00000000..63f1549a --- /dev/null +++ b/support/kernel/kernel_windows.go @@ -0,0 +1,18 @@ +// Package kernel provides methods for collecting information from OS kernel +package kernel + +// ////////////////////////////////////////////////////////////////////////////////// // +// // +// Copyright (c) 2024 ESSENTIAL KAOS // +// Apache License, Version 2.0 // +// // +// ////////////////////////////////////////////////////////////////////////////////// // + +import "github.com/essentialkaos/ek/v13/support" + +// ////////////////////////////////////////////////////////////////////////////////// // + +// Collect collects info from OS kernel +func Collect(params ...string) []support.KernelParam { + return nil +} diff --git a/support/support.go b/support/support.go index d56c8fee..7e4365a8 100644 --- a/support/support.go +++ b/support/support.go @@ -3,20 +3,21 @@ Package support provides methods for collecting and printing support information about system. By default, it collects information about the application and environment: - - Name - - Version - - Go version used - - Binary SHA - - Git commit SHA + - App name + - App version + - Go version used for build + - Binary SHA checksum + - Git commit SHA checksum There are also some sub-packages to collect/parse additional information: - - apps: Package for extracting apps versions info - - deps: Package for extracting dependency information from gomod data - - pkgs: Package for collecting information about installed packages - - services: Package for collecting information about services - - fs: Package for collecting information about the file system - - network: Package for collecting information about the network - - resources: Package for collecting information about CPU and memory + - apps: Extracting apps versions info + - deps: Extracting dependency information from gomod data + - pkgs: Collecting information about installed packages + - services: Collecting information about services + - fs: Collecting information about the file system + - network: Collecting information about the network + - resources: Collecting information about CPU and memory + - kernel: Collecting information from OS kernel Example of collecting maximum information about the application and system: @@ -31,6 +32,7 @@ Example of collecting maximum information about the application and system: WithNetwork(network.Collect("https://cloudflare.com/cdn-cgi/trace")). WithFS(fs.Collect()). WithResources(resources.Collect()). + WithKernel(kernel.Collect()). Print() Also, you can't encode data to JSON/GOB and send it to your server instead of printing @@ -45,8 +47,9 @@ it to the console. WithChecks(myAppAvailabilityCheck()). WithEnvVars("LANG", "PAGER", "SSH_CLIENT"). WithNetwork(network.Collect("https://cloudflare.com/cdn-cgi/trace")). - WithFS(fs.Collect()) - WithResources(resources.Collect()) + WithFS(fs.Collect()). + WithResources(resources.Collect()). + WithKernel(kernel.Collect()) b, _ := json.Marshal(info) @@ -67,6 +70,7 @@ import ( "path/filepath" "runtime" "runtime/debug" + "strconv" "strings" "github.com/essentialkaos/ek/v13/fmtc" @@ -108,6 +112,7 @@ type Info struct { System *SystemInfo `json:"system,omitempty"` Network *NetworkInfo `json:"network,omitempty"` Resources *ResourcesInfo `json:"resources,omitempty"` + Kernel []KernelParam `json:"kernel,omitempty"` FS []FSInfo `json:"fs,omitempty"` Pkgs []Pkg `json:"pkgs,omitempty"` Services []Service `json:"services,omitempty"` @@ -217,6 +222,9 @@ type EnvVar struct { Value string `json:"value"` } +// KernelParam contains info about kernel parameter +type KernelParam = EnvVar + // Check contains info about custom check type Check struct { Status CheckStatus `json:"status"` @@ -376,6 +384,17 @@ func (i *Info) WithResources(info *ResourcesInfo) *Info { return i } +// WithKernel adds kernel parameters info +func (i *Info) WithKernel(info []KernelParam) *Info { + if i == nil { + return nil + } + + i.Kernel = info + + return i +} + // ////////////////////////////////////////////////////////////////////////////////// // // Print prints support info @@ -392,6 +411,7 @@ func (i *Info) Print() { i.printAppInfo() i.printOSInfo() i.printResourcesInfo() + i.printKernelInfo() i.printNetworkInfo() i.printFSInfo() i.printEnvVars() @@ -422,7 +442,7 @@ func (i *Info) appendBuildInfo() { // printAppInfo prints info about app func (i *Info) printAppInfo() { - fmtutil.Separator(false, "APPLICATION INFO") + fmtutil.Separator(false, "APPLICATION") name := i.Name @@ -449,7 +469,7 @@ func (i *Info) printAppInfo() { // printOSInfo prints info about OS and system func (i *Info) printOSInfo() { if i.OS != nil { - fmtutil.Separator(false, "OS INFO") + fmtutil.Separator(false, "OS") format(12, true, "Name", strutil.Q(i.OS.coloredName, i.OS.Name), @@ -553,6 +573,26 @@ func (i *Info) printResourcesInfo() { } } +// printKernelInfo prints kernel parameters +func (i *Info) printKernelInfo() { + if i.Kernel == nil { + return + } + + fmtutil.Separator(false, "KERNEL") + + keySize := getMaxKeySize(i.Kernel) + + for _, p := range i.Kernel { + if mathutil.IsInt(p.Value) { + vi, _ := strconv.Atoi(p.Value) + format(keySize, true, p.Key, fmtutil.PrettyNum(vi)) + } else { + format(keySize, true, p.Key, p.Value) + } + } +} + // printEnvVars prints environment variables func (i *Info) printEnvVars() { if len(i.Env) == 0 { From 54597685ba53fd937c1a43004f3d6ea7bdbef9c9 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Sun, 15 Sep 2024 15:40:18 +0300 Subject: [PATCH 13/15] [system/sysctl] Fix stubs --- system/sysctl/sysctl_stubs.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/system/sysctl/sysctl_stubs.go b/system/sysctl/sysctl_stubs.go index 1cd96e24..f9dfd962 100644 --- a/system/sysctl/sysctl_stubs.go +++ b/system/sysctl/sysctl_stubs.go @@ -11,6 +11,16 @@ package sysctl // // // ////////////////////////////////////////////////////////////////////////////////// // +// Params contains all kernel parameters +type Params map[string]string + +// ////////////////////////////////////////////////////////////////////////////////// // + +// All returns all kernel parameters +func All() (Params, error) { + return nil, nil +} + // Get returns kernel parameter value as a string func Get(param string) (string, error) { return "", nil @@ -25,3 +35,20 @@ func GetI(param string) (int, error) { func GetI64(param string) (int64, error) { return 0, nil } + +// ////////////////////////////////////////////////////////////////////////////////// // + +// Get returns kernel parameter value as a string +func (p Params) Get(name string) string { + return "" +} + +// GetI returns kernel parameter value as an int +func (p Params) GetI(param string) (int, error) { + return 0, nil +} + +// GetI64 returns kernel parameter value as an int64 +func (p Params) GetI64(param string) (int64, error) { + return 0, nil +} From 8394fc4b0678870f9e30517d29bb74193d27ae12 Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Sun, 15 Sep 2024 15:55:54 +0300 Subject: [PATCH 14/15] [support] Add info if CGO is used for build --- CHANGELOG.md | 1 + support/support.go | 44 ++++++++++++++++++++++++-------------------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77ecc58e..5fcb405b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - `[system]` Added macOS support for `GetMemUsage` - `[support]` Added locale to default OS info output - `[mathutil]` Added methods `IsInt`, `IsFloat`, and `IsNumber` +- `[support]` Added info if CGO is used for build ### [13.4.0](https://kaos.sh/ek/13.4.0) diff --git a/support/support.go b/support/support.go index 7e4365a8..2ff28783 100644 --- a/support/support.go +++ b/support/support.go @@ -127,6 +127,7 @@ type BuildInfo struct { GoVersion string `json:"go_version"` GoArch string `json:"go_arch"` GoOS string `json:"go_os"` + CGO bool `json:"cgo"` GitSHA string `json:"git_sha,omitempty"` BinSHA string `json:"bin_sha,omitempty"` @@ -281,8 +282,6 @@ func (i *Info) WithRevision(rev string) *Info { return i } - i.Build.GitSHA = extractGitRevFromBuildInfo() - return i } @@ -436,6 +435,22 @@ func (i *Info) appendBuildInfo() { bin, _ := os.Executable() binSHA := hash.FileHash(bin) + info, ok := debug.ReadBuildInfo() + + if ok { + for _, s := range info.Settings { + switch s.Key { + case "CGO_ENABLED": + if s.Value == "1" { + i.Build.CGO = true + } + case "vcs.revision": + if len(s.Value) > 7 { + i.Build.GitSHA = s.Value[:7] + } + } + } + } i.Build.BinSHA = strutil.Head(binSHA, 7) } @@ -459,8 +474,14 @@ func (i *Info) printAppInfo() { return } + goInfo := fmtc.Sprintf("%s {s}(%s/%s){!}", i.Build.GoVersion, i.Build.GoOS, i.Build.GoArch) + + if i.Build.CGO { + goInfo += fmtc.Sprint(" {s}+CGO{!}") + } + format(7, false, - "Go", fmtc.Sprintf("%s {s}(%s/%s){!}", i.Build.GoVersion, i.Build.GoOS, i.Build.GoArch), + "Go", goInfo, "Git SHA", i.Build.GitSHA+getHashColorBullet(i.Build.GitSHA), "Bin SHA", i.Build.BinSHA+getHashColorBullet(i.Build.BinSHA), ) @@ -814,23 +835,6 @@ func format(size int, printEmpty bool, records ...string) { } } -// extractGitRevFromBuildInfo extracts git SHA from embedded build info -func extractGitRevFromBuildInfo() string { - info, ok := debug.ReadBuildInfo() - - if !ok { - return "" - } - - for _, s := range info.Settings { - if s.Key == "vcs.revision" && len(s.Value) > 7 { - return s.Value[:7] - } - } - - return "" -} - // getHashColorBullet return bullet with color from hash func getHashColorBullet(v string) string { if v == "" || fmtc.DisableColors || !fmtc.IsTrueColorSupported() { From b9bb86c2798721967773e57c44426d3e7d7d816f Mon Sep 17 00:00:00 2001 From: Anton Novojilov Date: Sun, 15 Sep 2024 16:21:46 +0300 Subject: [PATCH 15/15] [support] Improve tests --- support/support.go | 6 ++++- support/support_test.go | 56 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/support/support.go b/support/support.go index 2ff28783..62cf369d 100644 --- a/support/support.go +++ b/support/support.go @@ -235,6 +235,10 @@ type Check struct { // ////////////////////////////////////////////////////////////////////////////////// // +var buildInfoProvider = debug.ReadBuildInfo + +// ////////////////////////////////////////////////////////////////////////////////// // + // Collect collects basic info about system func Collect(app, ver string) *Info { bin, _ := os.Executable() @@ -435,7 +439,7 @@ func (i *Info) appendBuildInfo() { bin, _ := os.Executable() binSHA := hash.FileHash(bin) - info, ok := debug.ReadBuildInfo() + info, ok := buildInfoProvider() if ok { for _, s := range info.Settings { diff --git a/support/support_test.go b/support/support_test.go index 07956dc7..c80fae02 100644 --- a/support/support_test.go +++ b/support/support_test.go @@ -9,6 +9,7 @@ package support import ( "os" + "runtime/debug" "testing" "github.com/essentialkaos/ek/v13/fmtc" @@ -51,6 +52,8 @@ require ( func (s *SupportSuite) TestCollect(c *C) { i := Collect("test", "1.2.3") + c.Assert(i, NotNil) + os.Setenv("SUPPORT_VAR", "123") chks := []Check{ @@ -85,7 +88,29 @@ func (s *SupportSuite) TestCollect(c *C) { App{}, } - c.Assert(i, NotNil) + resources := &ResourcesInfo{ + CPU: []CPUInfo{ + { + Model: "Virtual", + Threads: 4, + Cores: 2, + }, + }, + MemTotal: 8 * 1024 * 1024, + MemFree: 6 * 1024 * 1024, + MemUsed: 2 * 1024 * 1024, + SwapTotal: 2 * 1024 * 1024, + SwapFree: 1 * 1024 * 1024, + SwapUsed: 1 * 1024 * 1024, + } + + params := []KernelParam{ + {"fs.inotify.max_user_watches", "27024"}, + {"kernel.random.boot_id", "3cfe3c24-c698-42fc-b232-af5345f828f7"}, + } + + i.Build.CGO = true + c.Assert(i.WithDeps(deps), NotNil) c.Assert(i.WithRevision(""), NotNil) c.Assert(i.WithRevision("1234567"), NotNil) @@ -96,6 +121,14 @@ func (s *SupportSuite) TestCollect(c *C) { c.Assert(i.WithEnvVars("", "SUPPORT_VAR", "TERM", "CI"), NotNil) c.Assert(i.WithNetwork(&NetworkInfo{PublicIP: "192.168.1.1", Hostname: "test.loc"}), NotNil) c.Assert(i.WithFS([]FSInfo{FSInfo{}, FSInfo{"/", "/dev/vda1", "ext4", 1000, 10000}}), NotNil) + c.Assert(i.WithResources(resources), NotNil) + c.Assert(i.WithKernel(params), NotNil) + + i.Print() + + i.Resources.SwapTotal = 0 + i.Resources.SwapFree = 0 + i.Resources.SwapUsed = 0 i.Print() @@ -109,6 +142,8 @@ func (s *SupportSuite) TestCollect(c *C) { i.Network = nil i.FS = nil i.Deps = nil + i.Resources = nil + i.Kernel = nil i.Print() @@ -136,6 +171,8 @@ func (s *SupportSuite) TestNil(c *C) { c.Assert(i.WithEnvVars(""), IsNil) c.Assert(i.WithNetwork(nil), IsNil) c.Assert(i.WithFS(nil), IsNil) + c.Assert(i.WithResources(nil), IsNil) + c.Assert(i.WithKernel(nil), IsNil) c.Assert(func() { i.Print() }, NotPanics) } @@ -163,3 +200,20 @@ func (s *SupportSuite) TestSizeCalc(c *C) { FSInfo{Device: "/dev/sda1"}, FSInfo{Device: "/dev/test/test"}, FSInfo{Device: "/dev"}, }), Equals, 14) } + +func (s *SupportSuite) TestBuildInfo(c *C) { + buildInfoProvider = func() (*debug.BuildInfo, bool) { + return &debug.BuildInfo{ + Settings: []debug.BuildSetting{ + {"CGO_ENABLED", "1"}, + {"vcs.revision", "8b6e70d9dce17f98595dd364bd6f699f8608b46e"}, + }, + }, true + } + + i := Collect("test", "1.2.3") + + c.Assert(i, NotNil) + + buildInfoProvider = debug.ReadBuildInfo +}