Skip to content

Commit fca75e8

Browse files
author
Arun S
committed
collector: add tainted collector for /proc/sys/kernel/tainted
Signed-off-by: Arun S <arun.srinivasan@flipkart.com>
1 parent d6c236f commit fca75e8

3 files changed

Lines changed: 180 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
12288

collector/tainted_linux.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2024 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package collector
15+
16+
import (
17+
"fmt"
18+
"log/slog"
19+
"strconv"
20+
21+
"github.com/prometheus/client_golang/prometheus"
22+
"github.com/prometheus/procfs"
23+
)
24+
25+
type taintedCollector struct {
26+
logger *slog.Logger
27+
desc *prometheus.Desc
28+
}
29+
30+
func init() {
31+
registerCollector("tainted", defaultDisabled, NewTaintedCollector)
32+
}
33+
34+
// NewTaintedCollector returns a Collector exposing kernel taint flags from
35+
// /proc/sys/kernel/tainted as a labelled gauge.
36+
// See https://www.kernel.org/doc/html/latest/admin-guide/tainted-kernels.html
37+
func NewTaintedCollector(logger *slog.Logger) (Collector, error) {
38+
return &taintedCollector{
39+
logger: logger,
40+
desc: prometheus.NewDesc(
41+
prometheus.BuildFQName(namespace, "kernel", "tainted"),
42+
"Taint flags set on the running Linux kernel, as reported by /proc/sys/kernel/tainted. "+
43+
"Value is 1 if the flag is set, 0 otherwise. "+
44+
"See https://www.kernel.org/doc/html/latest/admin-guide/tainted-kernels.html for flag meanings.",
45+
[]string{"bit", "flag"},
46+
nil,
47+
),
48+
}, nil
49+
}
50+
51+
func (c *taintedCollector) Update(ch chan<- prometheus.Metric) error {
52+
fs, err := procfs.NewFS(*procPath)
53+
if err != nil {
54+
return fmt.Errorf("failed to open procfs: %w", err)
55+
}
56+
57+
tainted, err := fs.KernelTainted()
58+
if err != nil {
59+
return fmt.Errorf("couldn't read kernel tainted state: %w", err)
60+
}
61+
62+
for _, b := range tainted.Bits {
63+
var val float64
64+
if b.Set {
65+
val = 1.0
66+
}
67+
ch <- prometheus.MustNewConstMetric(
68+
c.desc,
69+
prometheus.GaugeValue,
70+
val,
71+
strconv.Itoa(b.Index),
72+
b.Flag,
73+
)
74+
}
75+
return nil
76+
}

collector/tainted_linux_test.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright 2024 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
//go:build !notainted
15+
16+
package collector
17+
18+
import (
19+
"io"
20+
"log/slog"
21+
"testing"
22+
23+
"github.com/prometheus/client_golang/prometheus"
24+
)
25+
26+
func TestTaintedCollector(t *testing.T) {
27+
*procPath = "fixtures/proc"
28+
29+
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
30+
c, err := NewTaintedCollector(logger)
31+
if err != nil {
32+
t.Fatalf("failed to create tainted collector: %v", err)
33+
}
34+
35+
reg := prometheus.NewPedanticRegistry()
36+
reg.MustRegister(&taintedCollectorWrapper{c.(*taintedCollector)})
37+
38+
mfs, err := reg.Gather()
39+
if err != nil {
40+
t.Fatalf("gather failed: %v", err)
41+
}
42+
if len(mfs) != 1 {
43+
t.Fatalf("expected 1 metric family, got %d", len(mfs))
44+
}
45+
46+
mf := mfs[0]
47+
if got := mf.GetName(); got != "node_kernel_tainted" {
48+
t.Errorf("metric name: want node_kernel_tainted, got %s", got)
49+
}
50+
51+
// Expect one series per known taint bit (20 defined by the kernel).
52+
const wantBits = 20
53+
if got := len(mf.GetMetric()); got != wantBits {
54+
t.Errorf("metric count: want %d, got %d", wantBits, got)
55+
}
56+
57+
// Build bit → value map for assertion.
58+
// Fixture is 12288 = bit 12 (O) + bit 13 (E).
59+
// Build flag → value map for assertion (labels: bit, flag).
60+
flagVals := make(map[string]float64)
61+
for _, m := range mf.GetMetric() {
62+
// Each metric has exactly 2 labels: bit and flag.
63+
for _, lp := range m.GetLabel() {
64+
if lp.GetName() == "flag" {
65+
flagVals[lp.GetValue()] = m.GetGauge().GetValue()
66+
}
67+
}
68+
}
69+
70+
// Fixture is 12288 = bit 12 (O) + bit 13 (E).
71+
for _, tc := range []struct {
72+
flag string
73+
want float64
74+
}{
75+
{"O", 1}, // Externally-built (out-of-tree) module — set
76+
{"E", 1}, // Unsigned module — set
77+
{"L", 0}, // Soft lockup — must be clear
78+
{"P", 0},
79+
{"T", 0},
80+
} {
81+
got, ok := flagVals[tc.flag]
82+
if !ok {
83+
t.Errorf("flag %q not found in metrics", tc.flag)
84+
continue
85+
}
86+
if got != tc.want {
87+
t.Errorf("flag %q: want %.0f, got %.0f", tc.flag, tc.want, got)
88+
}
89+
}
90+
}
91+
92+
// taintedCollectorWrapper adapts taintedCollector to prometheus.Collector.
93+
type taintedCollectorWrapper struct {
94+
c *taintedCollector
95+
}
96+
97+
func (w *taintedCollectorWrapper) Describe(ch chan<- *prometheus.Desc) {
98+
ch <- w.c.desc
99+
}
100+
101+
func (w *taintedCollectorWrapper) Collect(ch chan<- prometheus.Metric) {
102+
_ = w.c.Update(ch)
103+
}

0 commit comments

Comments
 (0)