Files
troubleshoot/pkg/collect/host_kernel_configs.go
replicated-software-factory[bot]andElasticClaw Bot 7fa4497b7f chore: update Go dependencies (#2074)
- github.com/longhorn/go-iscsi-helper: replaced by github.com/longhorn/go-common-libs
  and migrated pkg/longhorn/util/iscsi.go to the new namespace executor API
- helm.sh/helm/v3: v3.21.2 -> v3.21.3
- oras.land/oras-go/v2: v2.6.1 -> v2.6.2
- google.golang.org/api: v0.287.1 -> v0.288.0
- golang.org/x/tools: v0.47.0 -> v0.48.0
- github.com/GoogleCloudPlatform/opentelemetry-operations-go/*: v1.33.0/v0.57.0 -> v1.34.0/v0.58.0
- examples/sdk/helm-template: helm.sh/helm/v3 v3.21.2 -> v3.21.3

Fixes required by the update environment:
- Makefile: use $(shell go env GOPATH)/bin for controller-gen/client-gen so
  generate works when the tools are not on PATH
- pkg/collect/host_kernel_configs.go: only use /proc/config.gz when the
  requested kernel release matches the running kernel, making the collector
  robust to hosts that expose a generic /proc/config.gz

Co-authored-by: ElasticClaw Bot <elasticclaw@openclaw.ai>
2026-07-14 13:12:51 +12:00

154 lines
4.2 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{
"/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",
}
// /proc/config.gz reflects the currently running kernel. Only use it when
// the requested kernel release matches the running kernel.
currentRelease, err := getKernelRelease()
if err == nil && currentRelease == kernelRelease {
possiblePaths = append([]string{"/proc/config.gz"}, possiblePaths...)
}
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
}