Skip to content

Commit c5e86ab

Browse files
authored
Merge pull request #91 from SchSeba/multiple_bumps
Bump Go, golangci-lint, update GitHub Actions, fix lint issues
2 parents 805adab + eb45670 commit c5e86ab

19 files changed

+304
-106
lines changed

.github/workflows/build-test-lint.yml

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ jobs:
99
name: build
1010
strategy:
1111
matrix:
12-
go-version: [1.22.x]
12+
go-version: [1.25.x]
1313
goarch: [amd64,arm64,ppc64le,s390x]
1414
os: [ubuntu-latest]
1515
runs-on: ${{ matrix.os }}
1616
steps:
1717
- name: Set up Go matrix
18-
uses: actions/setup-go@v5
18+
uses: actions/setup-go@v6
1919
with:
2020
go-version: ${{ matrix.go-version }}
2121

@@ -57,9 +57,9 @@ jobs:
5757
name: test
5858
steps:
5959
- name: Set up Go
60-
uses: actions/setup-go@v5
60+
uses: actions/setup-go@v6
6161
with:
62-
go-version: 1.22.x
62+
go-version: 1.25.x
6363

6464
- name: Check out code into the Go module directory
6565
uses: actions/checkout@v6
@@ -76,9 +76,9 @@ jobs:
7676
name: test-coverage
7777
steps:
7878
- name: Set up Go
79-
uses: actions/setup-go@v5
79+
uses: actions/setup-go@v6
8080
with:
81-
go-version: 1.22.x
81+
go-version: 1.25.x
8282

8383
- uses: actions/checkout@v6
8484

@@ -93,15 +93,14 @@ jobs:
9393
runs-on: ubuntu-latest
9494
steps:
9595
- name: Set up Go
96-
uses: actions/setup-go@v5
96+
uses: actions/setup-go@v6
9797
with:
98-
go-version: 1.22.x
98+
go-version: 1.25.x
9999
- uses: actions/checkout@v6
100100
- name: golangci-lint
101-
uses: golangci/golangci-lint-action@v3
101+
uses: golangci/golangci-lint-action@v8
102102
with:
103-
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
104-
version: v1.63.4
103+
version: v2.7.2
105104

106105
hadolint:
107106
runs-on: ubuntu-latest
@@ -120,9 +119,9 @@ jobs:
120119
- uses: actions/checkout@v6
121120

122121
- name: Set up Go
123-
uses: actions/setup-go@v5
122+
uses: actions/setup-go@v6
124123
with:
125-
go-version: 1.22.x
124+
go-version: 1.25.x
126125

127126
# if this fails, run go mod tidy
128127
- name: Check if module files are consistent with code

.golangci.yml

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
version: "2"
2+
3+
run:
4+
timeout: 5m
5+
6+
formatters:
7+
enable:
8+
- gofmt
9+
- goimports
10+
settings:
11+
goimports:
12+
local-prefixes:
13+
- github.com/k8snetworkplumbingwg/sriov-network-metrics-exporter
14+
15+
linters:
16+
default: none
17+
enable:
18+
- bodyclose
19+
- dogsled
20+
- dupl
21+
- errcheck
22+
- copyloopvar
23+
- exhaustive
24+
- funlen
25+
- goconst
26+
- gocritic
27+
- gocyclo
28+
- mnd
29+
- goprintffuncname
30+
- gosec
31+
- govet
32+
- ineffassign
33+
- lll
34+
- misspell
35+
- nakedret
36+
- prealloc
37+
- rowserrcheck
38+
- staticcheck
39+
- unconvert
40+
- unparam
41+
- unused
42+
- whitespace
43+
settings:
44+
dupl:
45+
threshold: 100
46+
funlen:
47+
lines: 100
48+
statements: 50
49+
goconst:
50+
min-len: 2
51+
min-occurrences: 2
52+
gocritic:
53+
enabled-tags:
54+
- diagnostic
55+
- experimental
56+
- opinionated
57+
- performance
58+
- style
59+
disabled-checks:
60+
- dupImport
61+
- ifElseChain
62+
- octalLiteral
63+
- whyNoLint
64+
- wrapperFunc
65+
- unnamedResult
66+
settings:
67+
hugeParam:
68+
sizeThreshold: 512
69+
rangeValCopy:
70+
sizeThreshold: 512
71+
gocyclo:
72+
min-complexity: 15
73+
lll:
74+
line-length: 140
75+
misspell:
76+
locale: US
77+
mnd:
78+
checks:
79+
- argument
80+
- case
81+
- condition
82+
- return
83+
prealloc:
84+
simple: true
85+
range-loops: true
86+
for-loops: false
87+
exclusions:
88+
rules:
89+
- path: ".*_test\\.go"
90+
linters:
91+
- dupl
92+
- funlen
93+
- goconst
94+
- mnd
95+
- errcheck
96+
- lll
97+
- gocritic
98+
- staticcheck
99+
- unconvert
100+

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM docker.io/golang:alpine as builder
1+
FROM docker.io/golang:1.25-alpine as builder
22

33
RUN apk add --no-cache --virtual build-dependencies build-base linux-headers git
44
COPY ./ /usr/src/sriov-network-metrics-exporter

Makefile

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,16 @@ test-coverage:
5151
go test ./... -coverprofile cover.out
5252
go tool cover -func cover.out
5353

54-
go-lint-install:
55-
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.63.4
54+
GOLANGCI_LINT_VER = v2.7.2
55+
GOLANGCI_LINT = $(GOPATH)/bin/golangci-lint
5656

57-
go-lint: go-lint-install
58-
go mod tidy
59-
go fmt ./...
60-
golangci-lint run --color always -v ./...
57+
$(GOLANGCI_LINT): ; $(info installing golangci-lint...)
58+
$Q go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VER)
6159

62-
go-lint-report: go-lint-install
63-
golangci-lint run --color always -v ./... &> golangci-lint.txt
60+
lint: | $(GOLANGCI_LINT) ; $(info running golangci-lint...) @ ## Run golangci-lint
61+
$Q $(GOLANGCI_LINT) run --timeout=5m
62+
63+
go-lint: lint
64+
65+
go-lint-report: | $(GOLANGCI_LINT)
66+
$(GOLANGCI_LINT) run --color always -v ./... &> golangci-lint.txt

cmd/sriov-network-metrics-exporter.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,24 @@ import (
77
"flag"
88
"log"
99
"net/http"
10-
11-
"github.com/k8snetworkplumbingwg/sriov-network-metrics-exporter/collectors"
10+
"time"
1211

1312
"github.com/prometheus/client_golang/prometheus"
1413
"github.com/prometheus/client_golang/prometheus/promhttp"
1514
"golang.org/x/time/rate"
15+
16+
"github.com/k8snetworkplumbingwg/sriov-network-metrics-exporter/collectors"
17+
)
18+
19+
const (
20+
defaultRateBurst = 10
21+
defaultReadHeaderTimeout = 10 * time.Second
1622
)
1723

1824
var (
1925
addr = flag.String("web.listen-address", ":9808", "Port to listen on for web interface and telemetry.")
2026
rateLimit = flag.Int("web.rate-limit", 1, "Limit for requests per second.")
21-
rateBurst = flag.Int("web.rate-burst", 10, "Maximum per second burst rate for requests.")
27+
rateBurst = flag.Int("web.rate-burst", defaultRateBurst, "Maximum per second burst rate for requests.")
2228
metricsEndpoint = "/metrics"
2329
)
2430

@@ -38,8 +44,13 @@ func main() {
3844
noBody(promhttp.Handler()), metricsEndpoint)),
3945
rate.Limit(*rateLimit), *rateBurst)
4046

47+
server := &http.Server{
48+
Addr: *addr,
49+
Handler: handlerWithMiddleware,
50+
ReadHeaderTimeout: defaultReadHeaderTimeout,
51+
}
4152
log.Printf("listening on %v", *addr)
42-
log.Fatalf("ListenAndServe error: %v", http.ListenAndServe(*addr, handlerWithMiddleware))
53+
log.Fatalf("ListenAndServe error: %v", server.ListenAndServe())
4354
}
4455

4556
func parseAndVerifyFlags() {

cmd/sriov-network-metrics-exporter_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func TestMain(t *testing.T) {
2121
var _ = DescribeTable("test endpointOnly handler", // endpointOnly
2222
func(endpoint string, expectedResponse int) {
2323
recorder := httptest.NewRecorder()
24-
request := httptest.NewRequest(http.MethodGet, endpoint, nil)
24+
request := httptest.NewRequest(http.MethodGet, endpoint, http.NoBody)
2525
handler := endpointOnly(promhttp.Handler(), metricsEndpoint)
2626

2727
handler.ServeHTTP(recorder, request)
@@ -35,7 +35,7 @@ var _ = DescribeTable("test endpointOnly handler", // endpointOnly
3535
var _ = DescribeTable("test getOnly handler", // getOnly
3636
func(method string, expectedResponse int) {
3737
recorder := httptest.NewRecorder()
38-
request := httptest.NewRequest(method, metricsEndpoint, nil)
38+
request := httptest.NewRequest(method, metricsEndpoint, http.NoBody)
3939
handler := getOnly(promhttp.Handler())
4040

4141
handler.ServeHTTP(recorder, request)
@@ -67,7 +67,7 @@ var _ = DescribeTable("test limitRequests handler", // limitRequests
6767
code := http.StatusOK
6868
for i := 0; i < requests; i++ {
6969
recorder := httptest.NewRecorder()
70-
request := httptest.NewRequest(http.MethodGet, metricsEndpoint, nil)
70+
request := httptest.NewRequest(http.MethodGet, metricsEndpoint, http.NoBody)
7171
handler.ServeHTTP(recorder, request)
7272

7373
code = recorder.Code

collectors/collectors.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
// Package Collectors defines the structure of the collector aggregator and contains the individual collectors used to gather metrics
2-
// Each collector should be created in its own file with any required command line flags, its collection behavior and its registration method defined.
1+
// Package collectors defines the structure of the collector aggregator and contains the individual collectors
2+
// used to gather metrics.
3+
// Each collector should be created in its own file with any required command line flags, its collection
4+
// behavior and its registration method defined.
35

46
package collectors
57

@@ -22,7 +24,8 @@ var (
2224
// SriovCollector registers the collectors used for specific data and exposes a Collect method to gather the data
2325
type SriovCollector []prometheus.Collector
2426

25-
// Register defines a flag for a collector and adds it to the registry of enabled collectors if the flag is set to true - either through the default option or the flag passed on start
27+
// Register defines a flag for a collector and adds it to the registry of enabled collectors
28+
// if the flag is set to true - either through the default option or the flag passed on start.
2629
// Run by each individual collector in its init function.
2730
func register(name string, enabled bool, collector func() prometheus.Collector) {
2831
collectorState[name] = &enabled

collectors/collectors_test.go

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"log"
77
"path/filepath"
88
"testing"
9+
"testing/fstest"
910
"time"
1011

1112
. "github.com/onsi/ginkgo/v2"
@@ -81,21 +82,34 @@ var _ = DescribeTable("test registering collector", // register
8182

8283
func assertLogs(logs []string) {
8384
for _, log := range logs {
84-
Eventually(&buffer).WithTimeout(time.Duration(2 * time.Second)).Should(gbytes.Say(log))
85+
Eventually(&buffer).WithTimeout(2 * time.Second).Should(gbytes.Say(log))
8586
}
8687
}
8788

8889
// Replaces filepath.EvalSymlinks with an emulated evaluation to work with the in-memory fs.
8990
var evalSymlinks = func(path string) (string, error) {
9091
path = filepath.Join(filepath.Base(filepath.Dir(path)), filepath.Base(path))
92+
dir := filepath.Dir(path)
93+
base := filepath.Base(path)
9194

92-
if stat, err := fs.Stat(devfs, path); err == nil && stat.Mode() == fs.ModeSymlink {
93-
if target, err := fs.ReadFile(devfs, path); err == nil {
94-
return string(target), nil
95-
} else {
96-
return "", fmt.Errorf("error")
95+
entries, err := fs.ReadDir(devfs, dir)
96+
if err != nil {
97+
return "", fmt.Errorf("error reading dir: %v", err)
98+
}
99+
100+
for _, entry := range entries {
101+
if entry.Name() == base && entry.Type()&fs.ModeSymlink != 0 {
102+
// In Go 1.25, fstest.MapFS treats fs.ModeSymlink files as actual symlinks
103+
// and tries to follow them, so fs.ReadFile won't work.
104+
// Access the MapFS directly to get the symlink target data.
105+
if mapFS, ok := devfs.(fstest.MapFS); ok {
106+
if mapFile, exists := mapFS[path]; exists {
107+
return string(mapFile.Data), nil
108+
}
109+
}
110+
return "", fmt.Errorf("error reading symlink target")
97111
}
98-
} else {
99-
return "", fmt.Errorf("error")
100112
}
113+
114+
return "", fmt.Errorf("not a symlink or not found")
101115
}

collectors/pod_cpu_link.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package collectors
22

3-
// kubepodCPUCollector is a Kubernetes focused collector that exposes information about CPUs linked to specific Kubernetes pods through the CPU Manager component in Kubelet
3+
// kubepodCPUCollector is a Kubernetes focused collector that exposes information about CPUs
4+
// linked to specific Kubernetes pods through the CPU Manager component in Kubelet
45

56
import (
67
"encoding/json"
@@ -21,9 +22,11 @@ import (
2122

2223
var (
2324
kubepodcpu = "kubepodcpu"
24-
kubePodCgroupPath = flag.String("path.kubecgroup", "/sys/fs/cgroup/cpuset/kubepods.slice/", "Path for location of kubernetes cgroups on the host system")
25+
kubePodCgroupPath = flag.String("path.kubecgroup",
26+
"/sys/fs/cgroup/cpuset/kubepods.slice/", "Path for kubernetes cgroups")
2527
sysDevSysNodePath = flag.String("path.nodecpuinfo", "/sys/devices/system/node/", "Path for location of system cpu information")
26-
cpuCheckPointFile = flag.String("path.cpucheckpoint", "/var/lib/kubelet/cpu_manager_state", "Path for location of cpu manager checkpoint file")
28+
cpuCheckPointFile = flag.String("path.cpucheckpoint",
29+
"/var/lib/kubelet/cpu_manager_state", "Path for cpu manager checkpoint file")
2730

2831
kubecgroupfs fs.FS
2932
cpuinfofs fs.FS
@@ -108,7 +111,7 @@ func (c kubepodCPUCollector) Describe(ch chan<- *prometheus.Desc) {
108111
func createKubepodCPUCollector() prometheus.Collector {
109112
cpuInfo, err := getCPUInfo()
110113
if err != nil {
111-
//Exporter will fail here if file can not be read.
114+
// Exporter will fail here if file can not be read.
112115
logFatal("Fatal Error: cpu info for node can not be collected, %v", err.Error())
113116
}
114117

@@ -154,7 +157,8 @@ func getCPUInfo() (map[string]string, error) {
154157
// This accounting will create an entry for each guaranteed pod, even if that pod isn't managed by CPU manager
155158
// i.e. it will still create an entry if the pod is looking for millis of CPU
156159
// Todo: validate regex matching and evaluate performance of this approach
157-
// Todo: validate assumptions about directory structure against other runtimes and kubelet config. Plausibly problematic with CgroupsPerQos and other possible future cgroup changes
160+
// Todo: validate assumptions about directory structure against other runtimes and kubelet config.
161+
// Plausibly problematic with CgroupsPerQos and other possible future cgroup changes
158162
func getGuaranteedPodCPUs() ([]podCPULink, error) {
159163
links := make([]podCPULink, 0)
160164

@@ -259,7 +263,7 @@ func parseCPURange(cpuString string) ([]string, error) {
259263
endpoints := strings.Split(r, "-")
260264
if len(endpoints) == 1 {
261265
cpuList = append(cpuList, endpoints[0])
262-
} else if len(endpoints) == 2 {
266+
} else if len(endpoints) == 2 { //nolint:mnd
263267
start, err := strconv.Atoi(endpoints[0])
264268
if err != nil {
265269
return cpuList, err

0 commit comments

Comments
 (0)