From 2be94c5a1f48615033e5d472d1f387c9c901af38 Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Mon, 3 Aug 2026 10:41:42 +0200 Subject: [PATCH 01/24] Add more non-identifying Collector attributes --- superv/go.mod | 8 +++ superv/go.sum | 20 ++++++ superv/supervisor/opamp_client.go | 72 +++++++++++-------- .../hostinfo/hostinfo-almalinux-10.json | 15 ++++ .../hostinfo/hostinfo-almalinux-8.json | 15 ++++ .../hostinfo/hostinfo-almalinux-9.json | 15 ++++ .../testdata/hostinfo/hostinfo-alpine-3.json | 15 ++++ .../testdata/hostinfo/hostinfo-debian-13.json | 15 ++++ .../hostinfo/hostinfo-linux-ubuntu-2404.json | 15 ++++ .../hostinfo/hostinfo-linux-ubuntu-2604.json | 15 ++++ .../testdata/hostinfo/hostinfo-macos-26.json | 15 ++++ .../hostinfo/hostinfo-ubuntu-2404.json | 15 ++++ .../hostinfo/hostinfo-ubuntu-2604.json | 15 ++++ .../hostinfo/hostinfo-windows-2019.json | 15 ++++ .../hostinfo/hostinfo-windows-2022.json | 15 ++++ .../hostinfo/hostinfo-windows-2025.json | 15 ++++ 16 files changed, 267 insertions(+), 28 deletions(-) create mode 100644 superv/testdata/hostinfo/hostinfo-almalinux-10.json create mode 100644 superv/testdata/hostinfo/hostinfo-almalinux-8.json create mode 100644 superv/testdata/hostinfo/hostinfo-almalinux-9.json create mode 100644 superv/testdata/hostinfo/hostinfo-alpine-3.json create mode 100644 superv/testdata/hostinfo/hostinfo-debian-13.json create mode 100644 superv/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json create mode 100644 superv/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json create mode 100644 superv/testdata/hostinfo/hostinfo-macos-26.json create mode 100644 superv/testdata/hostinfo/hostinfo-ubuntu-2404.json create mode 100644 superv/testdata/hostinfo/hostinfo-ubuntu-2604.json create mode 100644 superv/testdata/hostinfo/hostinfo-windows-2019.json create mode 100644 superv/testdata/hostinfo/hostinfo-windows-2022.json create mode 100644 superv/testdata/hostinfo/hostinfo-windows-2025.json diff --git a/superv/go.mod b/superv/go.mod index 9daa4e0c..3577e406 100644 --- a/superv/go.mod +++ b/superv/go.mod @@ -18,6 +18,7 @@ require ( github.com/knadh/koanf/providers/structs v1.0.0 github.com/knadh/koanf/v2 v2.3.5 github.com/open-telemetry/opamp-go v0.23.0 + github.com/shirou/gopsutil/v4 v4.26.6 github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.11.1 go.opentelemetry.io/contrib/bridges/otelzap v0.19.0 @@ -37,19 +38,26 @@ require ( github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/ebitengine/purego v0.10.0 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/compress v1.19.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/michel-laterman/proxy-connect-dialer-go v0.1.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/spf13/pflag v1.0.10 // indirect + github.com/tklauser/go-sysconf v0.3.16 // indirect + github.com/tklauser/numcpus v0.11.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/otel/log v0.20.0 // indirect go.opentelemetry.io/otel/metric v1.44.0 // indirect diff --git a/superv/go.sum b/superv/go.sum index 0b654de3..04210263 100644 --- a/superv/go.sum +++ b/superv/go.sum @@ -9,6 +9,8 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= +github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -20,6 +22,8 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= @@ -28,6 +32,7 @@ github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63Y github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/renameio/v2 v2.0.2 h1:qKZs+tfn+arruZZhQ7TKC/ergJunuJicWS6gLDt/dGw= @@ -60,6 +65,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/madflojo/testcerts v1.5.0 h1:GhQllyAiGzXVZU+i8O/cQkPTHzN59RxMGtm3uETgXnU= github.com/madflojo/testcerts v1.5.0/go.mod h1:MW8sh39gLnkKh4K0Nc55AyHEDl9l/FBLDUsQhpmkuo0= github.com/michel-laterman/proxy-connect-dialer-go v0.1.0 h1:Q8asukpmyrEheocd+R+6YEI4jcm62sHHalgTMG+LoLw= @@ -72,9 +79,13 @@ github.com/open-telemetry/opamp-go v0.23.0 h1:k7h7w/muprut9/DAhUC4anX4v7hIdgO02g github.com/open-telemetry/opamp-go v0.23.0/go.mod h1:DIIVdkLefdqPW5L+4I2twmAicVrTB0Bp5XJAfedZzAM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil/v4 v4.26.6 h1:Mzr/npDtQC/xpeEuQKHZt8Zo9CmPvhTj8nkR8w5TLDs= +github.com/shirou/gopsutil/v4 v4.26.6/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -84,6 +95,12 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= +github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= +github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= +github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/bridges/otelzap v0.19.0 h1:48Eq3xxFx2KlL/tF7lnl42kKJBDlhNTLRzv0h154JnM= @@ -124,10 +141,13 @@ golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/genproto/googleapis/api v0.0.0-20260706201446-f0a921348800 h1:admdQBe8jR3VWhBsUrAOaF2Qw6K/+p5pSm1GN8+6Fw4= diff --git a/superv/supervisor/opamp_client.go b/superv/supervisor/opamp_client.go index 8e5fe4a2..875267ed 100644 --- a/superv/supervisor/opamp_client.go +++ b/superv/supervisor/opamp_client.go @@ -30,7 +30,12 @@ import ( "github.com/Graylog2/collector/superv/supervisor/connection" "github.com/Graylog2/collector/superv/version" "github.com/open-telemetry/opamp-go/protobufs" + "github.com/shirou/gopsutil/v4/host" + "go.opentelemetry.io/otel/attribute" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" "go.uber.org/zap" + "golang.org/x/text/cases" + "golang.org/x/text/language" ) // createAndStartClient creates a new OpAMP client with current config, sets it up, and starts it. @@ -210,51 +215,62 @@ func (s *Supervisor) createOpAMPCallbacks() *opamp.Callbacks { } } +func stringKv(key, value string) *protobufs.KeyValue { + return &protobufs.KeyValue{ + Key: key, + Value: &protobufs.AnyValue{Value: &protobufs.AnyValue_StringValue{StringValue: value}}, + } +} + +func attributeStringKv(key attribute.Key, value string) *protobufs.KeyValue { + return stringKv(string(key), value) +} + // createAgentDescription creates the initial agent description for OpAMP. func (s *Supervisor) createAgentDescription() *protobufs.AgentDescription { hostname, _ := os.Hostname() return &protobufs.AgentDescription{ IdentifyingAttributes: []*protobufs.KeyValue{ - { - Key: "service.name", - Value: &protobufs.AnyValue{Value: &protobufs.AnyValue_StringValue{StringValue: ServiceName}}, - }, - { - Key: "service.instance.id", - Value: &protobufs.AnyValue{Value: &protobufs.AnyValue_StringValue{StringValue: s.instanceUID}}, - }, + attributeStringKv(semconv.ServiceNameKey, ServiceName), + attributeStringKv(semconv.ServiceInstanceIDKey, s.instanceUID), }, NonIdentifyingAttributes: s.nonIdentifyingAttributes(hostname), } } +// getOSDescription builds an "os.description" value for each platform. +func getOSDescription(info *host.InfoStat) string { + switch runtime.GOOS { + case "darwin": + return "macOS " + info.PlatformVersion + case "linux": + return cases.Title(language.English).String(info.Platform) + " " + info.PlatformVersion + case "windows": + return info.Platform + " " + info.PlatformVersion + default: + return "Unknown " + runtime.GOOS + } +} + // nonIdentifyingAttributes builds the list of non-identifying attributes for the agent description. func (s *Supervisor) nonIdentifyingAttributes(hostname string) []*protobufs.KeyValue { attrs := []*protobufs.KeyValue{ - { - Key: "service.version", - Value: &protobufs.AnyValue{Value: &protobufs.AnyValue_StringValue{StringValue: version.Version()}}, - }, - { - Key: "host.name", - Value: &protobufs.AnyValue{Value: &protobufs.AnyValue_StringValue{StringValue: hostname}}, - }, - { - Key: "os.type", - Value: &protobufs.AnyValue{Value: &protobufs.AnyValue_StringValue{StringValue: runtime.GOOS}}, - }, - { - Key: "host.arch", - Value: &protobufs.AnyValue{Value: &protobufs.AnyValue_StringValue{StringValue: runtime.GOARCH}}, - }, + attributeStringKv(semconv.HostArchKey, runtime.GOARCH), + attributeStringKv(semconv.HostNameKey, hostname), + attributeStringKv(semconv.OSTypeKey, runtime.GOOS), + attributeStringKv(semconv.ServiceVersionKey, version.Version()), + } + + info, err := host.Info() + if err == nil { + attrs = append(attrs, attributeStringKv(semconv.OSDescriptionKey, getOSDescription(info))) + } else { + s.logger.Warn("Failed to retrieve host information", zap.Error(err)) } if s.collectorVersion != "" { - attrs = append(attrs, &protobufs.KeyValue{ - Key: "collector.version", - Value: &protobufs.AnyValue{Value: &protobufs.AnyValue_StringValue{StringValue: s.collectorVersion}}, - }) + attrs = append(attrs, stringKv("collector.version", s.collectorVersion)) } return attrs diff --git a/superv/testdata/hostinfo/hostinfo-almalinux-10.json b/superv/testdata/hostinfo/hostinfo-almalinux-10.json new file mode 100644 index 00000000..5224d23f --- /dev/null +++ b/superv/testdata/hostinfo/hostinfo-almalinux-10.json @@ -0,0 +1,15 @@ +{ + "hostname": "60fb66970fa2", + "uptime": 17955, + "bootTime": 1785485632, + "procs": 1, + "os": "linux", + "platform": "almalinux", + "platformFamily": "rhel", + "platformVersion": "10.1", + "kernelVersion": "7.0.0-28-generic", + "kernelArch": "x86_64", + "virtualizationSystem": "docker", + "virtualizationRole": "guest", + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" +} diff --git a/superv/testdata/hostinfo/hostinfo-almalinux-8.json b/superv/testdata/hostinfo/hostinfo-almalinux-8.json new file mode 100644 index 00000000..682ceecb --- /dev/null +++ b/superv/testdata/hostinfo/hostinfo-almalinux-8.json @@ -0,0 +1,15 @@ +{ + "hostname": "1a3f2327d015", + "uptime": 17940, + "bootTime": 1785485632, + "procs": 1, + "os": "linux", + "platform": "almalinux", + "platformFamily": "rhel", + "platformVersion": "8.10", + "kernelVersion": "7.0.0-28-generic", + "kernelArch": "x86_64", + "virtualizationSystem": "docker", + "virtualizationRole": "guest", + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" +} diff --git a/superv/testdata/hostinfo/hostinfo-almalinux-9.json b/superv/testdata/hostinfo/hostinfo-almalinux-9.json new file mode 100644 index 00000000..a2b83170 --- /dev/null +++ b/superv/testdata/hostinfo/hostinfo-almalinux-9.json @@ -0,0 +1,15 @@ +{ + "hostname": "83fcd2bc3eac", + "uptime": 17904, + "bootTime": 1785485632, + "procs": 1, + "os": "linux", + "platform": "almalinux", + "platformFamily": "rhel", + "platformVersion": "9.7", + "kernelVersion": "7.0.0-28-generic", + "kernelArch": "x86_64", + "virtualizationSystem": "docker", + "virtualizationRole": "guest", + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" +} diff --git a/superv/testdata/hostinfo/hostinfo-alpine-3.json b/superv/testdata/hostinfo/hostinfo-alpine-3.json new file mode 100644 index 00000000..cde82abd --- /dev/null +++ b/superv/testdata/hostinfo/hostinfo-alpine-3.json @@ -0,0 +1,15 @@ +{ + "hostname": "65444f511f5a", + "uptime": 17977, + "bootTime": 1785485632, + "procs": 1, + "os": "linux", + "platform": "alpine", + "platformFamily": "alpine", + "platformVersion": "3.23.3", + "kernelVersion": "7.0.0-28-generic", + "kernelArch": "x86_64", + "virtualizationSystem": "docker", + "virtualizationRole": "guest", + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" +} diff --git a/superv/testdata/hostinfo/hostinfo-debian-13.json b/superv/testdata/hostinfo/hostinfo-debian-13.json new file mode 100644 index 00000000..73fa6af0 --- /dev/null +++ b/superv/testdata/hostinfo/hostinfo-debian-13.json @@ -0,0 +1,15 @@ +{ + "hostname": "23789806c978", + "uptime": 17856, + "bootTime": 1785485632, + "procs": 1, + "os": "linux", + "platform": "debian", + "platformFamily": "debian", + "platformVersion": "13.3", + "kernelVersion": "7.0.0-28-generic", + "kernelArch": "x86_64", + "virtualizationSystem": "docker", + "virtualizationRole": "guest", + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" +} diff --git a/superv/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json b/superv/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json new file mode 100644 index 00000000..8b8e20a1 --- /dev/null +++ b/superv/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json @@ -0,0 +1,15 @@ +{ + "hostname": "server-1", + "uptime": 703486, + "bootTime": 1784799975, + "procs": 148, + "os": "linux", + "platform": "ubuntu", + "platformFamily": "debian", + "platformVersion": "24.04", + "kernelVersion": "6.8.0-136-generic", + "kernelArch": "x86_64", + "virtualizationSystem": "", + "virtualizationRole": "guest", + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" +} diff --git a/superv/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json b/superv/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json new file mode 100644 index 00000000..e37d290d --- /dev/null +++ b/superv/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json @@ -0,0 +1,15 @@ +{ + "hostname": "server-2", + "uptime": 17700, + "bootTime": 1785485632, + "procs": 502, + "os": "linux", + "platform": "ubuntu", + "platformFamily": "debian", + "platformVersion": "26.04", + "kernelVersion": "7.0.0-28-generic", + "kernelArch": "x86_64", + "virtualizationSystem": "kvm", + "virtualizationRole": "host", + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" +} diff --git a/superv/testdata/hostinfo/hostinfo-macos-26.json b/superv/testdata/hostinfo/hostinfo-macos-26.json new file mode 100644 index 00000000..c2bfd2c6 --- /dev/null +++ b/superv/testdata/hostinfo/hostinfo-macos-26.json @@ -0,0 +1,15 @@ +{ + "hostname": "HOSTNAME-MP", + "uptime": 969219, + "bootTime": 1784534644, + "procs": 704, + "os": "darwin", + "platform": "darwin", + "platformFamily": "Standalone Workstation", + "platformVersion": "26.5.2", + "kernelVersion": "25.5.0", + "kernelArch": "x86_64", + "virtualizationSystem": "", + "virtualizationRole": "", + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" +} diff --git a/superv/testdata/hostinfo/hostinfo-ubuntu-2404.json b/superv/testdata/hostinfo/hostinfo-ubuntu-2404.json new file mode 100644 index 00000000..6fd3970b --- /dev/null +++ b/superv/testdata/hostinfo/hostinfo-ubuntu-2404.json @@ -0,0 +1,15 @@ +{ + "hostname": "nc", + "uptime": 703486, + "bootTime": 1784799975, + "procs": 148, + "os": "linux", + "platform": "ubuntu", + "platformFamily": "debian", + "platformVersion": "24.04", + "kernelVersion": "6.8.0-136-generic", + "kernelArch": "x86_64", + "virtualizationSystem": "", + "virtualizationRole": "guest", + "hostId": "6eb16cba-b843-4aa0-9d63-d1268ff85afa" +} diff --git a/superv/testdata/hostinfo/hostinfo-ubuntu-2604.json b/superv/testdata/hostinfo/hostinfo-ubuntu-2604.json new file mode 100644 index 00000000..cbfbd55c --- /dev/null +++ b/superv/testdata/hostinfo/hostinfo-ubuntu-2604.json @@ -0,0 +1,15 @@ +{ + "hostname": "t14s5", + "uptime": 17700, + "bootTime": 1785485632, + "procs": 502, + "os": "linux", + "platform": "ubuntu", + "platformFamily": "debian", + "platformVersion": "26.04", + "kernelVersion": "7.0.0-28-generic", + "kernelArch": "x86_64", + "virtualizationSystem": "kvm", + "virtualizationRole": "host", + "hostId": "9a028052-57c8-441e-8a21-263f8dec3cbf" +} diff --git a/superv/testdata/hostinfo/hostinfo-windows-2019.json b/superv/testdata/hostinfo/hostinfo-windows-2019.json new file mode 100644 index 00000000..95d1f7dd --- /dev/null +++ b/superv/testdata/hostinfo/hostinfo-windows-2019.json @@ -0,0 +1,15 @@ +{ + "hostname": "win2019", + "uptime": 13397856, + "bootTime": 1772105492, + "procs": 110, + "os": "windows", + "platform": "Microsoft Windows Server 2019 Datacenter", + "platformFamily": "Server", + "platformVersion": "", + "kernelVersion": "10.0.17763.8389 Build 17763.8389", + "kernelArch": "x86_64", + "virtualizationSystem": "", + "virtualizationRole": "", + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" +} diff --git a/superv/testdata/hostinfo/hostinfo-windows-2022.json b/superv/testdata/hostinfo/hostinfo-windows-2022.json new file mode 100644 index 00000000..0bcf8e19 --- /dev/null +++ b/superv/testdata/hostinfo/hostinfo-windows-2022.json @@ -0,0 +1,15 @@ +{ + "hostname": "win2022", + "uptime": 13398027, + "bootTime": 1772105348, + "procs": 117, + "os": "windows", + "platform": "Microsoft Windows Server 2022 Datacenter", + "platformFamily": "Server", + "platformVersion": "21H2", + "kernelVersion": "10.0.20348.4773 Build 20348.4773", + "kernelArch": "x86_64", + "virtualizationSystem": "", + "virtualizationRole": "", + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" +} diff --git a/superv/testdata/hostinfo/hostinfo-windows-2025.json b/superv/testdata/hostinfo/hostinfo-windows-2025.json new file mode 100644 index 00000000..b952705e --- /dev/null +++ b/superv/testdata/hostinfo/hostinfo-windows-2025.json @@ -0,0 +1,15 @@ +{ + "hostname": "win2025", + "uptime": 12094781, + "bootTime": 1773408613, + "procs": 144, + "os": "windows", + "platform": "Microsoft Windows Server 2025 Datacenter", + "platformFamily": "Server", + "platformVersion": "24H2", + "kernelVersion": "10.0.26100.32370 Build 26100.32370", + "kernelArch": "x86_64", + "virtualizationSystem": "", + "virtualizationRole": "", + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" +} From 1747e47716532ed51392fdf451f3a6938dac5c42 Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Mon, 3 Aug 2026 14:27:33 +0200 Subject: [PATCH 02/24] Restructure agent description code and add tests --- superv/supervisor/opamp_agent_description.go | 79 ++++++++++++ .../opamp_agent_description_test.go | 113 ++++++++++++++++++ superv/supervisor/opamp_client.go | 69 ----------- superv/supervisor/opamp_client_test.go | 35 ------ .../hostinfo/hostinfo-almalinux-10.json | 0 .../hostinfo/hostinfo-almalinux-8.json | 0 .../hostinfo/hostinfo-almalinux-9.json | 0 .../testdata/hostinfo/hostinfo-alpine-3.json | 0 .../testdata/hostinfo/hostinfo-debian-13.json | 0 .../hostinfo/hostinfo-linux-ubuntu-2404.json | 0 .../hostinfo/hostinfo-linux-ubuntu-2604.json | 0 .../testdata/hostinfo/hostinfo-macos-26.json | 0 .../hostinfo/hostinfo-ubuntu-2404.json | 0 .../hostinfo/hostinfo-ubuntu-2604.json | 0 .../hostinfo/hostinfo-windows-2019.json | 0 .../hostinfo/hostinfo-windows-2022.json | 0 .../hostinfo/hostinfo-windows-2025.json | 0 17 files changed, 192 insertions(+), 104 deletions(-) create mode 100644 superv/supervisor/opamp_agent_description.go create mode 100644 superv/supervisor/opamp_agent_description_test.go rename superv/{ => supervisor}/testdata/hostinfo/hostinfo-almalinux-10.json (100%) rename superv/{ => supervisor}/testdata/hostinfo/hostinfo-almalinux-8.json (100%) rename superv/{ => supervisor}/testdata/hostinfo/hostinfo-almalinux-9.json (100%) rename superv/{ => supervisor}/testdata/hostinfo/hostinfo-alpine-3.json (100%) rename superv/{ => supervisor}/testdata/hostinfo/hostinfo-debian-13.json (100%) rename superv/{ => supervisor}/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json (100%) rename superv/{ => supervisor}/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json (100%) rename superv/{ => supervisor}/testdata/hostinfo/hostinfo-macos-26.json (100%) rename superv/{ => supervisor}/testdata/hostinfo/hostinfo-ubuntu-2404.json (100%) rename superv/{ => supervisor}/testdata/hostinfo/hostinfo-ubuntu-2604.json (100%) rename superv/{ => supervisor}/testdata/hostinfo/hostinfo-windows-2019.json (100%) rename superv/{ => supervisor}/testdata/hostinfo/hostinfo-windows-2022.json (100%) rename superv/{ => supervisor}/testdata/hostinfo/hostinfo-windows-2025.json (100%) diff --git a/superv/supervisor/opamp_agent_description.go b/superv/supervisor/opamp_agent_description.go new file mode 100644 index 00000000..3fd69cdf --- /dev/null +++ b/superv/supervisor/opamp_agent_description.go @@ -0,0 +1,79 @@ +package supervisor + +import ( + "os" + "runtime" + "strings" + + "github.com/Graylog2/collector/superv/version" + "github.com/open-telemetry/opamp-go/protobufs" + "github.com/shirou/gopsutil/v4/host" + "go.opentelemetry.io/otel/attribute" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" + "go.uber.org/zap" + "golang.org/x/text/cases" + "golang.org/x/text/language" +) + +// createAgentDescription creates the initial agent description for OpAMP. +func (s *Supervisor) createAgentDescription() *protobufs.AgentDescription { + hostname, _ := os.Hostname() + + return &protobufs.AgentDescription{ + IdentifyingAttributes: []*protobufs.KeyValue{ + attributeStringKv(semconv.ServiceNameKey, ServiceName), + attributeStringKv(semconv.ServiceInstanceIDKey, s.instanceUID), + }, + NonIdentifyingAttributes: s.nonIdentifyingAttributes(hostname), + } +} + +// nonIdentifyingAttributes builds the list of non-identifying attributes for the agent description. +func (s *Supervisor) nonIdentifyingAttributes(hostname string) []*protobufs.KeyValue { + attrs := []*protobufs.KeyValue{ + attributeStringKv(semconv.HostArchKey, runtime.GOARCH), + attributeStringKv(semconv.HostNameKey, hostname), + attributeStringKv(semconv.OSTypeKey, runtime.GOOS), + attributeStringKv(semconv.ServiceVersionKey, version.Version()), + } + + info, err := host.Info() + if err == nil { + attrs = append(attrs, attributeStringKv(semconv.OSDescriptionKey, getOSDescription(info))) + } else { + s.logger.Warn("Failed to retrieve host information", zap.Error(err)) + } + + if s.collectorVersion != "" { + attrs = append(attrs, stringKv("collector.version", s.collectorVersion)) + } + + return attrs +} + +// getOSDescription builds an "os.description" value for each platform. +func getOSDescription(info *host.InfoStat) string { + switch info.OS { + case "darwin": + return strings.TrimSpace("macOS " + info.PlatformVersion) + case "linux": + return strings.TrimSpace(cases.Title(language.English).String(info.Platform) + " " + info.PlatformVersion) + case "windows": + return strings.TrimSpace(info.Platform + " " + info.PlatformVersion) + default: + return "Unknown " + info.OS + } +} + +// stringKv returns a protobufs.KeyValue for the given key and value. +func stringKv(key, value string) *protobufs.KeyValue { + return &protobufs.KeyValue{ + Key: key, + Value: &protobufs.AnyValue{Value: &protobufs.AnyValue_StringValue{StringValue: value}}, + } +} + +// attributeStringKv returns a protobufs.KeyValue for the given attribute.Key and string value. +func attributeStringKv(key attribute.Key, value string) *protobufs.KeyValue { + return stringKv(string(key), value) +} diff --git a/superv/supervisor/opamp_agent_description_test.go b/superv/supervisor/opamp_agent_description_test.go new file mode 100644 index 00000000..5b835d52 --- /dev/null +++ b/superv/supervisor/opamp_agent_description_test.go @@ -0,0 +1,113 @@ +// Copyright (C) 2026 Graylog, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the Server Side Public License, version 1, +// as published by MongoDB, Inc. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// Server Side Public License for more details. +// +// You should have received a copy of the Server Side Public License +// along with this program. If not, see +// . +// +// SPDX-License-Identifier: SSPL-1.0 + +package supervisor + +import ( + "encoding/json" + "os" + "path/filepath" + "testing" + + "github.com/shirou/gopsutil/v4/host" + "github.com/stretchr/testify/require" +) + +func TestSupervisor_NonIdentifyingAttributes_WithCollectorVersion(t *testing.T) { + s := &Supervisor{ + collectorVersion: "2.0.0-alpha.0", + } + + attrs := s.nonIdentifyingAttributes("test-host") + + attrMap := make(map[string]string) + for _, kv := range attrs { + attrMap[kv.GetKey()] = kv.GetValue().GetStringValue() + } + + require.Equal(t, "test-host", attrMap["host.name"]) + require.NotEmpty(t, attrMap["service.version"]) + require.NotEmpty(t, attrMap["os.type"]) + require.NotEmpty(t, attrMap["host.arch"]) + require.Equal(t, "2.0.0-alpha.0", attrMap["collector.version"]) +} + +func TestSupervisor_NonIdentifyingAttributes_WithoutCollectorVersion(t *testing.T) { + s := &Supervisor{} + + attrs := s.nonIdentifyingAttributes("test-host") + + attrMap := make(map[string]string) + for _, kv := range attrs { + attrMap[kv.GetKey()] = kv.GetValue().GetStringValue() + } + + require.Equal(t, "test-host", attrMap["host.name"]) + require.NotEmpty(t, attrMap["service.version"]) + _, hasCollectorVersion := attrMap["collector.version"] + require.False(t, hasCollectorVersion, "collector.version should not be present when empty") +} + +func loadHostInfo(t *testing.T, name string) *host.InfoStat { + t.Helper() + + data, err := os.ReadFile(filepath.Join("testdata", "hostinfo", "hostinfo-"+name+".json")) + require.NoError(t, err) + + info := &host.InfoStat{} + require.NoError(t, json.Unmarshal(data, info)) + + return info +} + +func TestGetOSDescription(t *testing.T) { + tests := []struct { + fixture string + want string + }{ + {"almalinux-8", "Almalinux 8.10"}, + {"almalinux-9", "Almalinux 9.7"}, + {"almalinux-10", "Almalinux 10.1"}, + {"alpine-3", "Alpine 3.23.3"}, + {"debian-13", "Debian 13.3"}, + {"linux-ubuntu-2404", "Ubuntu 24.04"}, + {"linux-ubuntu-2604", "Ubuntu 26.04"}, + {"ubuntu-2404", "Ubuntu 24.04"}, + {"ubuntu-2604", "Ubuntu 26.04"}, + {"macos-26", "macOS 26.5.2"}, + {"windows-2019", "Microsoft Windows Server 2019 Datacenter"}, + {"windows-2022", "Microsoft Windows Server 2022 Datacenter 21H2"}, + {"windows-2025", "Microsoft Windows Server 2025 Datacenter 24H2"}, + } + + for _, tc := range tests { + t.Run(tc.fixture, func(t *testing.T) { + info := loadHostInfo(t, tc.fixture) + require.Equal(t, tc.want, getOSDescription(info)) + }) + } +} + +func TestGetOSDescription_UnknownOS(t *testing.T) { + info := &host.InfoStat{ + OS: "freebsd", + Platform: "freebsd", + PlatformVersion: "14.1", + } + + require.Equal(t, "Unknown freebsd", getOSDescription(info)) +} diff --git a/superv/supervisor/opamp_client.go b/superv/supervisor/opamp_client.go index 875267ed..30ee4820 100644 --- a/superv/supervisor/opamp_client.go +++ b/superv/supervisor/opamp_client.go @@ -21,21 +21,13 @@ import ( "context" "crypto/tls" "fmt" - "os" - "runtime" "time" "github.com/Graylog2/collector/superv/components" "github.com/Graylog2/collector/superv/opamp" "github.com/Graylog2/collector/superv/supervisor/connection" - "github.com/Graylog2/collector/superv/version" "github.com/open-telemetry/opamp-go/protobufs" - "github.com/shirou/gopsutil/v4/host" - "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.41.0" "go.uber.org/zap" - "golang.org/x/text/cases" - "golang.org/x/text/language" ) // createAndStartClient creates a new OpAMP client with current config, sets it up, and starts it. @@ -215,67 +207,6 @@ func (s *Supervisor) createOpAMPCallbacks() *opamp.Callbacks { } } -func stringKv(key, value string) *protobufs.KeyValue { - return &protobufs.KeyValue{ - Key: key, - Value: &protobufs.AnyValue{Value: &protobufs.AnyValue_StringValue{StringValue: value}}, - } -} - -func attributeStringKv(key attribute.Key, value string) *protobufs.KeyValue { - return stringKv(string(key), value) -} - -// createAgentDescription creates the initial agent description for OpAMP. -func (s *Supervisor) createAgentDescription() *protobufs.AgentDescription { - hostname, _ := os.Hostname() - - return &protobufs.AgentDescription{ - IdentifyingAttributes: []*protobufs.KeyValue{ - attributeStringKv(semconv.ServiceNameKey, ServiceName), - attributeStringKv(semconv.ServiceInstanceIDKey, s.instanceUID), - }, - NonIdentifyingAttributes: s.nonIdentifyingAttributes(hostname), - } -} - -// getOSDescription builds an "os.description" value for each platform. -func getOSDescription(info *host.InfoStat) string { - switch runtime.GOOS { - case "darwin": - return "macOS " + info.PlatformVersion - case "linux": - return cases.Title(language.English).String(info.Platform) + " " + info.PlatformVersion - case "windows": - return info.Platform + " " + info.PlatformVersion - default: - return "Unknown " + runtime.GOOS - } -} - -// nonIdentifyingAttributes builds the list of non-identifying attributes for the agent description. -func (s *Supervisor) nonIdentifyingAttributes(hostname string) []*protobufs.KeyValue { - attrs := []*protobufs.KeyValue{ - attributeStringKv(semconv.HostArchKey, runtime.GOARCH), - attributeStringKv(semconv.HostNameKey, hostname), - attributeStringKv(semconv.OSTypeKey, runtime.GOOS), - attributeStringKv(semconv.ServiceVersionKey, version.Version()), - } - - info, err := host.Info() - if err == nil { - attrs = append(attrs, attributeStringKv(semconv.OSDescriptionKey, getOSDescription(info))) - } else { - s.logger.Warn("Failed to retrieve host information", zap.Error(err)) - } - - if s.collectorVersion != "" { - attrs = append(attrs, stringKv("collector.version", s.collectorVersion)) - } - - return attrs -} - // setClientAvailableComponents discovers and sets available components on the given client. // It also stores the discovered collector version for use in agent descriptions. func (s *Supervisor) setClientAvailableComponents(ctx context.Context, client *opamp.Client) error { diff --git a/superv/supervisor/opamp_client_test.go b/superv/supervisor/opamp_client_test.go index cded6325..c1834511 100644 --- a/superv/supervisor/opamp_client_test.go +++ b/superv/supervisor/opamp_client_test.go @@ -29,41 +29,6 @@ import ( "go.uber.org/zap" ) -func TestSupervisor_NonIdentifyingAttributes_WithCollectorVersion(t *testing.T) { - s := &Supervisor{ - collectorVersion: "2.0.0-alpha.0", - } - - attrs := s.nonIdentifyingAttributes("test-host") - - attrMap := make(map[string]string) - for _, kv := range attrs { - attrMap[kv.GetKey()] = kv.GetValue().GetStringValue() - } - - require.Equal(t, "test-host", attrMap["host.name"]) - require.NotEmpty(t, attrMap["service.version"]) - require.NotEmpty(t, attrMap["os.type"]) - require.NotEmpty(t, attrMap["host.arch"]) - require.Equal(t, "2.0.0-alpha.0", attrMap["collector.version"]) -} - -func TestSupervisor_NonIdentifyingAttributes_WithoutCollectorVersion(t *testing.T) { - s := &Supervisor{} - - attrs := s.nonIdentifyingAttributes("test-host") - - attrMap := make(map[string]string) - for _, kv := range attrs { - attrMap[kv.GetKey()] = kv.GetValue().GetStringValue() - } - - require.Equal(t, "test-host", attrMap["host.name"]) - require.NotEmpty(t, attrMap["service.version"]) - _, hasCollectorVersion := attrMap["collector.version"] - require.False(t, hasCollectorVersion, "collector.version should not be present when empty") -} - func TestSupervisor_InitialComponentHealth_DefaultHealthyWithoutMonitor(t *testing.T) { supervisor := &Supervisor{} diff --git a/superv/testdata/hostinfo/hostinfo-almalinux-10.json b/superv/supervisor/testdata/hostinfo/hostinfo-almalinux-10.json similarity index 100% rename from superv/testdata/hostinfo/hostinfo-almalinux-10.json rename to superv/supervisor/testdata/hostinfo/hostinfo-almalinux-10.json diff --git a/superv/testdata/hostinfo/hostinfo-almalinux-8.json b/superv/supervisor/testdata/hostinfo/hostinfo-almalinux-8.json similarity index 100% rename from superv/testdata/hostinfo/hostinfo-almalinux-8.json rename to superv/supervisor/testdata/hostinfo/hostinfo-almalinux-8.json diff --git a/superv/testdata/hostinfo/hostinfo-almalinux-9.json b/superv/supervisor/testdata/hostinfo/hostinfo-almalinux-9.json similarity index 100% rename from superv/testdata/hostinfo/hostinfo-almalinux-9.json rename to superv/supervisor/testdata/hostinfo/hostinfo-almalinux-9.json diff --git a/superv/testdata/hostinfo/hostinfo-alpine-3.json b/superv/supervisor/testdata/hostinfo/hostinfo-alpine-3.json similarity index 100% rename from superv/testdata/hostinfo/hostinfo-alpine-3.json rename to superv/supervisor/testdata/hostinfo/hostinfo-alpine-3.json diff --git a/superv/testdata/hostinfo/hostinfo-debian-13.json b/superv/supervisor/testdata/hostinfo/hostinfo-debian-13.json similarity index 100% rename from superv/testdata/hostinfo/hostinfo-debian-13.json rename to superv/supervisor/testdata/hostinfo/hostinfo-debian-13.json diff --git a/superv/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json b/superv/supervisor/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json similarity index 100% rename from superv/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json rename to superv/supervisor/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json diff --git a/superv/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json b/superv/supervisor/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json similarity index 100% rename from superv/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json rename to superv/supervisor/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json diff --git a/superv/testdata/hostinfo/hostinfo-macos-26.json b/superv/supervisor/testdata/hostinfo/hostinfo-macos-26.json similarity index 100% rename from superv/testdata/hostinfo/hostinfo-macos-26.json rename to superv/supervisor/testdata/hostinfo/hostinfo-macos-26.json diff --git a/superv/testdata/hostinfo/hostinfo-ubuntu-2404.json b/superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2404.json similarity index 100% rename from superv/testdata/hostinfo/hostinfo-ubuntu-2404.json rename to superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2404.json diff --git a/superv/testdata/hostinfo/hostinfo-ubuntu-2604.json b/superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2604.json similarity index 100% rename from superv/testdata/hostinfo/hostinfo-ubuntu-2604.json rename to superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2604.json diff --git a/superv/testdata/hostinfo/hostinfo-windows-2019.json b/superv/supervisor/testdata/hostinfo/hostinfo-windows-2019.json similarity index 100% rename from superv/testdata/hostinfo/hostinfo-windows-2019.json rename to superv/supervisor/testdata/hostinfo/hostinfo-windows-2019.json diff --git a/superv/testdata/hostinfo/hostinfo-windows-2022.json b/superv/supervisor/testdata/hostinfo/hostinfo-windows-2022.json similarity index 100% rename from superv/testdata/hostinfo/hostinfo-windows-2022.json rename to superv/supervisor/testdata/hostinfo/hostinfo-windows-2022.json diff --git a/superv/testdata/hostinfo/hostinfo-windows-2025.json b/superv/supervisor/testdata/hostinfo/hostinfo-windows-2025.json similarity index 100% rename from superv/testdata/hostinfo/hostinfo-windows-2025.json rename to superv/supervisor/testdata/hostinfo/hostinfo-windows-2025.json From 381d3bf8a2f9438c82c3962a812b0b671c602f6d Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Mon, 3 Aug 2026 14:39:28 +0200 Subject: [PATCH 03/24] Add missing license header --- superv/supervisor/opamp_agent_description.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/superv/supervisor/opamp_agent_description.go b/superv/supervisor/opamp_agent_description.go index 3fd69cdf..834c36c8 100644 --- a/superv/supervisor/opamp_agent_description.go +++ b/superv/supervisor/opamp_agent_description.go @@ -1,3 +1,20 @@ +// Copyright (C) 2026 Graylog, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the Server Side Public License, version 1, +// as published by MongoDB, Inc. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// Server Side Public License for more details. +// +// You should have received a copy of the Server Side Public License +// along with this program. If not, see +// . +// +// SPDX-License-Identifier: SSPL-1.0 + package supervisor import ( From 0f3d073c6ad60dc63b2d848373c18c69175a8bf7 Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Mon, 3 Aug 2026 14:53:49 +0200 Subject: [PATCH 04/24] Implement proper product naming for Linux distributions --- superv/supervisor/opamp_agent_description.go | 29 +++++++++++- .../opamp_agent_description_test.go | 46 +++++++++++++++++-- .../hostinfo-amazon-2023.12.20260724.json | 15 ++++++ .../hostinfo/hostinfo-arch-20260726.json | 15 ++++++ ...hostinfo-opensuse-tumbleweed-20260802.json | 15 ++++++ .../hostinfo/hostinfo-ubuntu-2404.json | 2 +- .../hostinfo/hostinfo-ubuntu-2604.json | 2 +- 7 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 superv/supervisor/testdata/hostinfo/hostinfo-amazon-2023.12.20260724.json create mode 100644 superv/supervisor/testdata/hostinfo/hostinfo-arch-20260726.json create mode 100644 superv/supervisor/testdata/hostinfo/hostinfo-opensuse-tumbleweed-20260802.json diff --git a/superv/supervisor/opamp_agent_description.go b/superv/supervisor/opamp_agent_description.go index 834c36c8..8b8f80af 100644 --- a/superv/supervisor/opamp_agent_description.go +++ b/superv/supervisor/opamp_agent_description.go @@ -18,6 +18,7 @@ package supervisor import ( + "cmp" "os" "runtime" "strings" @@ -68,13 +69,39 @@ func (s *Supervisor) nonIdentifyingAttributes(hostname string) []*protobufs.KeyV return attrs } +// linuxPlatformNames maps gopsutil platform identifiers to product names +// where simple title-casing gets the spelling wrong. Identifiers come from +// distro release files or the ID field in /etc/os-release. +var linuxPlatformNames = map[string]string{ + "almalinux": "AlmaLinux", + "amazon": "Amazon Linux", + "amzn": "Amazon Linux", + "centos": "CentOS", + "cloudlinux": "CloudLinux", + "linuxmint": "Linux Mint", + "nixos": "NixOS", + "ol": "Oracle Linux", + "opensuse": "openSUSE", + "opensuse-leap": "openSUSE Leap", + "opensuse-tumbleweed": "openSUSE Tumbleweed", + "oracle": "Oracle Linux", + "redhat": "Red Hat Enterprise Linux", + "rhel": "Red Hat Enterprise Linux", + "rocky": "Rocky Linux", + "scientific": "Scientific Linux", + "sled": "SUSE Linux Enterprise Desktop", + "sles": "SUSE Linux Enterprise Server", + "suse": "SUSE", +} + // getOSDescription builds an "os.description" value for each platform. func getOSDescription(info *host.InfoStat) string { switch info.OS { case "darwin": return strings.TrimSpace("macOS " + info.PlatformVersion) case "linux": - return strings.TrimSpace(cases.Title(language.English).String(info.Platform) + " " + info.PlatformVersion) + name := cmp.Or(linuxPlatformNames[info.Platform], cases.Title(language.English).String(info.Platform)) + return strings.TrimSpace(name + " " + info.PlatformVersion) case "windows": return strings.TrimSpace(info.Platform + " " + info.PlatformVersion) default: diff --git a/superv/supervisor/opamp_agent_description_test.go b/superv/supervisor/opamp_agent_description_test.go index 5b835d52..78d1f1b4 100644 --- a/superv/supervisor/opamp_agent_description_test.go +++ b/superv/supervisor/opamp_agent_description_test.go @@ -79,11 +79,14 @@ func TestGetOSDescription(t *testing.T) { fixture string want string }{ - {"almalinux-8", "Almalinux 8.10"}, - {"almalinux-9", "Almalinux 9.7"}, - {"almalinux-10", "Almalinux 10.1"}, + {"almalinux-8", "AlmaLinux 8.10"}, + {"almalinux-9", "AlmaLinux 9.7"}, + {"almalinux-10", "AlmaLinux 10.1"}, {"alpine-3", "Alpine 3.23.3"}, + {"amazon-2023.12.20260724", "Amazon Linux 2023.12.20260724"}, + {"arch-20260726", "Arch 20260726.0.562117"}, {"debian-13", "Debian 13.3"}, + {"opensuse-tumbleweed-20260802", "openSUSE Tumbleweed 20260802"}, {"linux-ubuntu-2404", "Ubuntu 24.04"}, {"linux-ubuntu-2604", "Ubuntu 26.04"}, {"ubuntu-2404", "Ubuntu 24.04"}, @@ -102,6 +105,43 @@ func TestGetOSDescription(t *testing.T) { } } +// TestGetOSDescription_LinuxPlatformNames covers platform identifiers that +// gopsutil can report but for which we have no captured host info, ensuring +// their product names are spelled properly. +func TestGetOSDescription_LinuxPlatformNames(t *testing.T) { + tests := []struct { + platform string + version string + want string + }{ + {"redhat", "9.4", "Red Hat Enterprise Linux 9.4"}, + {"rhel", "9.4", "Red Hat Enterprise Linux 9.4"}, + {"centos", "9", "CentOS 9"}, + {"rocky", "9.4", "Rocky Linux 9.4"}, + {"oracle", "9.4", "Oracle Linux 9.4"}, + {"ol", "9.4", "Oracle Linux 9.4"}, + {"amazon", "2023", "Amazon Linux 2023"}, + {"amzn", "2023", "Amazon Linux 2023"}, + {"sles", "15.6", "SUSE Linux Enterprise Server 15.6"}, + {"sled", "15.6", "SUSE Linux Enterprise Desktop 15.6"}, + {"opensuse-leap", "15.6", "openSUSE Leap 15.6"}, + {"opensuse-tumbleweed", "20260802", "openSUSE Tumbleweed 20260802"}, + {"opensuse", "13.2", "openSUSE 13.2"}, + {"suse", "11.4", "SUSE 11.4"}, + {"linuxmint", "22", "Linux Mint 22"}, + {"nixos", "24.11", "NixOS 24.11"}, + {"cloudlinux", "9.4", "CloudLinux 9.4"}, + {"scientific", "7.9", "Scientific Linux 7.9"}, + } + + for _, tc := range tests { + t.Run(tc.platform, func(t *testing.T) { + info := &host.InfoStat{OS: "linux", Platform: tc.platform, PlatformVersion: tc.version} + require.Equal(t, tc.want, getOSDescription(info)) + }) + } +} + func TestGetOSDescription_UnknownOS(t *testing.T) { info := &host.InfoStat{ OS: "freebsd", diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-amazon-2023.12.20260724.json b/superv/supervisor/testdata/hostinfo/hostinfo-amazon-2023.12.20260724.json new file mode 100644 index 00000000..4e359b92 --- /dev/null +++ b/superv/supervisor/testdata/hostinfo/hostinfo-amazon-2023.12.20260724.json @@ -0,0 +1,15 @@ +{ + "hostname": "09425185f0ec", + "uptime": 436414, + "bootTime": 1785324667, + "procs": 1, + "os": "linux", + "platform": "amazon", + "platformFamily": "rhel", + "platformVersion": "2023.12.20260724", + "kernelVersion": "7.0.0-28-generic", + "kernelArch": "x86_64", + "virtualizationSystem": "docker", + "virtualizationRole": "guest", + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" +} diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-arch-20260726.json b/superv/supervisor/testdata/hostinfo/hostinfo-arch-20260726.json new file mode 100644 index 00000000..0c84506c --- /dev/null +++ b/superv/supervisor/testdata/hostinfo/hostinfo-arch-20260726.json @@ -0,0 +1,15 @@ +{ + "hostname": "6b5cc7679706", + "uptime": 436271, + "bootTime": 1785324667, + "procs": 1, + "os": "linux", + "platform": "arch", + "platformFamily": "arch", + "platformVersion": "20260726.0.562117", + "kernelVersion": "7.0.0-28-generic", + "kernelArch": "x86_64", + "virtualizationSystem": "docker", + "virtualizationRole": "guest", + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" +} diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-opensuse-tumbleweed-20260802.json b/superv/supervisor/testdata/hostinfo/hostinfo-opensuse-tumbleweed-20260802.json new file mode 100644 index 00000000..2b3dbc2c --- /dev/null +++ b/superv/supervisor/testdata/hostinfo/hostinfo-opensuse-tumbleweed-20260802.json @@ -0,0 +1,15 @@ +{ + "hostname": "bbf8cd358a87", + "uptime": 436346, + "bootTime": 1785324667, + "procs": 1, + "os": "linux", + "platform": "opensuse-tumbleweed", + "platformFamily": "suse", + "platformVersion": "20260802", + "kernelVersion": "7.0.0-28-generic", + "kernelArch": "x86_64", + "virtualizationSystem": "docker", + "virtualizationRole": "guest", + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" +} diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2404.json b/superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2404.json index 6fd3970b..e0bacd08 100644 --- a/superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2404.json +++ b/superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2404.json @@ -11,5 +11,5 @@ "kernelArch": "x86_64", "virtualizationSystem": "", "virtualizationRole": "guest", - "hostId": "6eb16cba-b843-4aa0-9d63-d1268ff85afa" + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" } diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2604.json b/superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2604.json index cbfbd55c..f34bb926 100644 --- a/superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2604.json +++ b/superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2604.json @@ -11,5 +11,5 @@ "kernelArch": "x86_64", "virtualizationSystem": "kvm", "virtualizationRole": "host", - "hostId": "9a028052-57c8-441e-8a21-263f8dec3cbf" + "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" } From a157e7ed5760d753655c5553864110d05762c32b Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Mon, 3 Aug 2026 14:54:26 +0200 Subject: [PATCH 05/24] Go mod tidy --- superv/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superv/go.mod b/superv/go.mod index 3577e406..638cc3b4 100644 --- a/superv/go.mod +++ b/superv/go.mod @@ -30,6 +30,7 @@ require ( go.uber.org/zap v1.28.0 golang.org/x/crypto v0.54.0 golang.org/x/sys v0.47.0 + golang.org/x/text v0.40.0 google.golang.org/grpc v1.82.0 google.golang.org/protobuf v1.36.11 ) @@ -66,7 +67,6 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/net v0.57.0 // indirect - golang.org/x/text v0.40.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260706201446-f0a921348800 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect From 2d362b0c7dcd4003c440c3a085297c1f197e503a Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Mon, 3 Aug 2026 14:56:35 +0200 Subject: [PATCH 06/24] Add changelog snippet --- changelog/unreleased/pr-87.toml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/unreleased/pr-87.toml diff --git a/changelog/unreleased/pr-87.toml b/changelog/unreleased/pr-87.toml new file mode 100644 index 00000000..8a88f6ea --- /dev/null +++ b/changelog/unreleased/pr-87.toml @@ -0,0 +1,5 @@ +type = "a" +message = "Add `os.description` attribute to Collector information." + +issues = ["Graylog2/graylog2-server#25176"] +pulls = ["87"] From dc795721037756f81bbe7c810055983173a1c31d Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Mon, 3 Aug 2026 14:58:44 +0200 Subject: [PATCH 07/24] Go mod tidy in builder/ --- builder/go.mod | 2 +- builder/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builder/go.mod b/builder/go.mod index 7bbc53ca..3cf8baa8 100644 --- a/builder/go.mod +++ b/builder/go.mod @@ -119,7 +119,7 @@ require ( github.com/prometheus/procfs v0.20.1 // indirect github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect github.com/rs/cors v1.11.1 // indirect - github.com/shirou/gopsutil/v4 v4.26.5 // indirect + github.com/shirou/gopsutil/v4 v4.26.6 // indirect github.com/sirupsen/logrus v1.9.4 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/testify v1.11.1 // indirect diff --git a/builder/go.sum b/builder/go.sum index 55a004db..57265ed5 100644 --- a/builder/go.sum +++ b/builder/go.sum @@ -210,8 +210,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7 github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shirou/gopsutil/v4 v4.26.5 h1:RPcBXkpz7kOj9PqGFQOlBPZHsyaPvPVQc098y9RmCNM= -github.com/shirou/gopsutil/v4 v4.26.5/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/shirou/gopsutil/v4 v4.26.6 h1:Mzr/npDtQC/xpeEuQKHZt8Zo9CmPvhTj8nkR8w5TLDs= +github.com/shirou/gopsutil/v4 v4.26.6/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= From 9f662debaf194747a8095d14c5dbe1bc2a8b3ccc Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Mon, 3 Aug 2026 16:06:55 +0200 Subject: [PATCH 08/24] Update dependencies --- builder/go.mod | 12 ++++++------ builder/go.sum | 24 +++++++++++++----------- superv/go.mod | 28 ++++++++++++++-------------- superv/go.sum | 26 +++++++++++++------------- 4 files changed, 46 insertions(+), 44 deletions(-) diff --git a/builder/go.mod b/builder/go.mod index d26f064b..9855c60f 100644 --- a/builder/go.mod +++ b/builder/go.mod @@ -49,7 +49,7 @@ require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/ebitengine/purego v0.10.0 // indirect + github.com/ebitengine/purego v0.10.2 // indirect github.com/elastic/go-ucfg v0.9.1 // indirect github.com/elastic/gosigar v0.14.4 // indirect github.com/elastic/lunes v0.2.2 // indirect @@ -61,7 +61,7 @@ require ( github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.6 // indirect @@ -90,7 +90,7 @@ require ( github.com/knadh/koanf/v2 v2.3.5 // indirect github.com/leodido/go-syslog/v4 v4.6.0 // indirect github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b // indirect - github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 // indirect + github.com/lufia/plan9stats v0.0.0-20260802145828-341c2f0c90b5 // indirect github.com/magefile/mage v1.17.2 // indirect github.com/michel-laterman/proxy-connect-dialer-go v0.1.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -119,12 +119,12 @@ require ( github.com/prometheus/procfs v0.21.0 // indirect github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 // indirect github.com/rs/cors v1.11.1 // indirect - github.com/shirou/gopsutil/v4 v4.26.6 // indirect + github.com/shirou/gopsutil/v4 v4.26.7 // indirect github.com/sirupsen/logrus v1.9.4 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/stretchr/testify v1.11.1 // indirect - github.com/tklauser/go-sysconf v0.3.16 // indirect - github.com/tklauser/numcpus v0.11.0 // indirect + github.com/tklauser/go-sysconf v0.4.0 // indirect + github.com/tklauser/numcpus v0.12.0 // indirect github.com/valyala/fastjson v1.6.10 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.etcd.io/bbolt v1.5.0 // indirect diff --git a/builder/go.sum b/builder/go.sum index 8efd57c2..918239a7 100644 --- a/builder/go.sum +++ b/builder/go.sum @@ -23,8 +23,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= -github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.10.2 h1:W809HbnvzAxgdm+aOvlSekrM16wGCdT/e76+9tS7gzE= +github.com/ebitengine/purego v0.10.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/elastic/go-ucfg v0.9.1 h1:OwbVLC9pAmHqlBDq5owRC7HbfldsLuqPqrwg23n17BQ= github.com/elastic/go-ucfg v0.9.1/go.mod h1:6Z66LNkFK5xAlWg3Ny7qgtrvBUadaAcor+kYxw2pXBk= github.com/elastic/gosigar v0.14.4 h1:7NRnWJDFjEKpOjnHhtzrPGZWr9EMrYFsLjF4q0Czosk= @@ -50,8 +50,9 @@ github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= @@ -123,8 +124,8 @@ github.com/leodido/go-syslog/v4 v4.6.0 h1:3o9H6K0cj9ITrNt7MG6CJ/Ae+vXmZcEPhI/N30 github.com/leodido/go-syslog/v4 v4.6.0/go.mod h1:BOEXCJSgy32THF4eZWwtZ11w6LrrFVBj+nMtv06ge4w= github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b h1:11UHH39z1RhZ5dc4y4r/4koJo6IYFgTRMe/LlwRTEw0= github.com/leodido/ragel-machinery v0.0.0-20190525184631-5f46317e436b/go.mod h1:WZxr2/6a/Ar9bMDc2rN/LJrE/hF6bXE4LPyDSIxwAfg= -github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3 h1:PwQumkgq4/acIiZhtifTV5OUqqiP82UAl0h87xj/l9k= -github.com/lufia/plan9stats v0.0.0-20251013123823-9fd1530e3ec3/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= +github.com/lufia/plan9stats v0.0.0-20260802145828-341c2f0c90b5 h1:eveIIGn4BGM3qknO74omf6HYr30/exH+eVUTuAgwjZ0= +github.com/lufia/plan9stats v0.0.0-20260802145828-341c2f0c90b5/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/madflojo/testcerts v1.5.0 h1:GhQllyAiGzXVZU+i8O/cQkPTHzN59RxMGtm3uETgXnU= github.com/madflojo/testcerts v1.5.0/go.mod h1:MW8sh39gLnkKh4K0Nc55AyHEDl9l/FBLDUsQhpmkuo0= github.com/magefile/mage v1.17.2 h1:fyXVu1eadI8Ap1HCCNgEhJ5McIWiYhLR8uol64ZZc40= @@ -210,8 +211,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7 github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shirou/gopsutil/v4 v4.26.6 h1:Mzr/npDtQC/xpeEuQKHZt8Zo9CmPvhTj8nkR8w5TLDs= -github.com/shirou/gopsutil/v4 v4.26.6/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/shirou/gopsutil/v4 v4.26.7 h1:IXzpHz/dkMRYAhKkOXr1HB6SuzWU3eoyyeWe7g3bNZc= +github.com/shirou/gopsutil/v4 v4.26.7/go.mod h1:5O9FjBiXoTDFatIWjZZosqj4pV0DRtLx598xGbBehzM= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= @@ -225,10 +226,10 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= -github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= -github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= -github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= +github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU= +github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI= +github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4= +github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg= github.com/valyala/fastjson v1.6.10 h1:/yjJg8jaVQdYR3arGxPE2X5z89xrlhS0eGXdv+ADTh4= github.com/valyala/fastjson v1.6.10/go.mod h1:e6FubmQouUNP73jtMLmcbxS6ydWIpOfhz34TSfO3JaE= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= @@ -476,6 +477,7 @@ golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek= golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= diff --git a/superv/go.mod b/superv/go.mod index 638cc3b4..22ed7246 100644 --- a/superv/go.mod +++ b/superv/go.mod @@ -3,7 +3,7 @@ module github.com/Graylog2/collector/superv go 1.26.0 require ( - github.com/DeRuina/timberjack v1.4.5 + github.com/DeRuina/timberjack v1.4.6 github.com/cenkalti/backoff/v5 v5.0.3 github.com/goccy/go-yaml v1.19.2 github.com/golang-jwt/jwt/v5 v5.3.1 @@ -18,7 +18,7 @@ require ( github.com/knadh/koanf/providers/structs v1.0.0 github.com/knadh/koanf/v2 v2.3.5 github.com/open-telemetry/opamp-go v0.23.0 - github.com/shirou/gopsutil/v4 v4.26.6 + github.com/shirou/gopsutil/v4 v4.26.7 github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.11.1 go.opentelemetry.io/contrib/bridges/otelzap v0.19.0 @@ -31,7 +31,7 @@ require ( golang.org/x/crypto v0.54.0 golang.org/x/sys v0.47.0 golang.org/x/text v0.40.0 - google.golang.org/grpc v1.82.0 + google.golang.org/grpc v1.83.0 google.golang.org/protobuf v1.36.11 ) @@ -39,35 +39,35 @@ require ( github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/ebitengine/purego v0.10.0 // indirect + github.com/ebitengine/purego v0.10.2 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/fsnotify/fsnotify v1.10.1 // indirect - github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/logr v1.4.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/klauspost/compress v1.19.0 // indirect - github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/klauspost/compress v1.19.1 // indirect + github.com/lufia/plan9stats v0.0.0-20260802145828-341c2f0c90b5 // indirect github.com/michel-laterman/proxy-connect-dialer-go v0.1.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/tklauser/go-sysconf v0.3.16 // indirect - github.com/tklauser/numcpus v0.11.0 // indirect + github.com/tklauser/go-sysconf v0.4.0 // indirect + github.com/tklauser/numcpus v0.12.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/otel/log v0.20.0 // indirect go.opentelemetry.io/otel/metric v1.44.0 // indirect go.opentelemetry.io/otel/trace v1.44.0 // indirect - go.opentelemetry.io/proto/otlp v1.10.0 // indirect + go.opentelemetry.io/proto/otlp v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect + go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.57.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260706201446-f0a921348800 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260706201446-f0a921348800 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260729162451-8efbd57d26e0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260729162451-8efbd57d26e0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/superv/go.sum b/superv/go.sum index 34471db4..ff728883 100644 --- a/superv/go.sum +++ b/superv/go.sum @@ -9,8 +9,8 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/ISU= -github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ebitengine/purego v0.10.2 h1:W809HbnvzAxgdm+aOvlSekrM16wGCdT/e76+9tS7gzE= +github.com/ebitengine/purego v0.10.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= @@ -22,8 +22,9 @@ github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM= @@ -32,7 +33,6 @@ github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63Y github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/renameio/v2 v2.0.2 h1:qKZs+tfn+arruZZhQ7TKC/ergJunuJicWS6gLDt/dGw= @@ -65,8 +65,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lufia/plan9stats v0.0.0-20260802145828-341c2f0c90b5 h1:eveIIGn4BGM3qknO74omf6HYr30/exH+eVUTuAgwjZ0= +github.com/lufia/plan9stats v0.0.0-20260802145828-341c2f0c90b5/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/madflojo/testcerts v1.5.0 h1:GhQllyAiGzXVZU+i8O/cQkPTHzN59RxMGtm3uETgXnU= github.com/madflojo/testcerts v1.5.0/go.mod h1:MW8sh39gLnkKh4K0Nc55AyHEDl9l/FBLDUsQhpmkuo0= github.com/michel-laterman/proxy-connect-dialer-go v0.1.0 h1:Q8asukpmyrEheocd+R+6YEI4jcm62sHHalgTMG+LoLw= @@ -84,8 +84,8 @@ github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:Om github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shirou/gopsutil/v4 v4.26.6 h1:Mzr/npDtQC/xpeEuQKHZt8Zo9CmPvhTj8nkR8w5TLDs= -github.com/shirou/gopsutil/v4 v4.26.6/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ= +github.com/shirou/gopsutil/v4 v4.26.7 h1:IXzpHz/dkMRYAhKkOXr1HB6SuzWU3eoyyeWe7g3bNZc= +github.com/shirou/gopsutil/v4 v4.26.7/go.mod h1:5O9FjBiXoTDFatIWjZZosqj4pV0DRtLx598xGbBehzM= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -95,10 +95,10 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA= -github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI= -github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw= -github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ= +github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU= +github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI= +github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4= +github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= @@ -144,11 +144,11 @@ golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE= golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/genproto/googleapis/api v0.0.0-20260729162451-8efbd57d26e0 h1:ybvH/ZpOcpCrjtkb7oW/fdlzbEmRVeumw19SRQmNFKU= From 017207aa7a12ac1ea0cadd90ff424ea7408fb1c3 Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 13:32:45 +0200 Subject: [PATCH 09/24] Add os-release parser and use that data for Linux The gopsutil data doesn't return the properly spelled Linux distribution names. --- superv/internal/testfixtures/fixtures.go | 14 + .../hostinfo/hostinfo-almalinux-10.json | 0 .../hostinfo/hostinfo-almalinux-8.json | 0 .../hostinfo/hostinfo-almalinux-9.json | 0 .../testdata/hostinfo/hostinfo-alpine-3.json | 0 .../hostinfo-amazon-2023.12.20260724.json | 0 .../hostinfo/hostinfo-arch-20260726.json | 0 .../testdata/hostinfo/hostinfo-debian-13.json | 0 .../hostinfo/hostinfo-linux-ubuntu-2404.json | 0 .../hostinfo/hostinfo-linux-ubuntu-2604.json | 0 .../testdata/hostinfo/hostinfo-macos-26.json | 0 ...hostinfo-opensuse-tumbleweed-20260802.json | 0 .../hostinfo/hostinfo-ubuntu-2404.json | 0 .../hostinfo/hostinfo-ubuntu-2604.json | 0 .../hostinfo/hostinfo-windows-2019.json | 0 .../hostinfo/hostinfo-windows-2022.json | 0 .../hostinfo/hostinfo-windows-2025.json | 0 .../os-release/os-release-almalinux-10.txt | 22 ++ .../os-release/os-release-almalinux-8.txt | 19 + .../os-release/os-release-almalinux-9.txt | 19 + .../os-release/os-release-alpine-3.txt | 6 + .../os-release/os-release-amazonlinux-2.txt | 10 + .../os-release-amazonlinux-2023.txt | 16 + .../os-release/os-release-arch-2026-08.txt | 12 + .../os-release/os-release-debian-12.txt | 9 + .../os-release/os-release-debian-13.txt | 10 + .../os-release/os-release-fedora-44.txt | 21 + .../os-release-fedora-container-44.txt | 22 ++ ...os-release-opensuse-tumbleweed-2026-08.txt | 16 + .../os-release/os-release-oraclelinux-10.txt | 18 + .../os-release/os-release-oraclelinux-8.txt | 18 + .../os-release/os-release-oraclelinux-9.txt | 18 + .../os-release/os-release-redhat-8.txt | 17 + .../os-release/os-release-redhat-9.txt | 18 + .../os-release/os-release-rockylinux-8.txt | 17 + .../os-release/os-release-rockylinux-9.txt | 17 + .../os-release/os-release-ubuntu-24.04.txt | 13 + .../os-release/os-release-ubuntu-26.04.txt | 13 + superv/internal/testsysinfo/os_release.go | 32 ++ superv/supervisor/opamp_agent_description.go | 66 ++-- .../opamp_agent_description_test.go | 97 ++--- superv/sysinfo/os_release.go | 106 +++++ superv/sysinfo/os_release_test.go | 362 ++++++++++++++++++ 43 files changed, 902 insertions(+), 106 deletions(-) create mode 100644 superv/internal/testfixtures/fixtures.go rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-almalinux-10.json (100%) rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-almalinux-8.json (100%) rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-almalinux-9.json (100%) rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-alpine-3.json (100%) rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-amazon-2023.12.20260724.json (100%) rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-arch-20260726.json (100%) rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-debian-13.json (100%) rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json (100%) rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json (100%) rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-macos-26.json (100%) rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-opensuse-tumbleweed-20260802.json (100%) rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-ubuntu-2404.json (100%) rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-ubuntu-2604.json (100%) rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-windows-2019.json (100%) rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-windows-2022.json (100%) rename superv/{supervisor => internal/testfixtures}/testdata/hostinfo/hostinfo-windows-2025.json (100%) create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-almalinux-10.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-almalinux-8.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-almalinux-9.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-alpine-3.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-amazonlinux-2.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-amazonlinux-2023.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-arch-2026-08.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-debian-12.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-debian-13.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-fedora-44.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-fedora-container-44.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-opensuse-tumbleweed-2026-08.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-oraclelinux-10.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-oraclelinux-8.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-oraclelinux-9.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-redhat-8.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-redhat-9.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-rockylinux-8.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-rockylinux-9.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-ubuntu-24.04.txt create mode 100644 superv/internal/testfixtures/testdata/os-release/os-release-ubuntu-26.04.txt create mode 100644 superv/internal/testsysinfo/os_release.go create mode 100644 superv/sysinfo/os_release.go create mode 100644 superv/sysinfo/os_release_test.go diff --git a/superv/internal/testfixtures/fixtures.go b/superv/internal/testfixtures/fixtures.go new file mode 100644 index 00000000..421227d6 --- /dev/null +++ b/superv/internal/testfixtures/fixtures.go @@ -0,0 +1,14 @@ +// Package testfixtures provides fixtures as embed.FS instances for reusability from different packages. +package testfixtures + +import ( + "embed" +) + +// OsReleaseFS provides /etc/os-release file fixtures. +// +//go:embed testdata/os-release/*.txt +var OsReleaseFS embed.FS + +//go:embed testdata/hostinfo/*.json +var HostInfoFS embed.FS diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-almalinux-10.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-almalinux-10.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-almalinux-10.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-almalinux-10.json diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-almalinux-8.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-almalinux-8.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-almalinux-8.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-almalinux-8.json diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-almalinux-9.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-almalinux-9.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-almalinux-9.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-almalinux-9.json diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-alpine-3.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-alpine-3.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-alpine-3.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-alpine-3.json diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-amazon-2023.12.20260724.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-amazon-2023.12.20260724.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-amazon-2023.12.20260724.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-amazon-2023.12.20260724.json diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-arch-20260726.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-arch-20260726.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-arch-20260726.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-arch-20260726.json diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-debian-13.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-debian-13.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-debian-13.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-debian-13.json diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-macos-26.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-macos-26.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-macos-26.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-macos-26.json diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-opensuse-tumbleweed-20260802.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-opensuse-tumbleweed-20260802.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-opensuse-tumbleweed-20260802.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-opensuse-tumbleweed-20260802.json diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2404.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-ubuntu-2404.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2404.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-ubuntu-2404.json diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2604.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-ubuntu-2604.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-ubuntu-2604.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-ubuntu-2604.json diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-windows-2019.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-windows-2019.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-windows-2019.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-windows-2019.json diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-windows-2022.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-windows-2022.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-windows-2022.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-windows-2022.json diff --git a/superv/supervisor/testdata/hostinfo/hostinfo-windows-2025.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-windows-2025.json similarity index 100% rename from superv/supervisor/testdata/hostinfo/hostinfo-windows-2025.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-windows-2025.json diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-almalinux-10.txt b/superv/internal/testfixtures/testdata/os-release/os-release-almalinux-10.txt new file mode 100644 index 00000000..7fc3c1d0 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-almalinux-10.txt @@ -0,0 +1,22 @@ +NAME="AlmaLinux" +VERSION="10.2 (Lavender Lion)" +RELEASE_TYPE=stable +ID="almalinux" +ID_LIKE="rhel centos fedora" +VERSION_ID="10.2" +PLATFORM_ID="platform:el10" +PRETTY_NAME="AlmaLinux 10.2 (Lavender Lion)" +ANSI_COLOR="0;34" +LOGO="fedora-logo-icon" +CPE_NAME="cpe:/o:almalinux:almalinux:10.2" +HOME_URL="https://almalinux.org/" +DOCUMENTATION_URL="https://wiki.almalinux.org/" +VENDOR_NAME="AlmaLinux" +VENDOR_URL="https://almalinux.org/" +BUG_REPORT_URL="https://bugs.almalinux.org/" + +ALMALINUX_MANTISBT_PROJECT="AlmaLinux-10" +ALMALINUX_MANTISBT_PROJECT_VERSION="10.2" +REDHAT_SUPPORT_PRODUCT="AlmaLinux" +REDHAT_SUPPORT_PRODUCT_VERSION="10.2" +SUPPORT_END=2035-06-01 diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-almalinux-8.txt b/superv/internal/testfixtures/testdata/os-release/os-release-almalinux-8.txt new file mode 100644 index 00000000..6b594068 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-almalinux-8.txt @@ -0,0 +1,19 @@ +NAME="AlmaLinux" +VERSION="8.10 (Cerulean Leopard)" +ID="almalinux" +ID_LIKE="rhel centos fedora" +VERSION_ID="8.10" +PLATFORM_ID="platform:el8" +PRETTY_NAME="AlmaLinux 8.10 (Cerulean Leopard)" +ANSI_COLOR="0;34" +LOGO="fedora-logo-icon" +CPE_NAME="cpe:/o:almalinux:almalinux:8::baseos" +HOME_URL="https://almalinux.org/" +DOCUMENTATION_URL="https://wiki.almalinux.org/" +BUG_REPORT_URL="https://bugs.almalinux.org/" + +ALMALINUX_MANTISBT_PROJECT="AlmaLinux-8" +ALMALINUX_MANTISBT_PROJECT_VERSION="8.10" +REDHAT_SUPPORT_PRODUCT="AlmaLinux" +REDHAT_SUPPORT_PRODUCT_VERSION="8.10" +SUPPORT_END=2029-06-01 diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-almalinux-9.txt b/superv/internal/testfixtures/testdata/os-release/os-release-almalinux-9.txt new file mode 100644 index 00000000..f5914a97 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-almalinux-9.txt @@ -0,0 +1,19 @@ +NAME="AlmaLinux" +VERSION="9.7 (Moss Jungle Cat)" +ID="almalinux" +ID_LIKE="rhel centos fedora" +VERSION_ID="9.7" +PLATFORM_ID="platform:el9" +PRETTY_NAME="AlmaLinux 9.7 (Moss Jungle Cat)" +ANSI_COLOR="0;34" +LOGO="fedora-logo-icon" +CPE_NAME="cpe:/o:almalinux:almalinux:9::baseos" +HOME_URL="https://almalinux.org/" +DOCUMENTATION_URL="https://wiki.almalinux.org/" +BUG_REPORT_URL="https://bugs.almalinux.org/" + +ALMALINUX_MANTISBT_PROJECT="AlmaLinux-9" +ALMALINUX_MANTISBT_PROJECT_VERSION="9.7" +REDHAT_SUPPORT_PRODUCT="AlmaLinux" +REDHAT_SUPPORT_PRODUCT_VERSION="9.7" +SUPPORT_END=2032-06-01 diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-alpine-3.txt b/superv/internal/testfixtures/testdata/os-release/os-release-alpine-3.txt new file mode 100644 index 00000000..76489be1 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-alpine-3.txt @@ -0,0 +1,6 @@ +NAME="Alpine Linux" +ID=alpine +VERSION_ID=3.23.3 +PRETTY_NAME="Alpine Linux v3.23" +HOME_URL="https://alpinelinux.org/" +BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues" diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-amazonlinux-2.txt b/superv/internal/testfixtures/testdata/os-release/os-release-amazonlinux-2.txt new file mode 100644 index 00000000..e97399d9 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-amazonlinux-2.txt @@ -0,0 +1,10 @@ +NAME="Amazon Linux" +VERSION="2" +ID="amzn" +ID_LIKE="centos rhel fedora" +VERSION_ID="2" +PRETTY_NAME="Amazon Linux 2" +ANSI_COLOR="0;33" +CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2" +HOME_URL="https://amazonlinux.com/" +SUPPORT_END="2026-06-30" diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-amazonlinux-2023.txt b/superv/internal/testfixtures/testdata/os-release/os-release-amazonlinux-2023.txt new file mode 100644 index 00000000..d76ac4ca --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-amazonlinux-2023.txt @@ -0,0 +1,16 @@ +NAME="Amazon Linux" +VERSION="2023" +ID="amzn" +ID_LIKE="fedora" +VERSION_ID="2023" +PLATFORM_ID="platform:al2023" +PRETTY_NAME="Amazon Linux 2023.12.20260724" +ANSI_COLOR="0;33" +CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2023" +HOME_URL="https://aws.amazon.com/linux/amazon-linux-2023/" +DOCUMENTATION_URL="https://docs.aws.amazon.com/linux/" +SUPPORT_URL="https://aws.amazon.com/premiumsupport/" +BUG_REPORT_URL="https://github.com/amazonlinux/amazon-linux-2023" +VENDOR_NAME="AWS" +VENDOR_URL="https://aws.amazon.com/" +SUPPORT_END="2029-06-30" diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-arch-2026-08.txt b/superv/internal/testfixtures/testdata/os-release/os-release-arch-2026-08.txt new file mode 100644 index 00000000..2c59bef4 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-arch-2026-08.txt @@ -0,0 +1,12 @@ +NAME="Arch Linux" +PRETTY_NAME="Arch Linux" +ID=arch +BUILD_ID=rolling +VERSION_ID=20260726.0.562117 +ANSI_COLOR="38;2;23;147;209" +HOME_URL="https://archlinux.org/" +DOCUMENTATION_URL="https://wiki.archlinux.org/" +SUPPORT_URL="https://bbs.archlinux.org/" +BUG_REPORT_URL="https://gitlab.archlinux.org/groups/archlinux/-/issues" +PRIVACY_POLICY_URL="https://terms.archlinux.org/docs/privacy-policy/" +LOGO=archlinux-logo diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-debian-12.txt b/superv/internal/testfixtures/testdata/os-release/os-release-debian-12.txt new file mode 100644 index 00000000..33208621 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-debian-12.txt @@ -0,0 +1,9 @@ +PRETTY_NAME="Debian GNU/Linux 12 (bookworm)" +NAME="Debian GNU/Linux" +VERSION_ID="12" +VERSION="12 (bookworm)" +VERSION_CODENAME=bookworm +ID=debian +HOME_URL="https://www.debian.org/" +SUPPORT_URL="https://www.debian.org/support" +BUG_REPORT_URL="https://bugs.debian.org/" diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-debian-13.txt b/superv/internal/testfixtures/testdata/os-release/os-release-debian-13.txt new file mode 100644 index 00000000..2c83cc99 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-debian-13.txt @@ -0,0 +1,10 @@ +PRETTY_NAME="Debian GNU/Linux 13 (trixie)" +NAME="Debian GNU/Linux" +VERSION_ID="13" +VERSION="13 (trixie)" +VERSION_CODENAME=trixie +DEBIAN_VERSION_FULL=13.2 +ID=debian +HOME_URL="https://www.debian.org/" +SUPPORT_URL="https://www.debian.org/support" +BUG_REPORT_URL="https://bugs.debian.org/" diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-fedora-44.txt b/superv/internal/testfixtures/testdata/os-release/os-release-fedora-44.txt new file mode 100644 index 00000000..a4356331 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-fedora-44.txt @@ -0,0 +1,21 @@ +NAME="Fedora Linux" +VERSION="44 (Server Edition)" +RELEASE_TYPE=stable +ID=fedora +VERSION_ID=44 +VERSION_CODENAME="" +PRETTY_NAME="Fedora Linux 44 (Server Edition)" +ANSI_COLOR="0;38;2;60;110;180" +LOGO=fedora-logo-icon +CPE_NAME="cpe:/o:fedoraproject:fedora:44" +HOME_URL="https://fedoraproject.org/" +DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f44/" +SUPPORT_URL="https://ask.fedoraproject.org/" +BUG_REPORT_URL="https://bugzilla.redhat.com/" +REDHAT_BUGZILLA_PRODUCT="Fedora" +REDHAT_BUGZILLA_PRODUCT_VERSION=44 +REDHAT_SUPPORT_PRODUCT="Fedora" +REDHAT_SUPPORT_PRODUCT_VERSION=44 +SUPPORT_END=2027-05-19 +VARIANT="Server Edition" +VARIANT_ID=server diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-fedora-container-44.txt b/superv/internal/testfixtures/testdata/os-release/os-release-fedora-container-44.txt new file mode 100644 index 00000000..f9bcc3ea --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-fedora-container-44.txt @@ -0,0 +1,22 @@ +NAME="Fedora Linux" +VERSION="44 (Container Image)" +RELEASE_TYPE=stable +ID=fedora +VERSION_ID=44 +VERSION_CODENAME="" +PRETTY_NAME="Fedora Linux 44 (Container Image)" +ANSI_COLOR="0;38;2;60;110;180" +LOGO=fedora-logo-icon +CPE_NAME="cpe:/o:fedoraproject:fedora:44" +DEFAULT_HOSTNAME="fedora" +HOME_URL="https://fedoraproject.org/" +DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f44/" +SUPPORT_URL="https://ask.fedoraproject.org/" +BUG_REPORT_URL="https://bugzilla.redhat.com/" +REDHAT_BUGZILLA_PRODUCT="Fedora" +REDHAT_BUGZILLA_PRODUCT_VERSION=44 +REDHAT_SUPPORT_PRODUCT="Fedora" +REDHAT_SUPPORT_PRODUCT_VERSION=44 +SUPPORT_END=2027-05-19 +VARIANT="Container Image" +VARIANT_ID=container diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-opensuse-tumbleweed-2026-08.txt b/superv/internal/testfixtures/testdata/os-release/os-release-opensuse-tumbleweed-2026-08.txt new file mode 100644 index 00000000..f88f3111 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-opensuse-tumbleweed-2026-08.txt @@ -0,0 +1,16 @@ +NAME="openSUSE Tumbleweed" +# VERSION="20260802" +ID="opensuse-tumbleweed" +ID_LIKE="opensuse suse" +VERSION_ID="20260802" +PRETTY_NAME="openSUSE Tumbleweed" +ANSI_COLOR="0;32" +# CPE 2.3 format, boo#1217921 +CPE_NAME="cpe:2.3:o:opensuse:tumbleweed:20260802:*:*:*:*:*:*:*" +#CPE 2.2 format +#CPE_NAME="cpe:/o:opensuse:tumbleweed:20260802" +BUG_REPORT_URL="https://bugzilla.opensuse.org" +SUPPORT_URL="https://bugs.opensuse.org" +HOME_URL="https://www.opensuse.org" +DOCUMENTATION_URL="https://en.opensuse.org/Portal:Tumbleweed" +LOGO="distributor-logo-Tumbleweed" diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-oraclelinux-10.txt b/superv/internal/testfixtures/testdata/os-release/os-release-oraclelinux-10.txt new file mode 100644 index 00000000..2a57f448 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-oraclelinux-10.txt @@ -0,0 +1,18 @@ +NAME="Oracle Linux Server" +VERSION="10.2" +ID="ol" +ID_LIKE="fedora" +VARIANT="Server" +VARIANT_ID="server" +VERSION_ID="10.2" +PLATFORM_ID="platform:el10" +PRETTY_NAME="Oracle Linux Server 10.2" +ANSI_COLOR="0;31" +CPE_NAME="cpe:/o:oracle:linux:10:2:server" +HOME_URL="https://linux.oracle.com/" +BUG_REPORT_URL="https://github.com/oracle/oracle-linux" + +ORACLE_BUGZILLA_PRODUCT="Oracle Linux 10" +ORACLE_BUGZILLA_PRODUCT_VERSION=10.2 +ORACLE_SUPPORT_PRODUCT="Oracle Linux" +ORACLE_SUPPORT_PRODUCT_VERSION=10.2 diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-oraclelinux-8.txt b/superv/internal/testfixtures/testdata/os-release/os-release-oraclelinux-8.txt new file mode 100644 index 00000000..81c29c07 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-oraclelinux-8.txt @@ -0,0 +1,18 @@ +NAME="Oracle Linux Server" +VERSION="8.10" +ID="ol" +ID_LIKE="fedora" +VARIANT="Server" +VARIANT_ID="server" +VERSION_ID="8.10" +PLATFORM_ID="platform:el8" +PRETTY_NAME="Oracle Linux Server 8.10" +ANSI_COLOR="0;31" +CPE_NAME="cpe:/o:oracle:linux:8:10:server" +HOME_URL="https://linux.oracle.com/" +BUG_REPORT_URL="https://github.com/oracle/oracle-linux" + +ORACLE_BUGZILLA_PRODUCT="Oracle Linux 8" +ORACLE_BUGZILLA_PRODUCT_VERSION=8.10 +ORACLE_SUPPORT_PRODUCT="Oracle Linux" +ORACLE_SUPPORT_PRODUCT_VERSION=8.10 diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-oraclelinux-9.txt b/superv/internal/testfixtures/testdata/os-release/os-release-oraclelinux-9.txt new file mode 100644 index 00000000..3fa60342 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-oraclelinux-9.txt @@ -0,0 +1,18 @@ +NAME="Oracle Linux Server" +VERSION="9.8" +ID="ol" +ID_LIKE="fedora" +VARIANT="Server" +VARIANT_ID="server" +VERSION_ID="9.8" +PLATFORM_ID="platform:el9" +PRETTY_NAME="Oracle Linux Server 9.8" +ANSI_COLOR="0;31" +CPE_NAME="cpe:/o:oracle:linux:9:8:server" +HOME_URL="https://linux.oracle.com/" +BUG_REPORT_URL="https://github.com/oracle/oracle-linux" + +ORACLE_BUGZILLA_PRODUCT="Oracle Linux 9" +ORACLE_BUGZILLA_PRODUCT_VERSION=9.8 +ORACLE_SUPPORT_PRODUCT="Oracle Linux" +ORACLE_SUPPORT_PRODUCT_VERSION=9.8 diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-redhat-8.txt b/superv/internal/testfixtures/testdata/os-release/os-release-redhat-8.txt new file mode 100644 index 00000000..346e2966 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-redhat-8.txt @@ -0,0 +1,17 @@ +NAME="Red Hat Enterprise Linux" +VERSION="8.5 (Ootpa)" +ID="rhel" +ID_LIKE="fedora" +VERSION_ID="8.5" +PLATFORM_ID="platform:el8" +PRETTY_NAME="Red Hat Enterprise Linux 8.5 (Ootpa)" +ANSI_COLOR="0;31" +CPE_NAME="cpe:/o:redhat:enterprise_linux:8::baseos" +HOME_URL="https://www.redhat.com/" +DOCUMENTATION_URL="https://access.redhat.com/documentation/red_hat_enterprise_linux/8/" +BUG_REPORT_URL="https://bugzilla.redhat.com/" + +REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 8" +REDHAT_BUGZILLA_PRODUCT_VERSION=8.5 +REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux" +REDHAT_SUPPORT_PRODUCT_VERSION="8.5" diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-redhat-9.txt b/superv/internal/testfixtures/testdata/os-release/os-release-redhat-9.txt new file mode 100644 index 00000000..66458c56 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-redhat-9.txt @@ -0,0 +1,18 @@ +NAME="Red Hat Enterprise Linux" +VERSION="9.4 (Plow)" +ID="rhel" +ID_LIKE="fedora" +VERSION_ID="9.4" +PLATFORM_ID="platform:el9" +PRETTY_NAME="Red Hat Enterprise Linux 9.4 (Plow)" +ANSI_COLOR="0;31" +LOGO="fedora-logo-icon" +CPE_NAME="cpe:/o:redhat:enterprise_linux:9::baseos" +HOME_URL="https://www.redhat.com/" +DOCUMENTATION_URL="https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9" +BUG_REPORT_URL="https://issues.redhat.com/" + +REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 9" +REDHAT_BUGZILLA_PRODUCT_VERSION=9.4 +REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux" +REDHAT_SUPPORT_PRODUCT_VERSION="9.4" diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-rockylinux-8.txt b/superv/internal/testfixtures/testdata/os-release/os-release-rockylinux-8.txt new file mode 100644 index 00000000..4782b1e5 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-rockylinux-8.txt @@ -0,0 +1,17 @@ +NAME="Rocky Linux" +VERSION="8.9 (Green Obsidian)" +ID="rocky" +ID_LIKE="rhel centos fedora" +VERSION_ID="8.9" +PLATFORM_ID="platform:el8" +PRETTY_NAME="Rocky Linux 8.9 (Green Obsidian)" +ANSI_COLOR="0;32" +LOGO="fedora-logo-icon" +CPE_NAME="cpe:/o:rocky:rocky:8:GA" +HOME_URL="https://rockylinux.org/" +BUG_REPORT_URL="https://bugs.rockylinux.org/" +SUPPORT_END="2029-05-31" +ROCKY_SUPPORT_PRODUCT="Rocky-Linux-8" +ROCKY_SUPPORT_PRODUCT_VERSION="8.9" +REDHAT_SUPPORT_PRODUCT="Rocky Linux" +REDHAT_SUPPORT_PRODUCT_VERSION="8.9" diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-rockylinux-9.txt b/superv/internal/testfixtures/testdata/os-release/os-release-rockylinux-9.txt new file mode 100644 index 00000000..615382df --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-rockylinux-9.txt @@ -0,0 +1,17 @@ +NAME="Rocky Linux" +VERSION="9.3 (Blue Onyx)" +ID="rocky" +ID_LIKE="rhel centos fedora" +VERSION_ID="9.3" +PLATFORM_ID="platform:el9" +PRETTY_NAME="Rocky Linux 9.3 (Blue Onyx)" +ANSI_COLOR="0;32" +LOGO="fedora-logo-icon" +CPE_NAME="cpe:/o:rocky:rocky:9::baseos" +HOME_URL="https://rockylinux.org/" +BUG_REPORT_URL="https://bugs.rockylinux.org/" +SUPPORT_END="2032-05-31" +ROCKY_SUPPORT_PRODUCT="Rocky-Linux-9" +ROCKY_SUPPORT_PRODUCT_VERSION="9.3" +REDHAT_SUPPORT_PRODUCT="Rocky Linux" +REDHAT_SUPPORT_PRODUCT_VERSION="9.3" diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-ubuntu-24.04.txt b/superv/internal/testfixtures/testdata/os-release/os-release-ubuntu-24.04.txt new file mode 100644 index 00000000..18cf4770 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-ubuntu-24.04.txt @@ -0,0 +1,13 @@ +PRETTY_NAME="Ubuntu 24.04.1 LTS" +NAME="Ubuntu" +VERSION_ID="24.04" +VERSION="24.04.1 LTS (Noble Numbat)" +VERSION_CODENAME=noble +ID=ubuntu +ID_LIKE=debian +HOME_URL="https://www.ubuntu.com/" +SUPPORT_URL="https://help.ubuntu.com/" +BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" +PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" +UBUNTU_CODENAME=noble +LOGO=ubuntu-logo diff --git a/superv/internal/testfixtures/testdata/os-release/os-release-ubuntu-26.04.txt b/superv/internal/testfixtures/testdata/os-release/os-release-ubuntu-26.04.txt new file mode 100644 index 00000000..e89ba547 --- /dev/null +++ b/superv/internal/testfixtures/testdata/os-release/os-release-ubuntu-26.04.txt @@ -0,0 +1,13 @@ +PRETTY_NAME="Ubuntu 26.04 LTS" +NAME="Ubuntu" +VERSION_ID="26.04" +VERSION="26.04 LTS (Resolute Raccoon)" +VERSION_CODENAME=resolute +ID=ubuntu +ID_LIKE=debian +HOME_URL="https://www.ubuntu.com/" +SUPPORT_URL="https://help.ubuntu.com/" +BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" +PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" +UBUNTU_CODENAME=resolute +LOGO=ubuntu-logo diff --git a/superv/internal/testsysinfo/os_release.go b/superv/internal/testsysinfo/os_release.go new file mode 100644 index 00000000..c3273921 --- /dev/null +++ b/superv/internal/testsysinfo/os_release.go @@ -0,0 +1,32 @@ +package testsysinfo + +import ( + "path/filepath" + "strings" + "testing" + + "github.com/Graylog2/collector/superv/internal/testfixtures" + "github.com/Graylog2/collector/superv/sysinfo" + "github.com/stretchr/testify/require" +) + +func GetOSReleaseSupplier(t *testing.T, name string) func() (sysinfo.OSRelease, error) { + t.Helper() + + // os-release files only exist on Linux + if name == "" || strings.Contains(name, "macos") || strings.Contains(name, "windows") { + return func() (sysinfo.OSRelease, error) { + return sysinfo.OSRelease{}, nil + } + } + + file, err := testfixtures.OsReleaseFS.Open(filepath.Join("testdata", "os-release", "os-release-"+name+".txt")) + require.NoError(t, err) + + osRelease, err := sysinfo.ParseOSRelease(file) + require.NoError(t, err) + + return func() (sysinfo.OSRelease, error) { + return osRelease, nil + } +} diff --git a/superv/supervisor/opamp_agent_description.go b/superv/supervisor/opamp_agent_description.go index 8b8f80af..5d0d8426 100644 --- a/superv/supervisor/opamp_agent_description.go +++ b/superv/supervisor/opamp_agent_description.go @@ -18,19 +18,18 @@ package supervisor import ( - "cmp" + "fmt" "os" "runtime" "strings" + "github.com/Graylog2/collector/superv/sysinfo" "github.com/Graylog2/collector/superv/version" "github.com/open-telemetry/opamp-go/protobufs" "github.com/shirou/gopsutil/v4/host" "go.opentelemetry.io/otel/attribute" semconv "go.opentelemetry.io/otel/semconv/v1.41.0" "go.uber.org/zap" - "golang.org/x/text/cases" - "golang.org/x/text/language" ) // createAgentDescription creates the initial agent description for OpAMP. @@ -55,9 +54,8 @@ func (s *Supervisor) nonIdentifyingAttributes(hostname string) []*protobufs.KeyV attributeStringKv(semconv.ServiceVersionKey, version.Version()), } - info, err := host.Info() - if err == nil { - attrs = append(attrs, attributeStringKv(semconv.OSDescriptionKey, getOSDescription(info))) + if description, err := getOSDescription(runtime.GOOS, host.Info, sysinfo.GetOSRelease); err == nil { + attrs = append(attrs, attributeStringKv(semconv.OSDescriptionKey, description)) } else { s.logger.Warn("Failed to retrieve host information", zap.Error(err)) } @@ -69,43 +67,27 @@ func (s *Supervisor) nonIdentifyingAttributes(hostname string) []*protobufs.KeyV return attrs } -// linuxPlatformNames maps gopsutil platform identifiers to product names -// where simple title-casing gets the spelling wrong. Identifiers come from -// distro release files or the ID field in /etc/os-release. -var linuxPlatformNames = map[string]string{ - "almalinux": "AlmaLinux", - "amazon": "Amazon Linux", - "amzn": "Amazon Linux", - "centos": "CentOS", - "cloudlinux": "CloudLinux", - "linuxmint": "Linux Mint", - "nixos": "NixOS", - "ol": "Oracle Linux", - "opensuse": "openSUSE", - "opensuse-leap": "openSUSE Leap", - "opensuse-tumbleweed": "openSUSE Tumbleweed", - "oracle": "Oracle Linux", - "redhat": "Red Hat Enterprise Linux", - "rhel": "Red Hat Enterprise Linux", - "rocky": "Rocky Linux", - "scientific": "Scientific Linux", - "sled": "SUSE Linux Enterprise Desktop", - "sles": "SUSE Linux Enterprise Server", - "suse": "SUSE", -} - // getOSDescription builds an "os.description" value for each platform. -func getOSDescription(info *host.InfoStat) string { - switch info.OS { - case "darwin": - return strings.TrimSpace("macOS " + info.PlatformVersion) - case "linux": - name := cmp.Or(linuxPlatformNames[info.Platform], cases.Title(language.English).String(info.Platform)) - return strings.TrimSpace(name + " " + info.PlatformVersion) - case "windows": - return strings.TrimSpace(info.Platform + " " + info.PlatformVersion) - default: - return "Unknown " + info.OS +func getOSDescription(os string, infoSupplier func() (*host.InfoStat, error), osReleaseSupplier func() (sysinfo.OSRelease, error)) (string, error) { + // On Linux we use data from the /etc/os-release file to get properly formatted distribution names. + if os == "linux" { + osRelease, err := osReleaseSupplier() + if err != nil { + return "", fmt.Errorf("couldn't read os-release info: %w", err) + } + return strings.TrimSpace(osRelease.Name + " " + osRelease.VersionID), nil + } + if info, err := infoSupplier(); err == nil { + switch os { + case "darwin": + return strings.TrimSpace("macOS " + info.PlatformVersion), nil + case "windows": + return strings.TrimSpace(info.Platform + " " + info.PlatformVersion), nil + default: + return "Unknown " + info.OS, nil + } + } else { + return "", fmt.Errorf("couldn't read host info: %w", err) } } diff --git a/superv/supervisor/opamp_agent_description_test.go b/superv/supervisor/opamp_agent_description_test.go index 78d1f1b4..ecf78f6f 100644 --- a/superv/supervisor/opamp_agent_description_test.go +++ b/superv/supervisor/opamp_agent_description_test.go @@ -19,10 +19,11 @@ package supervisor import ( "encoding/json" - "os" "path/filepath" "testing" + "github.com/Graylog2/collector/superv/internal/testfixtures" + "github.com/Graylog2/collector/superv/internal/testsysinfo" "github.com/shirou/gopsutil/v4/host" "github.com/stretchr/testify/require" ) @@ -65,7 +66,7 @@ func TestSupervisor_NonIdentifyingAttributes_WithoutCollectorVersion(t *testing. func loadHostInfo(t *testing.T, name string) *host.InfoStat { t.Helper() - data, err := os.ReadFile(filepath.Join("testdata", "hostinfo", "hostinfo-"+name+".json")) + data, err := testfixtures.HostInfoFS.ReadFile(filepath.Join("testdata", "hostinfo", "hostinfo-"+name+".json")) require.NoError(t, err) info := &host.InfoStat{} @@ -76,78 +77,46 @@ func loadHostInfo(t *testing.T, name string) *host.InfoStat { func TestGetOSDescription(t *testing.T) { tests := []struct { + os string fixture string want string }{ - {"almalinux-8", "AlmaLinux 8.10"}, - {"almalinux-9", "AlmaLinux 9.7"}, - {"almalinux-10", "AlmaLinux 10.1"}, - {"alpine-3", "Alpine 3.23.3"}, - {"amazon-2023.12.20260724", "Amazon Linux 2023.12.20260724"}, - {"arch-20260726", "Arch 20260726.0.562117"}, - {"debian-13", "Debian 13.3"}, - {"opensuse-tumbleweed-20260802", "openSUSE Tumbleweed 20260802"}, - {"linux-ubuntu-2404", "Ubuntu 24.04"}, - {"linux-ubuntu-2604", "Ubuntu 26.04"}, - {"ubuntu-2404", "Ubuntu 24.04"}, - {"ubuntu-2604", "Ubuntu 26.04"}, - {"macos-26", "macOS 26.5.2"}, - {"windows-2019", "Microsoft Windows Server 2019 Datacenter"}, - {"windows-2022", "Microsoft Windows Server 2022 Datacenter 21H2"}, - {"windows-2025", "Microsoft Windows Server 2025 Datacenter 24H2"}, + {"linux", "almalinux-8", "AlmaLinux 8.10"}, + {"linux", "almalinux-9", "AlmaLinux 9.7"}, + {"linux", "almalinux-10", "AlmaLinux 10.2"}, + {"linux", "alpine-3", "Alpine Linux 3.23.3"}, + {"linux", "amazonlinux-2023", "Amazon Linux 2023"}, + {"linux", "arch-2026-08", "Arch Linux 20260726.0.562117"}, + {"linux", "debian-13", "Debian GNU/Linux 13"}, + {"linux", "opensuse-tumbleweed-2026-08", "openSUSE Tumbleweed 20260802"}, + {"linux", "ubuntu-24.04", "Ubuntu 24.04"}, + {"linux", "ubuntu-26.04", "Ubuntu 26.04"}, + {"darwin", "macos-26", "macOS 26.5.2"}, + {"windows", "windows-2019", "Microsoft Windows Server 2019 Datacenter"}, + {"windows", "windows-2022", "Microsoft Windows Server 2022 Datacenter 21H2"}, + {"windows", "windows-2025", "Microsoft Windows Server 2025 Datacenter 24H2"}, } for _, tc := range tests { t.Run(tc.fixture, func(t *testing.T) { - info := loadHostInfo(t, tc.fixture) - require.Equal(t, tc.want, getOSDescription(info)) - }) - } -} - -// TestGetOSDescription_LinuxPlatformNames covers platform identifiers that -// gopsutil can report but for which we have no captured host info, ensuring -// their product names are spelled properly. -func TestGetOSDescription_LinuxPlatformNames(t *testing.T) { - tests := []struct { - platform string - version string - want string - }{ - {"redhat", "9.4", "Red Hat Enterprise Linux 9.4"}, - {"rhel", "9.4", "Red Hat Enterprise Linux 9.4"}, - {"centos", "9", "CentOS 9"}, - {"rocky", "9.4", "Rocky Linux 9.4"}, - {"oracle", "9.4", "Oracle Linux 9.4"}, - {"ol", "9.4", "Oracle Linux 9.4"}, - {"amazon", "2023", "Amazon Linux 2023"}, - {"amzn", "2023", "Amazon Linux 2023"}, - {"sles", "15.6", "SUSE Linux Enterprise Server 15.6"}, - {"sled", "15.6", "SUSE Linux Enterprise Desktop 15.6"}, - {"opensuse-leap", "15.6", "openSUSE Leap 15.6"}, - {"opensuse-tumbleweed", "20260802", "openSUSE Tumbleweed 20260802"}, - {"opensuse", "13.2", "openSUSE 13.2"}, - {"suse", "11.4", "SUSE 11.4"}, - {"linuxmint", "22", "Linux Mint 22"}, - {"nixos", "24.11", "NixOS 24.11"}, - {"cloudlinux", "9.4", "CloudLinux 9.4"}, - {"scientific", "7.9", "Scientific Linux 7.9"}, - } - - for _, tc := range tests { - t.Run(tc.platform, func(t *testing.T) { - info := &host.InfoStat{OS: "linux", Platform: tc.platform, PlatformVersion: tc.version} - require.Equal(t, tc.want, getOSDescription(info)) + description, err := getOSDescription(tc.os, func() (*host.InfoStat, error) { + info := loadHostInfo(t, tc.fixture) + return info, nil + }, testsysinfo.GetOSReleaseSupplier(t, tc.fixture)) + require.NoError(t, err) + require.Equal(t, tc.want, description) }) } } func TestGetOSDescription_UnknownOS(t *testing.T) { - info := &host.InfoStat{ - OS: "freebsd", - Platform: "freebsd", - PlatformVersion: "14.1", - } - - require.Equal(t, "Unknown freebsd", getOSDescription(info)) + description, err := getOSDescription("freebsd", func() (*host.InfoStat, error) { + return &host.InfoStat{ + OS: "freebsd", + Platform: "freebsd", + PlatformVersion: "14.1", + }, nil + }, testsysinfo.GetOSReleaseSupplier(t, "")) + require.NoError(t, err) + require.Equal(t, "Unknown freebsd", description) } diff --git a/superv/sysinfo/os_release.go b/superv/sysinfo/os_release.go new file mode 100644 index 00000000..d5843cb2 --- /dev/null +++ b/superv/sysinfo/os_release.go @@ -0,0 +1,106 @@ +// Copyright (C) 2026 Graylog, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the Server Side Public License, version 1, +// as published by MongoDB, Inc. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// Server Side Public License for more details. +// +// You should have received a copy of the Server Side Public License +// along with this program. If not, see +// . +// +// SPDX-License-Identifier: SSPL-1.0 + +package sysinfo + +import ( + "bufio" + "fmt" + "io" + "os" + "strings" +) + +// OSRelease holds a subset of the fields defined in the os-release. +// specification: https://www.freedesktop.org/software/systemd/man/latest/os-release.html +type OSRelease struct { + Name string // NAME + Version string // VERSION + ID string // ID + IDLike string // ID_LIKE (space-separated list) + VersionID string // VERSION_ID + PrettyName string // PRETTY_NAME +} + +// GetOSRelease reads the os-release information of the current host, +// preferring /etc/os-release over the /usr/lib/os-release fallback. +func GetOSRelease() (OSRelease, error) { + release, err := ReadOSRelease("/etc/os-release") + if os.IsNotExist(err) { + return ReadOSRelease("/usr/lib/os-release") + } + return release, err +} + +// ReadOSRelease reads and parses the os-release file at the given path. +func ReadOSRelease(path string) (OSRelease, error) { + f, err := os.Open(path) + if err != nil { + return OSRelease{}, fmt.Errorf("opening os-release file: %w", err) + } + defer f.Close() + + return ParseOSRelease(f) +} + +// ParseOSRelease parses os-release formatted content. Comments, blank +// lines, malformed lines, and unknown fields are ignored. +func ParseOSRelease(r io.Reader) (OSRelease, error) { + var release OSRelease + + scanner := bufio.NewScanner(r) + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" || strings.HasPrefix(line, "#") { + continue + } + + key, value, found := strings.Cut(line, "=") + if !found { + continue + } + value = trimQuotes(value) + + switch key { + case "NAME": + release.Name = value + case "VERSION": + release.Version = value + case "ID": + release.ID = value + case "ID_LIKE": + release.IDLike = value + case "VERSION_ID": + release.VersionID = value + case "PRETTY_NAME": + release.PrettyName = value + } + } + if err := scanner.Err(); err != nil { + return OSRelease{}, fmt.Errorf("reading os-release content: %w", err) + } + + return release, nil +} + +// trimQuotes removes matching surrounding double or single quotes. +func trimQuotes(s string) string { + if len(s) >= 2 && (s[0] == '"' || s[0] == '\'') && s[len(s)-1] == s[0] { + return s[1 : len(s)-1] + } + return s +} diff --git a/superv/sysinfo/os_release_test.go b/superv/sysinfo/os_release_test.go new file mode 100644 index 00000000..6a461848 --- /dev/null +++ b/superv/sysinfo/os_release_test.go @@ -0,0 +1,362 @@ +// Copyright (C) 2026 Graylog, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the Server Side Public License, version 1, +// as published by MongoDB, Inc. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// Server Side Public License for more details. +// +// You should have received a copy of the Server Side Public License +// along with this program. If not, see +// . +// +// SPDX-License-Identifier: SSPL-1.0 + +package sysinfo + +import ( + "path/filepath" + "strings" + "testing" + + "github.com/Graylog2/collector/superv/internal/testfixtures" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestParseOSRelease(t *testing.T) { + tests := []struct { + name string + input string + want OSRelease + }{ + { + name: "unquoted values", + input: strings.Join([]string{ + `ID=ubuntu`, + `ID_LIKE=debian`, + `VERSION_ID=24.04`, + }, "\n"), + want: OSRelease{ID: "ubuntu", IDLike: "debian", VersionID: "24.04"}, + }, + { + name: "quoted values", + input: strings.Join([]string{ + `NAME="Red Hat Enterprise Linux"`, + `VERSION="9.4 (Plow)"`, + `ID="rhel"`, + }, "\n"), + want: OSRelease{Name: "Red Hat Enterprise Linux", Version: "9.4 (Plow)", ID: "rhel"}, + }, + { + name: "single-quoted values", + input: `NAME='Test Linux'`, + want: OSRelease{Name: "Test Linux"}, + }, + { + name: "comments and blank lines are skipped", + input: strings.Join([]string{ + `# VERSION="20260802"`, + ``, + `ID="opensuse-tumbleweed"`, + }, "\n"), + want: OSRelease{ID: "opensuse-tumbleweed"}, + }, + { + name: "value containing equals sign", + input: `PRETTY_NAME="Test a=b"`, + want: OSRelease{PrettyName: "Test a=b"}, + }, + { + name: "unknown fields are ignored", + input: "HOME_URL=\"https://example.com/\"\nID=test", + want: OSRelease{ID: "test"}, + }, + { + name: "malformed lines are skipped", + input: "GARBAGE\nID=test", + want: OSRelease{ID: "test"}, + }, + { + name: "surrounding whitespace is trimmed", + input: " ID=test \n", + want: OSRelease{ID: "test"}, + }, + { + name: "empty input", + input: "", + want: OSRelease{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseOSRelease(strings.NewReader(tt.input)) + require.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} + +func TestParseOSReleaseFromFixture(t *testing.T) { + tests := []struct { + file string + want OSRelease + }{ + { + file: "os-release-almalinux-8.txt", + want: OSRelease{ + Name: "AlmaLinux", + Version: "8.10 (Cerulean Leopard)", + ID: "almalinux", + IDLike: "rhel centos fedora", + VersionID: "8.10", + PrettyName: "AlmaLinux 8.10 (Cerulean Leopard)", + }, + }, + { + file: "os-release-almalinux-9.txt", + want: OSRelease{ + Name: "AlmaLinux", + Version: "9.7 (Moss Jungle Cat)", + ID: "almalinux", + IDLike: "rhel centos fedora", + VersionID: "9.7", + PrettyName: "AlmaLinux 9.7 (Moss Jungle Cat)", + }, + }, + { + file: "os-release-almalinux-10.txt", + want: OSRelease{ + Name: "AlmaLinux", + Version: "10.2 (Lavender Lion)", + ID: "almalinux", + IDLike: "rhel centos fedora", + VersionID: "10.2", + PrettyName: "AlmaLinux 10.2 (Lavender Lion)", + }, + }, + { + file: "os-release-alpine-3.txt", + want: OSRelease{ + Name: "Alpine Linux", + ID: "alpine", + VersionID: "3.23.3", + PrettyName: "Alpine Linux v3.23", + }, + }, + { + file: "os-release-amazonlinux-2.txt", + want: OSRelease{ + Name: "Amazon Linux", + Version: "2", + ID: "amzn", + IDLike: "centos rhel fedora", + VersionID: "2", + PrettyName: "Amazon Linux 2", + }, + }, + { + file: "os-release-amazonlinux-2023.txt", + want: OSRelease{ + Name: "Amazon Linux", + Version: "2023", + ID: "amzn", + IDLike: "fedora", + VersionID: "2023", + PrettyName: "Amazon Linux 2023.12.20260724", + }, + }, + { + file: "os-release-arch-2026-08.txt", + want: OSRelease{ + Name: "Arch Linux", + ID: "arch", + VersionID: "20260726.0.562117", + PrettyName: "Arch Linux", + }, + }, + { + file: "os-release-debian-12.txt", + want: OSRelease{ + Name: "Debian GNU/Linux", + Version: "12 (bookworm)", + ID: "debian", + VersionID: "12", + PrettyName: "Debian GNU/Linux 12 (bookworm)", + }, + }, + { + file: "os-release-debian-13.txt", + want: OSRelease{ + Name: "Debian GNU/Linux", + Version: "13 (trixie)", + ID: "debian", + VersionID: "13", + PrettyName: "Debian GNU/Linux 13 (trixie)", + }, + }, + { + file: "os-release-fedora-44.txt", + want: OSRelease{ + Name: "Fedora Linux", + Version: "44 (Server Edition)", + ID: "fedora", + VersionID: "44", + PrettyName: "Fedora Linux 44 (Server Edition)", + }, + }, + { + file: "os-release-fedora-container-44.txt", + want: OSRelease{ + Name: "Fedora Linux", + Version: "44 (Container Image)", + ID: "fedora", + VersionID: "44", + PrettyName: "Fedora Linux 44 (Container Image)", + }, + }, + { + file: "os-release-opensuse-tumbleweed-2026-08.txt", + want: OSRelease{ + Name: "openSUSE Tumbleweed", + ID: "opensuse-tumbleweed", + IDLike: "opensuse suse", + VersionID: "20260802", + PrettyName: "openSUSE Tumbleweed", + }, + }, + { + file: "os-release-oraclelinux-8.txt", + want: OSRelease{ + Name: "Oracle Linux Server", + Version: "8.10", + ID: "ol", + IDLike: "fedora", + VersionID: "8.10", + PrettyName: "Oracle Linux Server 8.10", + }, + }, + { + file: "os-release-oraclelinux-9.txt", + want: OSRelease{ + Name: "Oracle Linux Server", + Version: "9.8", + ID: "ol", + IDLike: "fedora", + VersionID: "9.8", + PrettyName: "Oracle Linux Server 9.8", + }, + }, + { + file: "os-release-oraclelinux-10.txt", + want: OSRelease{ + Name: "Oracle Linux Server", + Version: "10.2", + ID: "ol", + IDLike: "fedora", + VersionID: "10.2", + PrettyName: "Oracle Linux Server 10.2", + }, + }, + { + file: "os-release-redhat-8.txt", + want: OSRelease{ + Name: "Red Hat Enterprise Linux", + Version: "8.5 (Ootpa)", + ID: "rhel", + IDLike: "fedora", + VersionID: "8.5", + PrettyName: "Red Hat Enterprise Linux 8.5 (Ootpa)", + }, + }, + { + file: "os-release-redhat-9.txt", + want: OSRelease{ + Name: "Red Hat Enterprise Linux", + Version: "9.4 (Plow)", + ID: "rhel", + IDLike: "fedora", + VersionID: "9.4", + PrettyName: "Red Hat Enterprise Linux 9.4 (Plow)", + }, + }, + { + file: "os-release-rockylinux-8.txt", + want: OSRelease{ + Name: "Rocky Linux", + Version: "8.9 (Green Obsidian)", + ID: "rocky", + IDLike: "rhel centos fedora", + VersionID: "8.9", + PrettyName: "Rocky Linux 8.9 (Green Obsidian)", + }, + }, + { + file: "os-release-rockylinux-9.txt", + want: OSRelease{ + Name: "Rocky Linux", + Version: "9.3 (Blue Onyx)", + ID: "rocky", + IDLike: "rhel centos fedora", + VersionID: "9.3", + PrettyName: "Rocky Linux 9.3 (Blue Onyx)", + }, + }, + { + file: "os-release-ubuntu-24.04.txt", + want: OSRelease{ + Name: "Ubuntu", + Version: "24.04.1 LTS (Noble Numbat)", + ID: "ubuntu", + IDLike: "debian", + VersionID: "24.04", + PrettyName: "Ubuntu 24.04.1 LTS", + }, + }, + { + file: "os-release-ubuntu-26.04.txt", + want: OSRelease{ + Name: "Ubuntu", + Version: "26.04 LTS (Resolute Raccoon)", + ID: "ubuntu", + IDLike: "debian", + VersionID: "26.04", + PrettyName: "Ubuntu 26.04 LTS", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.file, func(t *testing.T) { + file, err := testfixtures.OsReleaseFS.Open(filepath.Join("testdata", "os-release", tt.file)) + require.NoError(t, err) + got, err := ParseOSRelease(file) + require.NoError(t, err) + assert.Equal(t, tt.want, got) + require.NoError(t, file.Close()) + }) + } + + t.Run("all testdata files are covered", func(t *testing.T) { + files, err := testfixtures.OsReleaseFS.ReadDir(filepath.Join("testdata", "os-release")) + require.NoError(t, err) + require.NotEmpty(t, files) + + covered := make(map[string]bool, len(tests)) + for _, tt := range tests { + covered[tt.file] = true + } + for _, file := range files { + assert.True(t, covered[filepath.Base(file.Name())], "missing test case for %s", file) + } + }) + + t.Run("missing file returns error", func(t *testing.T) { + _, err := ReadOSRelease(filepath.Join(t.TempDir(), "does-not-exist")) + require.Error(t, err) + }) +} From dba761bca484cfdbf1d8ca19b5ad5ce35309feb5 Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 14:01:34 +0200 Subject: [PATCH 10/24] Add missing license headers --- superv/internal/testfixtures/fixtures.go | 17 +++++++++++++++++ superv/internal/testsysinfo/os_release.go | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/superv/internal/testfixtures/fixtures.go b/superv/internal/testfixtures/fixtures.go index 421227d6..807e0fab 100644 --- a/superv/internal/testfixtures/fixtures.go +++ b/superv/internal/testfixtures/fixtures.go @@ -1,3 +1,20 @@ +// Copyright (C) 2026 Graylog, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the Server Side Public License, version 1, +// as published by MongoDB, Inc. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// Server Side Public License for more details. +// +// You should have received a copy of the Server Side Public License +// along with this program. If not, see +// . +// +// SPDX-License-Identifier: SSPL-1.0 + // Package testfixtures provides fixtures as embed.FS instances for reusability from different packages. package testfixtures diff --git a/superv/internal/testsysinfo/os_release.go b/superv/internal/testsysinfo/os_release.go index c3273921..30cacef4 100644 --- a/superv/internal/testsysinfo/os_release.go +++ b/superv/internal/testsysinfo/os_release.go @@ -1,3 +1,20 @@ +// Copyright (C) 2026 Graylog, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the Server Side Public License, version 1, +// as published by MongoDB, Inc. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// Server Side Public License for more details. +// +// You should have received a copy of the Server Side Public License +// along with this program. If not, see +// . +// +// SPDX-License-Identifier: SSPL-1.0 + package testsysinfo import ( From 327d4373fc941dfc90a6b48d8134908d860b67cf Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 14:30:43 +0200 Subject: [PATCH 11/24] Rename hostinfo fixtures for consistency --- ...260724.json => hostinfo-amazonlinux-2023.json} | 0 ...h-20260726.json => hostinfo-arch-2026-08.json} | 0 .../hostinfo/hostinfo-linux-ubuntu-2404.json | 15 --------------- .../hostinfo/hostinfo-linux-ubuntu-2604.json | 15 --------------- ... => hostinfo-opensuse-tumbleweed-2026-08.json} | 0 ...buntu-2404.json => hostinfo-ubuntu-24.04.json} | 0 ...buntu-2604.json => hostinfo-ubuntu-26.04.json} | 0 7 files changed, 30 deletions(-) rename superv/internal/testfixtures/testdata/hostinfo/{hostinfo-amazon-2023.12.20260724.json => hostinfo-amazonlinux-2023.json} (100%) rename superv/internal/testfixtures/testdata/hostinfo/{hostinfo-arch-20260726.json => hostinfo-arch-2026-08.json} (100%) delete mode 100644 superv/internal/testfixtures/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json delete mode 100644 superv/internal/testfixtures/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json rename superv/internal/testfixtures/testdata/hostinfo/{hostinfo-opensuse-tumbleweed-20260802.json => hostinfo-opensuse-tumbleweed-2026-08.json} (100%) rename superv/internal/testfixtures/testdata/hostinfo/{hostinfo-ubuntu-2404.json => hostinfo-ubuntu-24.04.json} (100%) rename superv/internal/testfixtures/testdata/hostinfo/{hostinfo-ubuntu-2604.json => hostinfo-ubuntu-26.04.json} (100%) diff --git a/superv/internal/testfixtures/testdata/hostinfo/hostinfo-amazon-2023.12.20260724.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-amazonlinux-2023.json similarity index 100% rename from superv/internal/testfixtures/testdata/hostinfo/hostinfo-amazon-2023.12.20260724.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-amazonlinux-2023.json diff --git a/superv/internal/testfixtures/testdata/hostinfo/hostinfo-arch-20260726.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-arch-2026-08.json similarity index 100% rename from superv/internal/testfixtures/testdata/hostinfo/hostinfo-arch-20260726.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-arch-2026-08.json diff --git a/superv/internal/testfixtures/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json deleted file mode 100644 index 8b8e20a1..00000000 --- a/superv/internal/testfixtures/testdata/hostinfo/hostinfo-linux-ubuntu-2404.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "hostname": "server-1", - "uptime": 703486, - "bootTime": 1784799975, - "procs": 148, - "os": "linux", - "platform": "ubuntu", - "platformFamily": "debian", - "platformVersion": "24.04", - "kernelVersion": "6.8.0-136-generic", - "kernelArch": "x86_64", - "virtualizationSystem": "", - "virtualizationRole": "guest", - "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" -} diff --git a/superv/internal/testfixtures/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json deleted file mode 100644 index e37d290d..00000000 --- a/superv/internal/testfixtures/testdata/hostinfo/hostinfo-linux-ubuntu-2604.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "hostname": "server-2", - "uptime": 17700, - "bootTime": 1785485632, - "procs": 502, - "os": "linux", - "platform": "ubuntu", - "platformFamily": "debian", - "platformVersion": "26.04", - "kernelVersion": "7.0.0-28-generic", - "kernelArch": "x86_64", - "virtualizationSystem": "kvm", - "virtualizationRole": "host", - "hostId": "bd5b5796-fec7-454b-9483-5eb014ea99cf" -} diff --git a/superv/internal/testfixtures/testdata/hostinfo/hostinfo-opensuse-tumbleweed-20260802.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-opensuse-tumbleweed-2026-08.json similarity index 100% rename from superv/internal/testfixtures/testdata/hostinfo/hostinfo-opensuse-tumbleweed-20260802.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-opensuse-tumbleweed-2026-08.json diff --git a/superv/internal/testfixtures/testdata/hostinfo/hostinfo-ubuntu-2404.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-ubuntu-24.04.json similarity index 100% rename from superv/internal/testfixtures/testdata/hostinfo/hostinfo-ubuntu-2404.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-ubuntu-24.04.json diff --git a/superv/internal/testfixtures/testdata/hostinfo/hostinfo-ubuntu-2604.json b/superv/internal/testfixtures/testdata/hostinfo/hostinfo-ubuntu-26.04.json similarity index 100% rename from superv/internal/testfixtures/testdata/hostinfo/hostinfo-ubuntu-2604.json rename to superv/internal/testfixtures/testdata/hostinfo/hostinfo-ubuntu-26.04.json From 051f0338e08d79a5594afcca40f407c5cb2bff45 Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 14:35:58 +0200 Subject: [PATCH 12/24] Fix error handling in sysinfo.GetOSRelease Also improve tests. --- superv/supervisor/opamp_agent_description_test.go | 4 +++- superv/sysinfo/os_release.go | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/superv/supervisor/opamp_agent_description_test.go b/superv/supervisor/opamp_agent_description_test.go index ecf78f6f..8a9e1559 100644 --- a/superv/supervisor/opamp_agent_description_test.go +++ b/superv/supervisor/opamp_agent_description_test.go @@ -26,11 +26,13 @@ import ( "github.com/Graylog2/collector/superv/internal/testsysinfo" "github.com/shirou/gopsutil/v4/host" "github.com/stretchr/testify/require" + "go.uber.org/zap" ) func TestSupervisor_NonIdentifyingAttributes_WithCollectorVersion(t *testing.T) { s := &Supervisor{ collectorVersion: "2.0.0-alpha.0", + logger: zap.NewNop(), } attrs := s.nonIdentifyingAttributes("test-host") @@ -48,7 +50,7 @@ func TestSupervisor_NonIdentifyingAttributes_WithCollectorVersion(t *testing.T) } func TestSupervisor_NonIdentifyingAttributes_WithoutCollectorVersion(t *testing.T) { - s := &Supervisor{} + s := &Supervisor{logger: zap.NewNop()} attrs := s.nonIdentifyingAttributes("test-host") diff --git a/superv/sysinfo/os_release.go b/superv/sysinfo/os_release.go index d5843cb2..76b08bc3 100644 --- a/superv/sysinfo/os_release.go +++ b/superv/sysinfo/os_release.go @@ -19,8 +19,10 @@ package sysinfo import ( "bufio" + "errors" "fmt" "io" + "io/fs" "os" "strings" ) @@ -40,7 +42,7 @@ type OSRelease struct { // preferring /etc/os-release over the /usr/lib/os-release fallback. func GetOSRelease() (OSRelease, error) { release, err := ReadOSRelease("/etc/os-release") - if os.IsNotExist(err) { + if errors.Is(err, fs.ErrNotExist) { return ReadOSRelease("/usr/lib/os-release") } return release, err From 7b6d5f94afd953c706c11c7b7867941fdcae8a37 Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 14:57:46 +0200 Subject: [PATCH 13/24] Show available files in case of error in testsysinfo --- superv/internal/testsysinfo/os_release.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/superv/internal/testsysinfo/os_release.go b/superv/internal/testsysinfo/os_release.go index 30cacef4..6786b112 100644 --- a/superv/internal/testsysinfo/os_release.go +++ b/superv/internal/testsysinfo/os_release.go @@ -38,7 +38,8 @@ func GetOSReleaseSupplier(t *testing.T, name string) func() (sysinfo.OSRelease, } file, err := testfixtures.OsReleaseFS.Open(filepath.Join("testdata", "os-release", "os-release-"+name+".txt")) - require.NoError(t, err) + files, _ := testfixtures.OsReleaseFS.ReadDir("testdata") + require.NoError(t, err, "available files: %v", files) osRelease, err := sysinfo.ParseOSRelease(file) require.NoError(t, err) From a5a323079740f47f3331dbde8904e350703c29e4 Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 15:08:52 +0200 Subject: [PATCH 14/24] Use path.Join for the embed.FS to fix tests on windows --- superv/internal/testsysinfo/os_release.go | 5 +++-- superv/supervisor/opamp_agent_description_test.go | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/superv/internal/testsysinfo/os_release.go b/superv/internal/testsysinfo/os_release.go index 6786b112..9a7d4bec 100644 --- a/superv/internal/testsysinfo/os_release.go +++ b/superv/internal/testsysinfo/os_release.go @@ -18,7 +18,7 @@ package testsysinfo import ( - "path/filepath" + "path" "strings" "testing" @@ -37,7 +37,8 @@ func GetOSReleaseSupplier(t *testing.T, name string) func() (sysinfo.OSRelease, } } - file, err := testfixtures.OsReleaseFS.Open(filepath.Join("testdata", "os-release", "os-release-"+name+".txt")) + // Using path.Join to avoid backslashes on windows. The embed.FS always uses slashes. + file, err := testfixtures.OsReleaseFS.Open(path.Join("testdata", "os-release", "os-release-"+name+".txt")) files, _ := testfixtures.OsReleaseFS.ReadDir("testdata") require.NoError(t, err, "available files: %v", files) diff --git a/superv/supervisor/opamp_agent_description_test.go b/superv/supervisor/opamp_agent_description_test.go index 8a9e1559..b71636f7 100644 --- a/superv/supervisor/opamp_agent_description_test.go +++ b/superv/supervisor/opamp_agent_description_test.go @@ -19,7 +19,7 @@ package supervisor import ( "encoding/json" - "path/filepath" + "path" "testing" "github.com/Graylog2/collector/superv/internal/testfixtures" @@ -68,7 +68,8 @@ func TestSupervisor_NonIdentifyingAttributes_WithoutCollectorVersion(t *testing. func loadHostInfo(t *testing.T, name string) *host.InfoStat { t.Helper() - data, err := testfixtures.HostInfoFS.ReadFile(filepath.Join("testdata", "hostinfo", "hostinfo-"+name+".json")) + // Using path.Join to avoid backslashes on windows. The embed.FS always uses slashes. + data, err := testfixtures.HostInfoFS.ReadFile(path.Join("testdata", "hostinfo", "hostinfo-"+name+".json")) require.NoError(t, err) info := &host.InfoStat{} From fe06f4a7cd1c504cb694c5ddcd06bc5f562ddf58 Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 15:34:12 +0200 Subject: [PATCH 15/24] More path.Join --- superv/sysinfo/os_release_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/superv/sysinfo/os_release_test.go b/superv/sysinfo/os_release_test.go index 6a461848..8d82fd9a 100644 --- a/superv/sysinfo/os_release_test.go +++ b/superv/sysinfo/os_release_test.go @@ -18,6 +18,7 @@ package sysinfo import ( + "path" "path/filepath" "strings" "testing" @@ -332,7 +333,8 @@ func TestParseOSReleaseFromFixture(t *testing.T) { for _, tt := range tests { t.Run(tt.file, func(t *testing.T) { - file, err := testfixtures.OsReleaseFS.Open(filepath.Join("testdata", "os-release", tt.file)) + // Using path.Join to avoid backslashes on windows. The embed.FS always uses slashes. + file, err := testfixtures.OsReleaseFS.Open(path.Join("testdata", "os-release", tt.file)) require.NoError(t, err) got, err := ParseOSRelease(file) require.NoError(t, err) @@ -342,7 +344,8 @@ func TestParseOSReleaseFromFixture(t *testing.T) { } t.Run("all testdata files are covered", func(t *testing.T) { - files, err := testfixtures.OsReleaseFS.ReadDir(filepath.Join("testdata", "os-release")) + // Using path.Join to avoid backslashes on windows. The embed.FS always uses slashes. + files, err := testfixtures.OsReleaseFS.ReadDir(path.Join("testdata", "os-release")) require.NoError(t, err) require.NotEmpty(t, files) From 81bf6722d94740dd83ce491107171865728c19fc Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 15:36:34 +0200 Subject: [PATCH 16/24] Use newer semconv version --- superv/ownlogs/resource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superv/ownlogs/resource.go b/superv/ownlogs/resource.go index e34f0f7a..b625a1b4 100644 --- a/superv/ownlogs/resource.go +++ b/superv/ownlogs/resource.go @@ -22,7 +22,7 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.39.0" + semconv "go.opentelemetry.io/otel/semconv/v1.41.0" ) // BuildResource creates an OTel resource with service identifying attributes From 130b29601a092582c36fb91edc0f3a4baedd850e Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 15:59:18 +0200 Subject: [PATCH 17/24] Go mod tidy --- superv/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superv/go.mod b/superv/go.mod index 22ed7246..b5c335bf 100644 --- a/superv/go.mod +++ b/superv/go.mod @@ -30,7 +30,6 @@ require ( go.uber.org/zap v1.28.0 golang.org/x/crypto v0.54.0 golang.org/x/sys v0.47.0 - golang.org/x/text v0.40.0 google.golang.org/grpc v1.83.0 google.golang.org/protobuf v1.36.11 ) @@ -67,6 +66,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect golang.org/x/net v0.57.0 // indirect + golang.org/x/text v0.40.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260729162451-8efbd57d26e0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260729162451-8efbd57d26e0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect From 65692954682e516707bc549f46cee6117076752b Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 17:05:07 +0200 Subject: [PATCH 18/24] Make getOSDescription more resilient and use fallback from host.Info() Also improve test coverage and os-release value parsing. --- superv/supervisor/opamp_agent_description.go | 42 +++++++------ .../opamp_agent_description_test.go | 63 +++++++++++++++++++ superv/sysinfo/os_release.go | 24 +++++-- superv/sysinfo/os_release_test.go | 63 ++++++++++++++++++- 4 files changed, 169 insertions(+), 23 deletions(-) diff --git a/superv/supervisor/opamp_agent_description.go b/superv/supervisor/opamp_agent_description.go index 5d0d8426..beb96b83 100644 --- a/superv/supervisor/opamp_agent_description.go +++ b/superv/supervisor/opamp_agent_description.go @@ -18,6 +18,7 @@ package supervisor import ( + "cmp" "fmt" "os" "runtime" @@ -57,7 +58,7 @@ func (s *Supervisor) nonIdentifyingAttributes(hostname string) []*protobufs.KeyV if description, err := getOSDescription(runtime.GOOS, host.Info, sysinfo.GetOSRelease); err == nil { attrs = append(attrs, attributeStringKv(semconv.OSDescriptionKey, description)) } else { - s.logger.Warn("Failed to retrieve host information", zap.Error(err)) + s.logger.Warn("Failed to determine the OS description", zap.Error(err)) } if s.collectorVersion != "" { @@ -68,27 +69,32 @@ func (s *Supervisor) nonIdentifyingAttributes(hostname string) []*protobufs.KeyV } // getOSDescription builds an "os.description" value for each platform. -func getOSDescription(os string, infoSupplier func() (*host.InfoStat, error), osReleaseSupplier func() (sysinfo.OSRelease, error)) (string, error) { - // On Linux we use data from the /etc/os-release file to get properly formatted distribution names. - if os == "linux" { - osRelease, err := osReleaseSupplier() - if err != nil { - return "", fmt.Errorf("couldn't read os-release info: %w", err) +func getOSDescription(goos string, infoSupplier func() (*host.InfoStat, error), osReleaseSupplier func() (sysinfo.OSRelease, error)) (string, error) { + // On Linux we prefer data from the /etc/os-release file to get properly + // formatted distribution names. All os-release fields are optional per the + // spec, so we fall through to the generic host info data if the file can't + // be read or yields nothing. + if goos == "linux" { + if osRelease, err := osReleaseSupplier(); err == nil { + description := cmp.Or(strings.TrimSpace(osRelease.Name+" "+osRelease.VersionID), osRelease.PrettyName) + if description != "" { + return description, nil + } } - return strings.TrimSpace(osRelease.Name + " " + osRelease.VersionID), nil } - if info, err := infoSupplier(); err == nil { - switch os { - case "darwin": - return strings.TrimSpace("macOS " + info.PlatformVersion), nil - case "windows": - return strings.TrimSpace(info.Platform + " " + info.PlatformVersion), nil - default: - return "Unknown " + info.OS, nil - } - } else { + + info, err := infoSupplier() + if err != nil { return "", fmt.Errorf("couldn't read host info: %w", err) } + switch goos { + case "darwin": + return strings.TrimSpace("macOS " + info.PlatformVersion), nil + case "windows": + return strings.TrimSpace(info.Platform + " " + info.PlatformVersion), nil + default: + return cmp.Or(strings.TrimSpace(info.Platform+" "+info.PlatformVersion), "Unknown "+info.OS), nil + } } // stringKv returns a protobufs.KeyValue for the given key and value. diff --git a/superv/supervisor/opamp_agent_description_test.go b/superv/supervisor/opamp_agent_description_test.go index b71636f7..29a6e63f 100644 --- a/superv/supervisor/opamp_agent_description_test.go +++ b/superv/supervisor/opamp_agent_description_test.go @@ -19,12 +19,15 @@ package supervisor import ( "encoding/json" + "errors" "path" "testing" "github.com/Graylog2/collector/superv/internal/testfixtures" "github.com/Graylog2/collector/superv/internal/testsysinfo" + "github.com/Graylog2/collector/superv/sysinfo" "github.com/shirou/gopsutil/v4/host" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" ) @@ -110,6 +113,21 @@ func TestGetOSDescription(t *testing.T) { require.Equal(t, tc.want, description) }) } + + t.Run("all hostinfo testdata files are covered", func(t *testing.T) { + // Using path.Join to avoid backslashes on windows. The embed.FS always uses slashes. + files, err := testfixtures.HostInfoFS.ReadDir(path.Join("testdata", "hostinfo")) + require.NoError(t, err) + require.NotEmpty(t, files) + + covered := make(map[string]bool, len(tests)) + for _, tc := range tests { + covered["hostinfo-"+tc.fixture+".json"] = true + } + for _, file := range files { + assert.True(t, covered[file.Name()], "missing test case for %s", file.Name()) + } + }) } func TestGetOSDescription_UnknownOS(t *testing.T) { @@ -121,5 +139,50 @@ func TestGetOSDescription_UnknownOS(t *testing.T) { }, nil }, testsysinfo.GetOSReleaseSupplier(t, "")) require.NoError(t, err) + require.Equal(t, "freebsd 14.1", description) +} + +func TestGetOSDescription_UnknownOSWithoutPlatform(t *testing.T) { + description, err := getOSDescription("freebsd", func() (*host.InfoStat, error) { + return &host.InfoStat{OS: "freebsd"}, nil + }, testsysinfo.GetOSReleaseSupplier(t, "")) + require.NoError(t, err) require.Equal(t, "Unknown freebsd", description) } + +func TestGetOSDescription_LinuxPrettyNameFallback(t *testing.T) { + description, err := getOSDescription("linux", nil, func() (sysinfo.OSRelease, error) { + return sysinfo.OSRelease{PrettyName: "Foo Linux 1.0 (Bar)"}, nil + }) + require.NoError(t, err) + require.Equal(t, "Foo Linux 1.0 (Bar)", description) +} + +func TestGetOSDescription_LinuxEmptyOSReleaseFallsBackToHostInfo(t *testing.T) { + description, err := getOSDescription("linux", func() (*host.InfoStat, error) { + return &host.InfoStat{OS: "linux", Platform: "ubuntu", PlatformVersion: "24.04"}, nil + }, func() (sysinfo.OSRelease, error) { + return sysinfo.OSRelease{}, nil + }) + require.NoError(t, err) + require.Equal(t, "ubuntu 24.04", description) +} + +func TestGetOSDescription_LinuxOSReleaseErrorFallsBackToHostInfo(t *testing.T) { + description, err := getOSDescription("linux", func() (*host.InfoStat, error) { + return &host.InfoStat{OS: "linux", Platform: "ubuntu", PlatformVersion: "24.04"}, nil + }, func() (sysinfo.OSRelease, error) { + return sysinfo.OSRelease{}, errors.New("no os-release file") + }) + require.NoError(t, err) + require.Equal(t, "ubuntu 24.04", description) +} + +func TestGetOSDescription_LinuxAllSourcesFailing(t *testing.T) { + _, err := getOSDescription("linux", func() (*host.InfoStat, error) { + return nil, errors.New("no host info") + }, func() (sysinfo.OSRelease, error) { + return sysinfo.OSRelease{}, errors.New("no os-release file") + }) + require.Error(t, err) +} diff --git a/superv/sysinfo/os_release.go b/superv/sysinfo/os_release.go index 76b08bc3..333a972b 100644 --- a/superv/sysinfo/os_release.go +++ b/superv/sysinfo/os_release.go @@ -41,9 +41,13 @@ type OSRelease struct { // GetOSRelease reads the os-release information of the current host, // preferring /etc/os-release over the /usr/lib/os-release fallback. func GetOSRelease() (OSRelease, error) { - release, err := ReadOSRelease("/etc/os-release") + return getOSRelease("/etc/os-release", "/usr/lib/os-release") +} + +func getOSRelease(etcPath, usrLibPath string) (OSRelease, error) { + release, err := ReadOSRelease(etcPath) if errors.Is(err, fs.ErrNotExist) { - return ReadOSRelease("/usr/lib/os-release") + return ReadOSRelease(usrLibPath) } return release, err } @@ -99,10 +103,22 @@ func ParseOSRelease(r io.Reader) (OSRelease, error) { return release, nil } -// trimQuotes removes matching surrounding double or single quotes. +// unescaper reverses the backslash escapes that shell-style double quoting +// requires for "$", quotes, backslashes, and backticks. Backslashes before +// any other character are retained, matching shell semantics. +var unescaper = strings.NewReplacer(`\"`, `"`, `\\`, `\`, `\$`, `$`, "\\`", "`") + +// trimQuotes removes matching surrounding double or single quotes. Escape +// sequences are only interpreted inside double quotes; single-quoted values +// are taken literally. Trailing content after a closing quote is kept +// verbatim (such input is invalid per the os-release spec). func trimQuotes(s string) string { if len(s) >= 2 && (s[0] == '"' || s[0] == '\'') && s[len(s)-1] == s[0] { - return s[1 : len(s)-1] + inner := s[1 : len(s)-1] + if s[0] == '"' { + return unescaper.Replace(inner) + } + return inner } return s } diff --git a/superv/sysinfo/os_release_test.go b/superv/sysinfo/os_release_test.go index 8d82fd9a..e92a6ab5 100644 --- a/superv/sysinfo/os_release_test.go +++ b/superv/sysinfo/os_release_test.go @@ -18,6 +18,7 @@ package sysinfo import ( + "os" "path" "path/filepath" "strings" @@ -91,6 +92,31 @@ func TestParseOSRelease(t *testing.T) { input: "", want: OSRelease{}, }, + { + name: "escaped quotes in double-quoted value", + input: `NAME="Foo \"Bar\""`, + want: OSRelease{Name: `Foo "Bar"`}, + }, + { + name: "escaped backslash and dollar in double-quoted value", + input: `NAME="a \\ b \$c"`, + want: OSRelease{Name: `a \ b $c`}, + }, + { + name: "escaped backtick in double-quoted value", + input: "NAME=\"run \\`cmd\\`\"", + want: OSRelease{Name: "run `cmd`"}, + }, + { + name: "backslash before other characters is retained", + input: `NAME="a\nb"`, + want: OSRelease{Name: `a\nb`}, + }, + { + name: "escapes are literal in single-quoted values", + input: `NAME='a \"b\"'`, + want: OSRelease{Name: `a \"b\"`}, + }, } for _, tt := range tests { @@ -354,7 +380,7 @@ func TestParseOSReleaseFromFixture(t *testing.T) { covered[tt.file] = true } for _, file := range files { - assert.True(t, covered[filepath.Base(file.Name())], "missing test case for %s", file) + assert.True(t, covered[file.Name()], "missing test case for %s", file.Name()) } }) @@ -363,3 +389,38 @@ func TestParseOSReleaseFromFixture(t *testing.T) { require.Error(t, err) }) } + +func TestGetOSRelease(t *testing.T) { + writeFile := func(t *testing.T, content string) string { + t.Helper() + path := filepath.Join(t.TempDir(), "os-release") + require.NoError(t, os.WriteFile(path, []byte(content), 0o600)) + return path + } + missing := func(t *testing.T) string { + t.Helper() + return filepath.Join(t.TempDir(), "does-not-exist") + } + + t.Run("prefers the etc path", func(t *testing.T) { + etcPath := writeFile(t, "ID=etc\n") + usrLibPath := writeFile(t, "ID=usrlib\n") + + release, err := getOSRelease(etcPath, usrLibPath) + require.NoError(t, err) + assert.Equal(t, "etc", release.ID) + }) + + t.Run("falls back to the usr lib path when the etc path is missing", func(t *testing.T) { + usrLibPath := writeFile(t, "ID=usrlib\n") + + release, err := getOSRelease(missing(t), usrLibPath) + require.NoError(t, err) + assert.Equal(t, "usrlib", release.ID) + }) + + t.Run("returns an error when both paths are missing", func(t *testing.T) { + _, err := getOSRelease(missing(t), missing(t)) + require.Error(t, err) + }) +} From 22e8acfe2ef743b0c900d87fafa110a2b09b4060 Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 17:05:47 +0200 Subject: [PATCH 19/24] Add TODO to remove collector.version attribute --- superv/supervisor/supervisor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superv/supervisor/supervisor.go b/superv/supervisor/supervisor.go index bd7742be..d637bf81 100644 --- a/superv/supervisor/supervisor.go +++ b/superv/supervisor/supervisor.go @@ -68,7 +68,7 @@ type Supervisor struct { maxHeartbeatInterval time.Duration persistenceDir string instanceUID string - collectorVersion string + collectorVersion string // TODO: Remove separate collector.version field authManager *auth.Manager connectionSettingsManager *connection.SettingsManager configManager *configmanager.Manager From aa24209fac3dec79b20bf72a995e63737195b53f Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 17:40:25 +0200 Subject: [PATCH 20/24] Abstract retrieving platform information into sysinfo package Better fallback handling in getOSDescription(). --- superv/supervisor/opamp_agent_description.go | 21 +++-- .../opamp_agent_description_test.go | 80 ++++++++++++++----- superv/sysinfo/platform_info.go | 48 +++++++++++ superv/sysinfo/platform_info_test.go | 33 ++++++++ 4 files changed, 153 insertions(+), 29 deletions(-) create mode 100644 superv/sysinfo/platform_info.go create mode 100644 superv/sysinfo/platform_info_test.go diff --git a/superv/supervisor/opamp_agent_description.go b/superv/supervisor/opamp_agent_description.go index beb96b83..372cf6ba 100644 --- a/superv/supervisor/opamp_agent_description.go +++ b/superv/supervisor/opamp_agent_description.go @@ -27,7 +27,6 @@ import ( "github.com/Graylog2/collector/superv/sysinfo" "github.com/Graylog2/collector/superv/version" "github.com/open-telemetry/opamp-go/protobufs" - "github.com/shirou/gopsutil/v4/host" "go.opentelemetry.io/otel/attribute" semconv "go.opentelemetry.io/otel/semconv/v1.41.0" "go.uber.org/zap" @@ -55,7 +54,7 @@ func (s *Supervisor) nonIdentifyingAttributes(hostname string) []*protobufs.KeyV attributeStringKv(semconv.ServiceVersionKey, version.Version()), } - if description, err := getOSDescription(runtime.GOOS, host.Info, sysinfo.GetOSRelease); err == nil { + if description, err := getOSDescription(runtime.GOOS, sysinfo.GetPlatformInfo, sysinfo.GetOSRelease); err == nil { attrs = append(attrs, attributeStringKv(semconv.OSDescriptionKey, description)) } else { s.logger.Warn("Failed to determine the OS description", zap.Error(err)) @@ -69,15 +68,21 @@ func (s *Supervisor) nonIdentifyingAttributes(hostname string) []*protobufs.KeyV } // getOSDescription builds an "os.description" value for each platform. -func getOSDescription(goos string, infoSupplier func() (*host.InfoStat, error), osReleaseSupplier func() (sysinfo.OSRelease, error)) (string, error) { +func getOSDescription(goos string, infoSupplier func() (sysinfo.PlatformInfo, error), osReleaseSupplier func() (sysinfo.OSRelease, error)) (string, error) { // On Linux we prefer data from the /etc/os-release file to get properly // formatted distribution names. All os-release fields are optional per the // spec, so we fall through to the generic host info data if the file can't // be read or yields nothing. if goos == "linux" { if osRelease, err := osReleaseSupplier(); err == nil { - description := cmp.Or(strings.TrimSpace(osRelease.Name+" "+osRelease.VersionID), osRelease.PrettyName) - if description != "" { + // Prefer NAME + VERSION_ID only when both are present; a partial + // combination would mask the more complete PRETTY_NAME. A bare + // NAME still beats the generic host info data. + var nameAndVersion string + if osRelease.Name != "" && osRelease.VersionID != "" { + nameAndVersion = osRelease.Name + " " + osRelease.VersionID + } + if description := cmp.Or(nameAndVersion, osRelease.PrettyName, osRelease.Name); description != "" { return description, nil } } @@ -89,11 +94,11 @@ func getOSDescription(goos string, infoSupplier func() (*host.InfoStat, error), } switch goos { case "darwin": - return strings.TrimSpace("macOS " + info.PlatformVersion), nil + return strings.TrimSpace("macOS " + info.Version), nil case "windows": - return strings.TrimSpace(info.Platform + " " + info.PlatformVersion), nil + return strings.TrimSpace(info.Name + " " + info.Version), nil default: - return cmp.Or(strings.TrimSpace(info.Platform+" "+info.PlatformVersion), "Unknown "+info.OS), nil + return cmp.Or(strings.TrimSpace(info.Name+" "+info.Version), "Unknown "+info.OS), nil } } diff --git a/superv/supervisor/opamp_agent_description_test.go b/superv/supervisor/opamp_agent_description_test.go index 29a6e63f..8644d761 100644 --- a/superv/supervisor/opamp_agent_description_test.go +++ b/superv/supervisor/opamp_agent_description_test.go @@ -26,7 +26,6 @@ import ( "github.com/Graylog2/collector/superv/internal/testfixtures" "github.com/Graylog2/collector/superv/internal/testsysinfo" "github.com/Graylog2/collector/superv/sysinfo" - "github.com/shirou/gopsutil/v4/host" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" @@ -68,17 +67,29 @@ func TestSupervisor_NonIdentifyingAttributes_WithoutCollectorVersion(t *testing. require.False(t, hasCollectorVersion, "collector.version should not be present when empty") } -func loadHostInfo(t *testing.T, name string) *host.InfoStat { +// loadPlatformInfo loads a gopsutil host.Info capture from the hostinfo +// testdata and maps it to a sysinfo.PlatformInfo. +func loadPlatformInfo(t *testing.T, name string) sysinfo.PlatformInfo { t.Helper() // Using path.Join to avoid backslashes on windows. The embed.FS always uses slashes. data, err := testfixtures.HostInfoFS.ReadFile(path.Join("testdata", "hostinfo", "hostinfo-"+name+".json")) require.NoError(t, err) - info := &host.InfoStat{} - require.NoError(t, json.Unmarshal(data, info)) + var info struct { + OS string `json:"os"` + Platform string `json:"platform"` + PlatformFamily string `json:"platformFamily"` + PlatformVersion string `json:"platformVersion"` + } + require.NoError(t, json.Unmarshal(data, &info)) - return info + return sysinfo.PlatformInfo{ + OS: info.OS, + Name: info.Platform, + Family: info.PlatformFamily, + Version: info.PlatformVersion, + } } func TestGetOSDescription(t *testing.T) { @@ -105,9 +116,8 @@ func TestGetOSDescription(t *testing.T) { for _, tc := range tests { t.Run(tc.fixture, func(t *testing.T) { - description, err := getOSDescription(tc.os, func() (*host.InfoStat, error) { - info := loadHostInfo(t, tc.fixture) - return info, nil + description, err := getOSDescription(tc.os, func() (sysinfo.PlatformInfo, error) { + return loadPlatformInfo(t, tc.fixture), nil }, testsysinfo.GetOSReleaseSupplier(t, tc.fixture)) require.NoError(t, err) require.Equal(t, tc.want, description) @@ -131,11 +141,11 @@ func TestGetOSDescription(t *testing.T) { } func TestGetOSDescription_UnknownOS(t *testing.T) { - description, err := getOSDescription("freebsd", func() (*host.InfoStat, error) { - return &host.InfoStat{ - OS: "freebsd", - Platform: "freebsd", - PlatformVersion: "14.1", + description, err := getOSDescription("freebsd", func() (sysinfo.PlatformInfo, error) { + return sysinfo.PlatformInfo{ + OS: "freebsd", + Name: "freebsd", + Version: "14.1", }, nil }, testsysinfo.GetOSReleaseSupplier(t, "")) require.NoError(t, err) @@ -143,8 +153,8 @@ func TestGetOSDescription_UnknownOS(t *testing.T) { } func TestGetOSDescription_UnknownOSWithoutPlatform(t *testing.T) { - description, err := getOSDescription("freebsd", func() (*host.InfoStat, error) { - return &host.InfoStat{OS: "freebsd"}, nil + description, err := getOSDescription("freebsd", func() (sysinfo.PlatformInfo, error) { + return sysinfo.PlatformInfo{OS: "freebsd"}, nil }, testsysinfo.GetOSReleaseSupplier(t, "")) require.NoError(t, err) require.Equal(t, "Unknown freebsd", description) @@ -158,9 +168,37 @@ func TestGetOSDescription_LinuxPrettyNameFallback(t *testing.T) { require.Equal(t, "Foo Linux 1.0 (Bar)", description) } +func TestGetOSDescription_LinuxPrettyNamePreferredOverPartialName(t *testing.T) { + // Without a VERSION_ID, PRETTY_NAME is the better presentation value. + description, err := getOSDescription("linux", nil, func() (sysinfo.OSRelease, error) { + return sysinfo.OSRelease{Name: "Gentoo", PrettyName: "Gentoo Linux"}, nil + }) + require.NoError(t, err) + require.Equal(t, "Gentoo Linux", description) +} + +func TestGetOSDescription_LinuxNameOnly(t *testing.T) { + description, err := getOSDescription("linux", nil, func() (sysinfo.OSRelease, error) { + return sysinfo.OSRelease{Name: "Gentoo"}, nil + }) + require.NoError(t, err) + require.Equal(t, "Gentoo", description) +} + +func TestGetOSDescription_LinuxVersionIDOnlyFallsBackToHostInfo(t *testing.T) { + // A bare version number is useless as a description. + description, err := getOSDescription("linux", func() (sysinfo.PlatformInfo, error) { + return sysinfo.PlatformInfo{OS: "linux", Name: "ubuntu", Version: "24.04"}, nil + }, func() (sysinfo.OSRelease, error) { + return sysinfo.OSRelease{VersionID: "24.04"}, nil + }) + require.NoError(t, err) + require.Equal(t, "ubuntu 24.04", description) +} + func TestGetOSDescription_LinuxEmptyOSReleaseFallsBackToHostInfo(t *testing.T) { - description, err := getOSDescription("linux", func() (*host.InfoStat, error) { - return &host.InfoStat{OS: "linux", Platform: "ubuntu", PlatformVersion: "24.04"}, nil + description, err := getOSDescription("linux", func() (sysinfo.PlatformInfo, error) { + return sysinfo.PlatformInfo{OS: "linux", Name: "ubuntu", Version: "24.04"}, nil }, func() (sysinfo.OSRelease, error) { return sysinfo.OSRelease{}, nil }) @@ -169,8 +207,8 @@ func TestGetOSDescription_LinuxEmptyOSReleaseFallsBackToHostInfo(t *testing.T) { } func TestGetOSDescription_LinuxOSReleaseErrorFallsBackToHostInfo(t *testing.T) { - description, err := getOSDescription("linux", func() (*host.InfoStat, error) { - return &host.InfoStat{OS: "linux", Platform: "ubuntu", PlatformVersion: "24.04"}, nil + description, err := getOSDescription("linux", func() (sysinfo.PlatformInfo, error) { + return sysinfo.PlatformInfo{OS: "linux", Name: "ubuntu", Version: "24.04"}, nil }, func() (sysinfo.OSRelease, error) { return sysinfo.OSRelease{}, errors.New("no os-release file") }) @@ -179,8 +217,8 @@ func TestGetOSDescription_LinuxOSReleaseErrorFallsBackToHostInfo(t *testing.T) { } func TestGetOSDescription_LinuxAllSourcesFailing(t *testing.T) { - _, err := getOSDescription("linux", func() (*host.InfoStat, error) { - return nil, errors.New("no host info") + _, err := getOSDescription("linux", func() (sysinfo.PlatformInfo, error) { + return sysinfo.PlatformInfo{}, errors.New("no platform info") }, func() (sysinfo.OSRelease, error) { return sysinfo.OSRelease{}, errors.New("no os-release file") }) diff --git a/superv/sysinfo/platform_info.go b/superv/sysinfo/platform_info.go new file mode 100644 index 00000000..db0322dc --- /dev/null +++ b/superv/sysinfo/platform_info.go @@ -0,0 +1,48 @@ +// Copyright (C) 2026 Graylog, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the Server Side Public License, version 1, +// as published by MongoDB, Inc. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// Server Side Public License for more details. +// +// You should have received a copy of the Server Side Public License +// along with this program. If not, see +// . +// +// SPDX-License-Identifier: SSPL-1.0 + +package sysinfo + +import ( + "runtime" + + "github.com/shirou/gopsutil/v4/host" +) + +type PlatformInfo struct { + OS string + Name string + Family string + Version string +} + +// GetPlatformInfo returns host platform data using the narrow +// host.PlatformInformation probe. host.Info would fail if any of its +// unrelated probes (host ID, boot time, uptime, ...) fails, and would +// query the host ID on a code path that only needs non-identifying data. +func GetPlatformInfo() (PlatformInfo, error) { + platform, family, version, err := host.PlatformInformation() + if err != nil { + return PlatformInfo{}, err + } + return PlatformInfo{ + OS: runtime.GOOS, + Name: platform, + Family: family, + Version: version, + }, nil +} diff --git a/superv/sysinfo/platform_info_test.go b/superv/sysinfo/platform_info_test.go new file mode 100644 index 00000000..550777c0 --- /dev/null +++ b/superv/sysinfo/platform_info_test.go @@ -0,0 +1,33 @@ +// Copyright (C) 2026 Graylog, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the Server Side Public License, version 1, +// as published by MongoDB, Inc. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// Server Side Public License for more details. +// +// You should have received a copy of the Server Side Public License +// along with this program. If not, see +// . +// +// SPDX-License-Identifier: SSPL-1.0 + +package sysinfo + +import ( + "runtime" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetPlatformInfo(t *testing.T) { + info, err := GetPlatformInfo() + require.NoError(t, err) + require.Equal(t, runtime.GOOS, info.OS) + require.NotEmpty(t, info.Name) + t.Logf("platform info: %+v", info) +} From 7f3d904fe33c79668f2e8fc70df2e7663e2d7a81 Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 17:44:53 +0200 Subject: [PATCH 21/24] Wrap error in sysinfo.GetPlatformInfo --- superv/sysinfo/platform_info.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/superv/sysinfo/platform_info.go b/superv/sysinfo/platform_info.go index db0322dc..21e7dcd6 100644 --- a/superv/sysinfo/platform_info.go +++ b/superv/sysinfo/platform_info.go @@ -18,6 +18,7 @@ package sysinfo import ( + "fmt" "runtime" "github.com/shirou/gopsutil/v4/host" @@ -37,7 +38,7 @@ type PlatformInfo struct { func GetPlatformInfo() (PlatformInfo, error) { platform, family, version, err := host.PlatformInformation() if err != nil { - return PlatformInfo{}, err + return PlatformInfo{}, fmt.Errorf("couldn't retrieve platform information: %w", err) } return PlatformInfo{ OS: runtime.GOOS, From 318985efa7116e9dfe717d91e6170993a99955d1 Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 17:51:49 +0200 Subject: [PATCH 22/24] The error from GetPlatformInfo is already wrapped --- superv/supervisor/opamp_agent_description.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/superv/supervisor/opamp_agent_description.go b/superv/supervisor/opamp_agent_description.go index 372cf6ba..36a1fbe0 100644 --- a/superv/supervisor/opamp_agent_description.go +++ b/superv/supervisor/opamp_agent_description.go @@ -19,7 +19,6 @@ package supervisor import ( "cmp" - "fmt" "os" "runtime" "strings" @@ -90,7 +89,7 @@ func getOSDescription(goos string, infoSupplier func() (sysinfo.PlatformInfo, er info, err := infoSupplier() if err != nil { - return "", fmt.Errorf("couldn't read host info: %w", err) + return "", err } switch goos { case "darwin": From 7c4160890e694706b9d613fb5bffdc24e5a962cc Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 18:10:09 +0200 Subject: [PATCH 23/24] Remove debug output --- superv/internal/testsysinfo/os_release.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/superv/internal/testsysinfo/os_release.go b/superv/internal/testsysinfo/os_release.go index 9a7d4bec..d4ed5ce7 100644 --- a/superv/internal/testsysinfo/os_release.go +++ b/superv/internal/testsysinfo/os_release.go @@ -39,8 +39,7 @@ func GetOSReleaseSupplier(t *testing.T, name string) func() (sysinfo.OSRelease, // Using path.Join to avoid backslashes on windows. The embed.FS always uses slashes. file, err := testfixtures.OsReleaseFS.Open(path.Join("testdata", "os-release", "os-release-"+name+".txt")) - files, _ := testfixtures.OsReleaseFS.ReadDir("testdata") - require.NoError(t, err, "available files: %v", files) + require.NoError(t, err) osRelease, err := sysinfo.ParseOSRelease(file) require.NoError(t, err) From 78e5e3feb5293bed16a08c9c3744587fbf52497c Mon Sep 17 00:00:00 2001 From: Bernd Ahlers Date: Tue, 4 Aug 2026 18:14:56 +0200 Subject: [PATCH 24/24] Fix godoc --- superv/sysinfo/os_release.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superv/sysinfo/os_release.go b/superv/sysinfo/os_release.go index 333a972b..7e131474 100644 --- a/superv/sysinfo/os_release.go +++ b/superv/sysinfo/os_release.go @@ -27,7 +27,7 @@ import ( "strings" ) -// OSRelease holds a subset of the fields defined in the os-release. +// OSRelease holds a subset of the fields defined in the os-release // specification: https://www.freedesktop.org/software/systemd/man/latest/os-release.html type OSRelease struct { Name string // NAME