Files
troubleshoot/pkg/collect/host_kernel_configs.go
Gerard Nguyen f0b8de68ae feat: multiple nodes analyzers (#1667)
* implement refactor for multiple node analyzers

---------

Co-authored-by: Diamon Wiggins <38189728+diamonwiggins@users.noreply.github.com>
2024-11-04 14:17:39 +11:00

148 lines
3.9 KiB
Go

package collect
import (
"bufio"
"bytes"
"compress/gzip"
"encoding/json"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"regexp"
"strings"
"github.com/pkg/errors"
troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2"
)
type CollectHostKernelConfigs struct {
hostCollector *troubleshootv1beta2.HostKernelConfigs
BundlePath string
}
type KConfigs map[string]string
const HostKernelConfigsPath = `host-collectors/system/kernel-configs.json`
const HostKernelConfigsFileName = `kernel-configs.json`
const (
kConfigBuiltIn string = "y"
kConfigAsModule string = "m"
kConfigLeftOut string = "n"
)
func (c *CollectHostKernelConfigs) Title() string {
return hostCollectorTitleOrDefault(c.hostCollector.HostCollectorMeta, "kernel-configs")
}
func (c *CollectHostKernelConfigs) IsExcluded() (bool, error) {
return isExcluded(c.hostCollector.Exclude)
}
func (c *CollectHostKernelConfigs) Collect(progressChan chan<- interface{}) (map[string][]byte, error) {
kernelRelease, err := getKernelRelease()
if err != nil {
return nil, errors.Wrap(err, "failed to get kernel release")
}
var kConfigs KConfigs
kConfigs, err = loadKConfigs(kernelRelease)
if err != nil {
return nil, errors.Wrap(err, "failed to load kernel configs")
}
b, err := json.Marshal(kConfigs)
if err != nil {
return nil, errors.Wrap(err, "failed to marshal kernel configs")
}
output := NewResult()
output.SaveResult(c.BundlePath, HostKernelConfigsPath, bytes.NewBuffer(b))
return output, nil
}
func getKernelRelease() (string, error) {
out, err := exec.Command("uname", "-r").Output()
if err != nil {
return "", errors.Wrap(err, "failed to determine kernel release using uname -r")
}
release := strings.TrimSpace(string(out))
return release, nil
}
// https://github.com/k0sproject/k0s/blob/ddee3f980443e19620e678a6e1dc136ff053bff9/internal/pkg/sysinfo/probes/linux/kernel.go#L282
// loadKConfigs checks a list of well-known file system paths for kernel
// configuration files and tries to parse them.
func loadKConfigs(kernelRelease string) (KConfigs, error) {
// At least some references to those paths may be fond here:
// https://github.com/torvalds/linux/blob/v4.3/init/Kconfig#L794
// https://github.com/torvalds/linux/blob/v4.3/init/Kconfig#L9
possiblePaths := []string{
"/proc/config.gz",
"/boot/config-" + kernelRelease,
"/usr/src/linux-" + kernelRelease + "/.config",
"/usr/src/linux/.config",
"/usr/lib/modules/" + kernelRelease + "/config",
"/usr/lib/ostree-boot/config-" + kernelRelease,
"/usr/lib/kernel/config-" + kernelRelease,
"/usr/src/linux-headers-" + kernelRelease + "/.config",
"/lib/modules/" + kernelRelease + "/build/.config",
}
for _, path := range possiblePaths {
// open file for reading
f, err := os.Open(path)
if err != nil {
if os.IsNotExist(err) {
continue
}
return nil, err
}
defer f.Close()
r := io.Reader(bufio.NewReader(f))
// This is a gzip file (config.gz), unzip it.
if filepath.Ext(path) == ".gz" {
gr, err := gzip.NewReader(r)
if err != nil {
return nil, err
}
defer gr.Close()
r = gr
}
return parseKConfigs(r)
}
return nil, errors.Errorf("no kernel config files found for kernel release %q", kernelRelease)
}
// parseKConfigs parses `r` line by line, extracting all kernel config options.
func parseKConfigs(r io.Reader) (KConfigs, error) {
configs := KConfigs{}
kConfigLineRegex := regexp.MustCompile(fmt.Sprintf(
"^(CONFIG_[A-Z0-9_]+)=([%s%s%s])$",
string(kConfigBuiltIn), string(kConfigLeftOut), string(kConfigAsModule),
))
s := bufio.NewScanner(r)
for s.Scan() {
if err := s.Err(); err != nil {
return nil, err
}
if matches := kConfigLineRegex.FindStringSubmatch(s.Text()); matches != nil {
configs[matches[1]] = matches[2]
}
}
return configs, nil
}
func (c *CollectHostKernelConfigs) RemoteCollect(progressChan chan<- interface{}) (map[string][]byte, error) {
return nil, ErrRemoteCollectorNotImplemented
}