From 9c0452fef67274541196f3f57149a95573b47938 Mon Sep 17 00:00:00 2001 From: Neo Zhuo Date: Tue, 20 Jul 2021 11:19:47 +0800 Subject: [PATCH] support custom `/proc` path --- pkg/systemstatsmonitor/cpu_collector.go | 10 +++++++--- pkg/systemstatsmonitor/cpu_collector_linux.go | 3 +-- pkg/systemstatsmonitor/cpu_collector_test.go | 2 +- pkg/systemstatsmonitor/net_collector.go | 6 ++++-- pkg/systemstatsmonitor/osfeature_collector.go | 13 +++++++++---- pkg/systemstatsmonitor/system_stats_monitor.go | 6 +++--- pkg/systemstatsmonitor/types/config.go | 9 +++++++++ pkg/systemstatsmonitor/types/config_test.go | 2 ++ pkg/util/metrics/system/cmdline_args.go | 4 +--- pkg/util/metrics/system/cmdline_args_test.go | 10 ++-------- pkg/util/metrics/system/module_stats.go | 4 +--- pkg/util/metrics/system/module_stats_test.go | 10 ++-------- 12 files changed, 42 insertions(+), 37 deletions(-) diff --git a/pkg/systemstatsmonitor/cpu_collector.go b/pkg/systemstatsmonitor/cpu_collector.go index 8419756a6..e4c5b6752 100644 --- a/pkg/systemstatsmonitor/cpu_collector.go +++ b/pkg/systemstatsmonitor/cpu_collector.go @@ -46,13 +46,17 @@ type cpuCollector struct { mSystemInterruptsTotal *metrics.Int64Metric mSystemCPUStat *metrics.Float64Metric // per-cpu time from /proc/stats - config *ssmtypes.CPUStatsConfig + config *ssmtypes.CPUStatsConfig + procPath string lastUsageTime map[string]float64 } -func NewCPUCollectorOrDie(cpuConfig *ssmtypes.CPUStatsConfig) *cpuCollector { - cc := cpuCollector{config: cpuConfig} +func NewCPUCollectorOrDie(cpuConfig *ssmtypes.CPUStatsConfig, procPath string) *cpuCollector { + cc := cpuCollector{ + config: cpuConfig, + procPath: procPath, + } var err error cc.mRunnableTaskCount, err = metrics.NewFloat64Metric( diff --git a/pkg/systemstatsmonitor/cpu_collector_linux.go b/pkg/systemstatsmonitor/cpu_collector_linux.go index 0d10887a6..9ab1b9c20 100644 --- a/pkg/systemstatsmonitor/cpu_collector_linux.go +++ b/pkg/systemstatsmonitor/cpu_collector_linux.go @@ -52,7 +52,6 @@ func (cc *cpuCollector) recordLoad() { } func (cc *cpuCollector) recordSystemStats() { - // don't collect the load metrics if the configs are not present. if cc.mSystemCPUStat == nil && cc.mSystemInterruptsTotal == nil && cc.mSystemProcessesTotal == nil && cc.mSystemProcsBlocked == nil && @@ -60,7 +59,7 @@ func (cc *cpuCollector) recordSystemStats() { return } - fs, err := procfs.NewFS("/proc") + fs, err := procfs.NewFS(cc.procPath) stats, err := fs.Stat() if err != nil { glog.Errorf("Failed to retrieve cpu/process stats: %v", err) diff --git a/pkg/systemstatsmonitor/cpu_collector_test.go b/pkg/systemstatsmonitor/cpu_collector_test.go index faf3fb674..1ed204d1f 100644 --- a/pkg/systemstatsmonitor/cpu_collector_test.go +++ b/pkg/systemstatsmonitor/cpu_collector_test.go @@ -67,6 +67,6 @@ func TestCpuCollector(t *testing.T) { if err := json.Unmarshal([]byte(fakeCPUConfig), cfg); err != nil { t.Fatalf("cannot load cpu config: %s", err) } - mc := NewCPUCollectorOrDie(cfg) + mc := NewCPUCollectorOrDie(cfg, "/proc") mc.collect() } diff --git a/pkg/systemstatsmonitor/net_collector.go b/pkg/systemstatsmonitor/net_collector.go index df6300442..716145edd 100644 --- a/pkg/systemstatsmonitor/net_collector.go +++ b/pkg/systemstatsmonitor/net_collector.go @@ -28,12 +28,14 @@ import ( type netCollector struct { config *ssmtypes.NetStatsConfig + procPath string recorder *ifaceStatRecorder } -func NewNetCollectorOrDie(netConfig *ssmtypes.NetStatsConfig) *netCollector { +func NewNetCollectorOrDie(netConfig *ssmtypes.NetStatsConfig, procPath string) *netCollector { nc := netCollector{ config: netConfig, + procPath: procPath, recorder: newIfaceStatRecorder(), } @@ -207,7 +209,7 @@ func (nc *netCollector) mustRegisterMetric(metricID metrics.MetricID, descriptio } func (nc *netCollector) recordNetDev() { - fs, err := procfs.NewFS("/proc") + fs, err := procfs.NewFS(nc.procPath) stats, err := fs.NetDev() if err != nil { glog.Errorf("Failed to retrieve net dev stat: %v", err) diff --git a/pkg/systemstatsmonitor/osfeature_collector.go b/pkg/systemstatsmonitor/osfeature_collector.go index afff5fe97..ede1196b2 100644 --- a/pkg/systemstatsmonitor/osfeature_collector.go +++ b/pkg/systemstatsmonitor/osfeature_collector.go @@ -16,6 +16,7 @@ package systemstatsmonitor import ( "encoding/json" "io/ioutil" + "path/filepath" "strconv" "strings" @@ -27,11 +28,15 @@ import ( type osFeatureCollector struct { config *ssmtypes.OSFeatureStatsConfig + procPath string osFeature *metrics.Int64Metric } -func NewOsFeatureCollectorOrDie(osFeatureConfig *ssmtypes.OSFeatureStatsConfig) *osFeatureCollector { - oc := osFeatureCollector{config: osFeatureConfig} +func NewOsFeatureCollectorOrDie(osFeatureConfig *ssmtypes.OSFeatureStatsConfig, procPath string) *osFeatureCollector { + oc := osFeatureCollector{ + config: osFeatureConfig, + procPath: procPath, + } var err error // Use metrics.Last aggregation method to ensure the metric is a guage metric. if osFeatureConfig.MetricsConfigs["system/os_feature"].DisplayName != "" { @@ -145,12 +150,12 @@ func (ofc *osFeatureCollector) collect() { if ofc == nil || ofc.osFeature == nil { return } - cmdlineArgs, err := system.CmdlineArgs() + cmdlineArgs, err := system.CmdlineArgs(filepath.Join(ofc.procPath, "/cmdline")) if err != nil { glog.Fatalf("Error retrieving cmdline args: %v", err) } ofc.recordFeaturesFromCmdline(cmdlineArgs) - modules, err := system.Modules() + modules, err := system.Modules(filepath.Join(ofc.procPath, "/modules")) if err != nil { glog.Fatalf("Error retrieving kernel modules: %v", err) } diff --git a/pkg/systemstatsmonitor/system_stats_monitor.go b/pkg/systemstatsmonitor/system_stats_monitor.go index f419c6ded..765db3ecd 100644 --- a/pkg/systemstatsmonitor/system_stats_monitor.go +++ b/pkg/systemstatsmonitor/system_stats_monitor.go @@ -78,7 +78,7 @@ func NewSystemStatsMonitorOrDie(configPath string) types.Monitor { } if len(ssm.config.CPUConfig.MetricsConfigs) > 0 { - ssm.cpuCollector = NewCPUCollectorOrDie(&ssm.config.CPUConfig) + ssm.cpuCollector = NewCPUCollectorOrDie(&ssm.config.CPUConfig, ssm.config.ProcPath) } if len(ssm.config.DiskConfig.MetricsConfigs) > 0 { ssm.diskCollector = NewDiskCollectorOrDie(&ssm.config.DiskConfig) @@ -96,10 +96,10 @@ func NewSystemStatsMonitorOrDie(configPath string) types.Monitor { ssm.config.OsFeatureConfig.KnownModulesConfigPath = filepath.Join(filepath.Dir(configPath), ssm.config.OsFeatureConfig.KnownModulesConfigPath) } - ssm.osFeatureCollector = NewOsFeatureCollectorOrDie(&ssm.config.OsFeatureConfig) + ssm.osFeatureCollector = NewOsFeatureCollectorOrDie(&ssm.config.OsFeatureConfig, ssm.config.ProcPath) } if len(ssm.config.NetConfig.MetricsConfigs) > 0 { - ssm.netCollector = NewNetCollectorOrDie(&ssm.config.NetConfig) + ssm.netCollector = NewNetCollectorOrDie(&ssm.config.NetConfig, ssm.config.ProcPath) } return &ssm } diff --git a/pkg/systemstatsmonitor/types/config.go b/pkg/systemstatsmonitor/types/config.go index e2f265bd2..690d23789 100644 --- a/pkg/systemstatsmonitor/types/config.go +++ b/pkg/systemstatsmonitor/types/config.go @@ -18,6 +18,7 @@ package types import ( "fmt" + "os" "time" ) @@ -25,6 +26,7 @@ var ( defaultInvokeIntervalString = (60 * time.Second).String() defaultlsblkTimeoutString = (5 * time.Second).String() defaultKnownModulesConfigPath = "guestosconfig/known-modules.json" + defaultProcPath = "/proc" ) type MetricConfig struct { @@ -69,6 +71,7 @@ type SystemStatsConfig struct { NetConfig NetStatsConfig `json:"net"` InvokeIntervalString string `json:"invokeInterval"` InvokeInterval time.Duration `json:"-"` + ProcPath string `json:"procPath"` } // ApplyConfiguration applies default configurations. @@ -76,6 +79,9 @@ func (ssc *SystemStatsConfig) ApplyConfiguration() error { if ssc.InvokeIntervalString == "" { ssc.InvokeIntervalString = defaultInvokeIntervalString } + if ssc.ProcPath == "" { + ssc.ProcPath = defaultProcPath + } if ssc.DiskConfig.LsblkTimeoutString == "" { ssc.DiskConfig.LsblkTimeoutString = defaultlsblkTimeoutString } @@ -101,6 +107,9 @@ func (ssc *SystemStatsConfig) Validate() error { if ssc.InvokeInterval <= time.Duration(0) { return fmt.Errorf("InvokeInterval %v must be above 0s", ssc.InvokeInterval) } + if _, err := os.Stat(ssc.ProcPath); err != nil { + return fmt.Errorf("ProcPath %v check failed: %s", ssc.ProcPath, err) + } if ssc.DiskConfig.LsblkTimeout <= time.Duration(0) { return fmt.Errorf("LsblkTimeout %v must be above 0s", ssc.DiskConfig.LsblkTimeout) } diff --git a/pkg/systemstatsmonitor/types/config_test.go b/pkg/systemstatsmonitor/types/config_test.go index ebc933a3e..253ca62f0 100644 --- a/pkg/systemstatsmonitor/types/config_test.go +++ b/pkg/systemstatsmonitor/types/config_test.go @@ -48,6 +48,7 @@ func TestApplyConfiguration(t *testing.T) { }, InvokeIntervalString: "60s", InvokeInterval: 60 * time.Second, + ProcPath: defaultProcPath, }, }, { @@ -66,6 +67,7 @@ func TestApplyConfiguration(t *testing.T) { }, InvokeIntervalString: "1m0s", InvokeInterval: 60 * time.Second, + ProcPath: defaultProcPath, }, }, { diff --git a/pkg/util/metrics/system/cmdline_args.go b/pkg/util/metrics/system/cmdline_args.go index 792261b37..db1c53cfa 100644 --- a/pkg/util/metrics/system/cmdline_args.go +++ b/pkg/util/metrics/system/cmdline_args.go @@ -19,8 +19,6 @@ import ( "strings" ) -var cmdlineFilePath = "/proc/cmdline" - type CmdlineArg struct { Key string `json:"key"` Value string `json:"value"` @@ -52,7 +50,7 @@ func splitAfterSpace(inputChar rune) bool { } // CmdlineArgs returns all the kernel cmdline. It is read from cat /proc/cmdline. -func CmdlineArgs() ([]CmdlineArg, error) { +func CmdlineArgs(cmdlineFilePath string) ([]CmdlineArg, error) { lines, err := ReadFileIntoLines(cmdlineFilePath) if err != nil { return nil, fmt.Errorf("error reading the file %s, %v", cmdlineFilePath, err) diff --git a/pkg/util/metrics/system/cmdline_args_test.go b/pkg/util/metrics/system/cmdline_args_test.go index 6470ffd6b..2e8da95ca 100644 --- a/pkg/util/metrics/system/cmdline_args_test.go +++ b/pkg/util/metrics/system/cmdline_args_test.go @@ -69,15 +69,9 @@ func TestCmdlineStats(t *testing.T) { } for _, test := range testcases { t.Run(test.name, func(t *testing.T) { - originalCmdlineFilePath := cmdlineFilePath - defer func() { - cmdlineFilePath = originalCmdlineFilePath - }() - - cmdlineFilePath = test.fakeCmdlineFilePath - cmdlineArgs, err := CmdlineArgs() + cmdlineArgs, err := CmdlineArgs(test.fakeCmdlineFilePath) if err != nil { - t.Errorf("Unexpected error retrieving cmdlineArgs: %v\nCmdlineArgsFilePath: %s\n", err, cmdlineFilePath) + t.Errorf("Unexpected error retrieving cmdlineArgs: %v\nCmdlineArgsFilePath: %s\n", err, test.fakeCmdlineFilePath) } for _, expectedCmdlineArg := range test.expectedCmdlineArgs { assert.Contains(t, cmdlineArgs, expectedCmdlineArg, "Failed to find cmdlineArgs: %v\n", expectedCmdlineArg) diff --git a/pkg/util/metrics/system/module_stats.go b/pkg/util/metrics/system/module_stats.go index 5b5da12ac..4cdf6211c 100644 --- a/pkg/util/metrics/system/module_stats.go +++ b/pkg/util/metrics/system/module_stats.go @@ -20,8 +20,6 @@ import ( "strings" ) -var modulesFilePath = "/proc/modules" - type Module struct { ModuleName string `json:"moduleName"` Instances uint64 `json:"instances"` @@ -37,7 +35,7 @@ func (d Module) String() string { // Module returns all the kernel modules and their // usage. It is read from cat /proc/modules. -func Modules() ([]Module, error) { +func Modules(modulesFilePath string) ([]Module, error) { lines, err := ReadFileIntoLines(modulesFilePath) if err != nil { return nil, fmt.Errorf("error reading the contents of %s: %s", modulesFilePath, err) diff --git a/pkg/util/metrics/system/module_stats_test.go b/pkg/util/metrics/system/module_stats_test.go index b80f00881..d08048e8c 100644 --- a/pkg/util/metrics/system/module_stats_test.go +++ b/pkg/util/metrics/system/module_stats_test.go @@ -97,15 +97,9 @@ func TestModules(t *testing.T) { } for _, test := range testcases { t.Run(test.name, func(t *testing.T) { - originalModuleFilePath := modulesFilePath - defer func() { - modulesFilePath = originalModuleFilePath - }() - - modulesFilePath = test.fakeModuleFilePath - modules, err := Modules() + modules, err := Modules(test.fakeModuleFilePath) if err != nil { - t.Errorf("Unexpected error retrieving modules: %v\nModulesFilePath: %s\n", err, modulesFilePath) + t.Errorf("Unexpected error retrieving modules: %v\nModulesFilePath: %s\n", err, test.fakeModuleFilePath) } assert.Equal(t, modules, test.expectedModules, "unpected modules retrieved: %v, expected: %v", modules, test.expectedModules) })