diff --git a/.circleci/config.yml b/.circleci/config.yml index 478aa310..b389cc3f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -7,10 +7,10 @@ executors: # should also be updated. golang: docker: - - image: cimg/go:1.24 + - image: cimg/go:1.25 arm: docker: - - image: cimg/go:1.24 + - image: cimg/go:1.25 resource_class: arm.medium jobs: @@ -70,7 +70,7 @@ jobs: machine: image: ubuntu-2404:current environment: - DOCKER_TEST_IMAGE_NAME: quay.io/prometheus/golang-builder:1.24-base + DOCKER_TEST_IMAGE_NAME: quay.io/prometheus/golang-builder:1.25-base REPO_PATH: github.com/prometheus/node_exporter steps: - prometheus/setup_environment diff --git a/.github/workflows/bsd.yml b/.github/workflows/bsd.yml index 8130bbda..6c61e177 100644 --- a/.github/workflows/bsd.yml +++ b/.github/workflows/bsd.yml @@ -13,11 +13,11 @@ permissions: env: GNU_TAR_VERSION: "1.35" - GO_VERSION_DRAGONFLY: "1.24.1" - GO_VERSION_FREEBSD: "123" - GO_VERSION_NETBSD: "1.24.1" + GO_VERSION_DRAGONFLY: "1.25.1" + GO_VERSION_FREEBSD: "125" + GO_VERSION_NETBSD: "1.25.1" GO_VERSION_OPENBSD: "1.24.1" - GO_VERSION_SOLARIS: "1.24.1" + GO_VERSION_SOLARIS: "1.25.1" # To spin up one of the VMs below, see the "Debug Shell" section here: https://github.com/vmactions jobs: @@ -28,8 +28,9 @@ jobs: - name: Checkout the repository uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - name: test-e2e - uses: vmactions/freebsd-vm@05856381fab64eeee9b038a0818f6cec649ca17a # v1.2.3 + uses: vmactions/freebsd-vm@487ce35b96fae3e60d45b521735f5aa436ecfade # v1.2.4 with: + release: "15.0" copyback: false envs: 'GO_VERSION_FREEBSD GNU_TAR_VERSION' usesh: true diff --git a/.promu-cgo.yml b/.promu-cgo.yml index 7ce1b201..aa40105c 100644 --- a/.promu-cgo.yml +++ b/.promu-cgo.yml @@ -1,7 +1,7 @@ go: # Whenever the Go version is updated here, .circle/config.yml and # .promu.yml should also be updated. - version: 1.24 + version: 1.25 cgo: true repository: path: github.com/prometheus/node_exporter diff --git a/.promu.yml b/.promu.yml index b38799f8..a7076d5b 100644 --- a/.promu.yml +++ b/.promu.yml @@ -1,7 +1,7 @@ go: # Whenever the Go version is updated here, .circle/config.yml and # .promu-cgo.yml should also be updated. - version: 1.24 + version: 1.25 repository: path: github.com/prometheus/node_exporter build: diff --git a/README.md b/README.md index 0e4f3d38..3761bcc9 100644 --- a/README.md +++ b/README.md @@ -208,6 +208,7 @@ qdisc | Exposes [queuing discipline](https://en.wikipedia.org/wiki/Network_sched slabinfo | Exposes slab statistics from `/proc/slabinfo`. Note that permission of `/proc/slabinfo` is usually 0400, so set it appropriately. | Linux softirqs | Exposes detailed softirq statistics from `/proc/softirqs`. | Linux sysctl | Expose sysctl values from `/proc/sys`. Use `--collector.sysctl.include(-info)` to configure. | Linux +swap | Expose swap information from `/proc/swaps`. | Linux systemd | Exposes service and system status from [systemd](http://www.freedesktop.org/wiki/Software/systemd/). | Linux tcpstat | Exposes TCP connection status information from `/proc/net/tcp` and `/proc/net/tcp6`. (Warning: the current version has potential performance issues in high load situations.) | Linux wifi | Exposes WiFi device and station statistics. | Linux diff --git a/collector/fixtures/e2e-output-darwin.txt b/collector/fixtures/e2e-output-darwin.txt index 61b36997..cf29fb47 100644 --- a/collector/fixtures/e2e-output-darwin.txt +++ b/collector/fixtures/e2e-output-darwin.txt @@ -94,15 +94,19 @@ node_buddyinfo_blocks{node="0",size="9",zone="Normal"} 0 # HELP node_disk_read_errors_total The total number of read errors. # TYPE node_disk_read_errors_total counter node_disk_read_errors_total{device="disk0"} 0 +node_disk_read_errors_total{device="disk4"} 0 # HELP node_disk_read_retries_total The total number of read retries. # TYPE node_disk_read_retries_total counter node_disk_read_retries_total{device="disk0"} 0 +node_disk_read_retries_total{device="disk4"} 0 # HELP node_disk_write_errors_total The total number of write errors. # TYPE node_disk_write_errors_total counter node_disk_write_errors_total{device="disk0"} 0 +node_disk_write_errors_total{device="disk4"} 0 # HELP node_disk_write_retries_total The total number of write retries. # TYPE node_disk_write_retries_total counter node_disk_write_retries_total{device="disk0"} 0 +node_disk_write_retries_total{device="disk4"} 0 # HELP node_exporter_build_info A metric with a constant '1' value labeled by version, revision, branch, goversion from which node_exporter was built, and the goos and goarch for the build. # TYPE node_exporter_build_info gauge # HELP node_memory_swap_total_bytes Memory information field swap_total_bytes. diff --git a/collector/fixtures/e2e-output-freebsd.txt b/collector/fixtures/e2e-output-freebsd.txt index fad76e6f..0356d44e 100644 --- a/collector/fixtures/e2e-output-freebsd.txt +++ b/collector/fixtures/e2e-output-freebsd.txt @@ -261,6 +261,18 @@ node_xfrm_out_state_proto_error_packets_total 4542 # HELP node_xfrm_out_state_seq_error_packets_total Sequence error i.e. Sequence number overflow # TYPE node_xfrm_out_state_seq_error_packets_total counter node_xfrm_out_state_seq_error_packets_total 543 +# HELP node_zfs_arcstats_c_min_bytes ZFS ARC minimum size +# TYPE node_zfs_arcstats_c_min_bytes gauge +node_zfs_arcstats_c_min_bytes 1.99932544e+08 +# HELP node_zfs_arcstats_mfu_ghost_size ZFS ARC MFU ghost size +# TYPE node_zfs_arcstats_mfu_ghost_size gauge +node_zfs_arcstats_mfu_ghost_size 0 +# HELP node_zfs_arcstats_mru_ghost_hits_total ZFS ARC MRU ghost hits +# TYPE node_zfs_arcstats_mru_ghost_hits_total counter +node_zfs_arcstats_mru_ghost_hits_total 0 +# HELP node_zfs_arcstats_pm_bytes ZFS ARC meta MRU target frac +# TYPE node_zfs_arcstats_pm_bytes gauge +node_zfs_arcstats_pm_bytes 2.147483648e+09 # HELP promhttp_metric_handler_errors_total Total number of internal errors encountered by the promhttp metric handler. # TYPE promhttp_metric_handler_errors_total counter promhttp_metric_handler_errors_total{cause="encoding"} 0 diff --git a/collector/fixtures/proc/swaps b/collector/fixtures/proc/swaps new file mode 100644 index 00000000..c0fbf778 --- /dev/null +++ b/collector/fixtures/proc/swaps @@ -0,0 +1,2 @@ +Filename Type Size Used Priority +/dev/zram0 partition 8388604 76 100 diff --git a/collector/perf_linux.go b/collector/perf_linux.go index b62d9b80..67b48995 100644 --- a/collector/perf_linux.go +++ b/collector/perf_linux.go @@ -86,10 +86,10 @@ var ( "BPUReadHit": perf.BPUReadHitProfiler, "BPUReadMiss": perf.BPUReadMissProfiler, // "L1InstrReadHit": perf.L1InstrReadHitProfiler, - // "DataTLBReadHit": perf.DataTLBReadHitProfiler, - // "DataTLBReadMiss": perf.DataTLBReadMissProfiler, - // "DataTLBWriteHit": perf.DataTLBWriteHitProfiler, - // "DataTLBWriteMiss": perf.DataTLBWriteMissProfiler, + "DataTLBReadHit": perf.DataTLBReadHitProfiler, + "DataTLBReadMiss": perf.DataTLBReadMissProfiler, + "DataTLBWriteHit": perf.DataTLBWriteHitProfiler, + "DataTLBWriteMiss": perf.DataTLBWriteMissProfiler, // "NodeCacheReadHit": perf.NodeCacheReadHitProfiler, // "NodeCacheReadMiss": perf.NodeCacheReadMissProfiler, // "NodeCacheWriteHit": perf.NodeCacheWriteHitProfiler, @@ -355,7 +355,7 @@ func NewPerfCollector(logger *slog.Logger) (Collector, error) { } } } - cacheProfilers := perf.L1DataReadHitProfiler | perf.L1DataReadMissProfiler | perf.L1DataWriteHitProfiler | perf.L1InstrReadMissProfiler | perf.InstrTLBReadHitProfiler | perf.InstrTLBReadMissProfiler | perf.LLReadHitProfiler | perf.LLReadMissProfiler | perf.LLWriteHitProfiler | perf.LLWriteMissProfiler | perf.BPUReadHitProfiler | perf.BPUReadMissProfiler + cacheProfilers := perf.L1DataReadHitProfiler | perf.L1DataReadMissProfiler | perf.L1DataWriteHitProfiler | perf.L1InstrReadMissProfiler | perf.InstrTLBReadHitProfiler | perf.InstrTLBReadMissProfiler | perf.DataTLBReadHitProfiler | perf.DataTLBReadMissProfiler | perf.DataTLBWriteHitProfiler | perf.DataTLBWriteMissProfiler | perf.LLReadHitProfiler | perf.LLReadMissProfiler | perf.LLWriteHitProfiler | perf.LLWriteMissProfiler | perf.BPUReadHitProfiler | perf.BPUReadMissProfiler if *perfCaProfilerFlag != nil && len(*perfCaProfilerFlag) > 0 { cacheProfilers = 0 for _, cf := range *perfCaProfilerFlag { @@ -615,6 +615,46 @@ func NewPerfCollector(logger *slog.Logger) (Collector, error) { []string{"cpu"}, nil, ), + "cache_tlb_data_read_hits_total": prometheus.NewDesc( + prometheus.BuildFQName( + namespace, + perfSubsystem, + "cache_tlb_data_read_hits_total", + ), + "Number of data TLB read hits", + []string{"cpu"}, + nil, + ), + "cache_tlb_data_read_misses_total": prometheus.NewDesc( + prometheus.BuildFQName( + namespace, + perfSubsystem, + "cache_tlb_data_read_misses_total", + ), + "Number of data TLB read misses", + []string{"cpu"}, + nil, + ), + "cache_tlb_data_write_hits_total": prometheus.NewDesc( + prometheus.BuildFQName( + namespace, + perfSubsystem, + "cache_tlb_data_write_hits_total", + ), + "Number of data TLB write hits", + []string{"cpu"}, + nil, + ), + "cache_tlb_data_write_misses_total": prometheus.NewDesc( + prometheus.BuildFQName( + namespace, + perfSubsystem, + "cache_tlb_data_write_misses_total", + ), + "Number of data TLB write misses", + []string{"cpu"}, + nil, + ), "cache_ll_read_hits_total": prometheus.NewDesc( prometheus.BuildFQName( namespace, @@ -895,6 +935,38 @@ func (c *perfCollector) updateCacheStats(ch chan<- prometheus.Metric) error { ) } + if cacheProfile.DataTLBReadHit != nil { + ch <- prometheus.MustNewConstMetric( + c.desc["cache_tlb_data_read_hits_total"], + prometheus.CounterValue, float64(*cacheProfile.DataTLBReadHit), + cpuid, + ) + } + + if cacheProfile.DataTLBReadMiss != nil { + ch <- prometheus.MustNewConstMetric( + c.desc["cache_tlb_data_read_misses_total"], + prometheus.CounterValue, float64(*cacheProfile.DataTLBReadMiss), + cpuid, + ) + } + + if cacheProfile.DataTLBWriteHit != nil { + ch <- prometheus.MustNewConstMetric( + c.desc["cache_tlb_data_write_hits_total"], + prometheus.CounterValue, float64(*cacheProfile.DataTLBWriteHit), + cpuid, + ) + } + + if cacheProfile.DataTLBWriteMiss != nil { + ch <- prometheus.MustNewConstMetric( + c.desc["cache_tlb_data_write_misses_total"], + prometheus.CounterValue, float64(*cacheProfile.DataTLBWriteMiss), + cpuid, + ) + } + if cacheProfile.LastLevelReadHit != nil { ch <- prometheus.MustNewConstMetric( c.desc["cache_ll_read_hits_total"], diff --git a/collector/swap_linux.go b/collector/swap_linux.go new file mode 100644 index 00000000..9a275e16 --- /dev/null +++ b/collector/swap_linux.go @@ -0,0 +1,129 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !noswap + +package collector + +import ( + "fmt" + "log/slog" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/procfs" +) + +const ( + swapSubsystem = "swap" +) + +var swapLabelNames = []string{"device", "swap_type"} + +type swapCollector struct { + fs procfs.FS + logger *slog.Logger +} + +func init() { + registerCollector("swap", defaultDisabled, NewSwapCollector) +} + +// NewSwapCollector returns a new Collector exposing swap device statistics. +func NewSwapCollector(logger *slog.Logger) (Collector, error) { + fs, err := procfs.NewFS(*procPath) + if err != nil { + return nil, fmt.Errorf("failed to open procfs: %w", err) + } + + return &swapCollector{ + fs: fs, + logger: logger, + }, nil +} + +type SwapsEntry struct { + Device string + Type string + Priority int + Size int + Used int +} + +func (c *swapCollector) getSwapInfo() ([]SwapsEntry, error) { + swaps, err := c.fs.Swaps() + if err != nil { + return nil, fmt.Errorf("couldn't get proc/swap information: %w", err) + } + + metrics := make([]SwapsEntry, 0, len(swaps)) + + for _, swap := range swaps { + metrics = append(metrics, SwapsEntry{Device: swap.Filename, Type: swap.Type, + Priority: swap.Priority, Size: swap.Size, Used: swap.Used}) + } + + return metrics, nil +} + +func (c *swapCollector) Update(ch chan<- prometheus.Metric) error { + swaps, err := c.getSwapInfo() + if err != nil { + return fmt.Errorf("couldn't get swap information: %w", err) + } + + for _, swap := range swaps { + swapLabelValues := []string{swap.Device, swap.Type} + + // Export swap size in bytes + ch <- prometheus.MustNewConstMetric( + prometheus.NewDesc( + prometheus.BuildFQName(namespace, swapSubsystem, "size_bytes"), + "Swap device size in bytes.", + []string{"device", "swap_type"}, nil, + ), + prometheus.GaugeValue, + // Size is provided in kbytes (not bytes), translate to bytes + // see https://github.com/torvalds/linux/blob/fd94619c43360eb44d28bd3ef326a4f85c600a07/mm/swapfile.c#L3079-L3080 + float64(swap.Size*1024), + swapLabelValues..., + ) + + // Export swap used in bytes + ch <- prometheus.MustNewConstMetric( + prometheus.NewDesc( + prometheus.BuildFQName(namespace, swapSubsystem, "used_bytes"), + "Swap device used in bytes.", + swapLabelNames, nil, + ), + prometheus.GaugeValue, + // Swap used is also provided in kbytes, translate to bytes + float64(swap.Used*1024), + swapLabelValues..., + ) + + // Export swap priority + ch <- prometheus.MustNewConstMetric( + prometheus.NewDesc( + prometheus.BuildFQName(namespace, swapSubsystem, "priority"), + "Swap device priority.", + swapLabelNames, nil, + ), + prometheus.GaugeValue, + float64(swap.Priority), + swapLabelValues..., + ) + + } + + return nil +} diff --git a/collector/swap_linux_test.go b/collector/swap_linux_test.go new file mode 100644 index 00000000..bd9e44eb --- /dev/null +++ b/collector/swap_linux_test.go @@ -0,0 +1,58 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//go:build !noswap +// +build !noswap + +package collector + +import ( + "io" + "log/slog" + "testing" +) + +func TestSwap(t *testing.T) { + *procPath = "fixtures/proc" + logger := slog.New(slog.NewTextHandler(io.Discard, nil)) + + collector, err := NewSwapCollector(logger) + if err != nil { + panic(err) + } + + swapInfo, err := collector.(*swapCollector).getSwapInfo() + if err != nil { + panic(err) + } + + if want, got := "/dev/zram0", swapInfo[0].Device; want != got { + t.Errorf("want swap device %s, got %s", want, got) + } + + if want, got := "partition", swapInfo[0].Type; want != got { + t.Errorf("want swap type %s, got %s", want, got) + } + + if want, got := 100, swapInfo[0].Priority; want != got { + t.Errorf("want swap priority %d, got %d", want, got) + } + + if want, got := 8388604, swapInfo[0].Size; want != got { + t.Errorf("want swap size %d, got %d", want, got) + } + + if want, got := 76, swapInfo[0].Used; want != got { + t.Errorf("want swpa used %d, got %d", want, got) + } +} diff --git a/end-to-end-test.sh b/end-to-end-test.sh index 0bd988ef..ac786134 100755 --- a/end-to-end-test.sh +++ b/end-to-end-test.sh @@ -365,6 +365,33 @@ non_deterministic_metrics=$(cat << METRICS node_network_receive_bytes_total node_network_receive_multicast_total node_network_transmit_multicast_total + node_zfs_abdstats_linear_count_total + node_zfs_abdstats_linear_data_bytes + node_zfs_abdstats_scatter_chunk_waste_bytes + node_zfs_abdstats_scatter_count_total + node_zfs_abdstats_scatter_data_bytes + node_zfs_abdstats_struct_bytes + node_zfs_arcstats_anon_bytes + node_zfs_arcstats_c_bytes + node_zfs_arcstats_c_max_bytes + node_zfs_arcstats_data_bytes + node_zfs_arcstats_demand_data_hits_total + node_zfs_arcstats_demand_data_misses_total + node_zfs_arcstats_demand_metadata_hits_total + node_zfs_arcstats_demand_metadata_misses_total + node_zfs_arcstats_hdr_bytes + node_zfs_arcstats_hits_total + node_zfs_arcstats_meta_bytes + node_zfs_arcstats_mfu_bytes + node_zfs_arcstats_mfu_ghost_hits_total + node_zfs_arcstats_misses_total + node_zfs_arcstats_mru_bytes + node_zfs_arcstats_mru_ghost_bytes + node_zfs_arcstats_other_bytes + node_zfs_arcstats_pd_bytes + node_zfs_arcstats_size_bytes + node_zfs_zfetchstats_hits_total + node_zfs_zfetchstats_misses_total METRICS ) diff --git a/go.mod b/go.mod index 9a4a2f5d..f6264284 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/alecthomas/kingpin/v2 v2.4.0 github.com/beevik/ntp v1.4.3 github.com/coreos/go-systemd/v22 v22.6.0 - github.com/dennwc/btrfs v0.0.0-20240418142341-0167142bde7a + github.com/dennwc/btrfs v0.0.0-20241002142654-12ae127e0bf6 github.com/ema/qdisc v1.0.0 github.com/godbus/dbus/v5 v5.1.0 github.com/hashicorp/go-envparse v0.1.0 @@ -27,7 +27,7 @@ require ( github.com/prometheus/exporter-toolkit v0.14.1 github.com/prometheus/procfs v0.17.0 github.com/safchain/ethtool v0.6.2 - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 + golang.org/x/exp v0.0.0-20250911091902-df9299821621 golang.org/x/sys v0.36.0 howett.net/plist v1.0.1 ) @@ -53,7 +53,7 @@ require ( golang.org/x/crypto v0.41.0 // indirect golang.org/x/net v0.43.0 // indirect golang.org/x/oauth2 v0.30.0 // indirect - golang.org/x/sync v0.16.0 // indirect + golang.org/x/sync v0.17.0 // indirect golang.org/x/text v0.28.0 // indirect google.golang.org/protobuf v1.36.8 // indirect ) diff --git a/go.sum b/go.sum index d85a8039..2c0cf80d 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,8 @@ github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/dennwc/btrfs v0.0.0-20240418142341-0167142bde7a h1:KfFsGLJFVdCXlySUkV2FmxNtmiztpJb6tV+XYBmmv8E= -github.com/dennwc/btrfs v0.0.0-20240418142341-0167142bde7a/go.mod h1:MYsOV9Dgsec3FFSOjywi0QK5r6TeBbdWxdrMGtiYXHA= +github.com/dennwc/btrfs v0.0.0-20241002142654-12ae127e0bf6 h1:fV+JlCY0cCJh3l0jfE7iB3ZmrdfJSgfcjdrCQhPokGg= +github.com/dennwc/btrfs v0.0.0-20241002142654-12ae127e0bf6/go.mod h1:MYsOV9Dgsec3FFSOjywi0QK5r6TeBbdWxdrMGtiYXHA= github.com/dennwc/ioctl v1.0.0 h1:DsWAAjIxRqNcLn9x6mwfuf2pet3iB7aK90K4tF16rLg= github.com/dennwc/ioctl v1.0.0/go.mod h1:ellh2YB5ldny99SBU/VX7Nq0xiZbHphf1DrtHxxjMk0= github.com/ema/qdisc v1.0.0 h1:EHLG08FVRbWLg8uRICa3xzC9Zm0m7HyMHfXobWFnXYg= @@ -105,14 +105,14 @@ go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU= +golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20211031064116-611d5d643895/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=