diff --git a/.gitignore b/.gitignore
index 584f5cfd..7047eaac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,9 +4,20 @@ VERSION
*.un~
output/
.vscode
-.idea
*.syso
installer/*.msi
installer/*.wixpdb
local/
-!.idea/inspectionProfiles/Project_Default.xml
+
+/.idea/*
+!/.idea/inspectionProfiles/
+/.idea/inspectionProfiles/*
+!/.idea/inspectionProfiles/Project_Default.xml
+!/.idea/dictionaries/
+/.idea/dictionaries/*
+!/.idea/dictionaries/project.xml
+/.idea/copyright/*
+!/.idea/copyright/profiles_settings.xml
+!/.idea/copyright/windows_exporter.xml
+!/.idea/vcs.xml
+!/.idea/go.imports.xml
diff --git a/.golangci.yaml b/.golangci.yaml
index 58be63c0..7f83057b 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -4,6 +4,7 @@ linters:
disable:
- cyclop
- depguard
+ - dogsled
- dupl
- err113
- exhaustive
@@ -56,6 +57,11 @@ linters:
excludes:
- G101
- G115
+ govet:
+ enable-all: true
+ disable:
+ - fieldalignment
+ - shadow
sloglint:
no-mixed-args: true
kv-only: false
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 00000000..93d50412
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copyright/windows_exporter.xml b/.idea/copyright/windows_exporter.xml
new file mode 100644
index 00000000..9c29b697
--- /dev/null
+++ b/.idea/copyright/windows_exporter.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml
new file mode 100644
index 00000000..1ea1cf2a
--- /dev/null
+++ b/.idea/dictionaries/project.xml
@@ -0,0 +1,7 @@
+
+
+
+ spdx
+
+
+
\ No newline at end of file
diff --git a/.idea/go.imports.xml b/.idea/go.imports.xml
new file mode 100644
index 00000000..8e1cbe42
--- /dev/null
+++ b/.idea/go.imports.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 00000000..ca3c807f
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 00000000..35eb1ddf
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/collector.container.md b/docs/collector.container.md
index d16ca01c..ae974303 100644
--- a/docs/collector.container.md
+++ b/docs/collector.container.md
@@ -5,7 +5,7 @@ The container collector exposes metrics about containers running on a Hyper-V sy
|||
-|-
Metric name prefix | `container`
-Data source | [hcsshim](https://github.com/Microsoft/hcsshim)
+Data source | [HCS](https://learn.microsoft.com/en-us/virtualization/api/hcs/overview)
Enabled by default? | No
## Flags
@@ -14,26 +14,26 @@ None
## Metrics
-Name | Description | Type | Labels
------|-------------|------|-------
-`windows_container_available` | Available | counter | `container_id`
-`windows_container_count` | Number of containers | gauge | `container_id`
-`windows_container_cpu_usage_seconds_kernelmode` | Run time in Kernel mode in Seconds | counter | `container_id`
-`windows_container_cpu_usage_seconds_usermode` | Run Time in User mode in Seconds | counter | `container_id`
-`windows_container_cpu_usage_seconds_total` | Total Run time in Seconds | counter | `container_id`
-`windows_container_memory_usage_commit_bytes` | Memory Usage Commit Bytes | gauge | `container_id`
-`windows_container_memory_usage_commit_peak_bytes` | Memory Usage Commit Peak Bytes | gauge | `container_id`
-`windows_container_memory_usage_private_working_set_bytes` | Memory Usage Private Working Set Bytes | gauge | `container_id`
-`windows_container_network_receive_bytes_total` | Bytes Received on Interface | counter | `container_id`, `interface`
-`windows_container_network_receive_packets_total` | Packets Received on Interface | counter | `container_id`, `interface`
-`windows_container_network_receive_packets_dropped_total` | Dropped Incoming Packets on Interface | counter | `container_id`, `interface`
-`windows_container_network_transmit_bytes_total` | Bytes Sent on Interface | counter | `container_id`, `interface`
-`windows_container_network_transmit_packets_total` | Packets Sent on Interface | counter | `container_id`, `interface`
-`windows_container_network_transmit_packets_dropped_total` | Dropped Outgoing Packets on Interface | counter | `container_id`, `interface`
-`windows_container_storage_read_count_normalized_total` | Read Count Normalized | counter | `container_id`
-`windows_container_storage_read_size_bytes_total` | Read Size Bytes | counter | `container_id`
-`windows_container_storage_write_count_normalized_total` | Write Count Normalized | counter | `container_id`
-`windows_container_storage_write_size_bytes_total` | Write Size Bytes | counter | `container_id`
+| Name | Description | Type | Labels |
+|------------------------------------------------------------|----------------------------------------|---------|----------------------------------------------------------|
+| `windows_container_available` | Available | counter | `container_id`,`namespace`,`pod`,`container`, |
+| `windows_container_count` | Number of containers | gauge | `container_id`,`namespace`,`pod`,`container`, |
+| `windows_container_cpu_usage_seconds_kernelmode` | Run time in Kernel mode in Seconds | counter | `container_id`,`namespace`,`pod`,`container`, |
+| `windows_container_cpu_usage_seconds_usermode` | Run Time in User mode in Seconds | counter | `container_id`,`namespace`,`pod`,`container`, |
+| `windows_container_cpu_usage_seconds_total` | Total Run time in Seconds | counter | `container_id`,`namespace`,`pod`,`container`, |
+| `windows_container_memory_usage_commit_bytes` | Memory Usage Commit Bytes | gauge | `container_id`,`namespace`,`pod`,`container`, |
+| `windows_container_memory_usage_commit_peak_bytes` | Memory Usage Commit Peak Bytes | gauge | `container_id`,`namespace`,`pod`,`container`, |
+| `windows_container_memory_usage_private_working_set_bytes` | Memory Usage Private Working Set Bytes | gauge | `container_id`,`namespace`,`pod`,`container`, |
+| `windows_container_network_receive_bytes_total` | Bytes Received on Interface | counter | `container_id`,`namespace`,`pod`,`container`,`interface` |
+| `windows_container_network_receive_packets_total` | Packets Received on Interface | counter | `container_id`,`namespace`,`pod`,`container`,`interface` |
+| `windows_container_network_receive_packets_dropped_total` | Dropped Incoming Packets on Interface | counter | `container_id`,`namespace`,`pod`,`container`,`interface` |
+| `windows_container_network_transmit_bytes_total` | Bytes Sent on Interface | counter | `container_id`,`namespace`,`pod`,`container`,`interface` |
+| `windows_container_network_transmit_packets_total` | Packets Sent on Interface | counter | `container_id`,`namespace`,`pod`,`container`,`interface` |
+| `windows_container_network_transmit_packets_dropped_total` | Dropped Outgoing Packets on Interface | counter | `container_id`,`namespace`,`pod`,`container`,`interface` |
+| `windows_container_storage_read_count_normalized_total` | Read Count Normalized | counter | `container_id`,`namespace`,`pod`,`container`, |
+| `windows_container_storage_read_size_bytes_total` | Read Size Bytes | counter | `container_id`,`namespace`,`pod`,`container`, |
+| `windows_container_storage_write_count_normalized_total` | Write Count Normalized | counter | `container_id`,`namespace`,`pod`,`container`, |
+| `windows_container_storage_write_size_bytes_total` | Write Size Bytes | counter | `container_id`,`namespace`,`pod`,`container`, |
### Example metric
_windows_container_network_receive_bytes_total{container_id="docker://1bd30e8b8ac28cbd76a9b697b4d7bb9d760267b0733d1bc55c60024e98d1e43e",interface="822179E7-002C-4280-ABBA-28BCFE401826"} 9.3305343e+07_
diff --git a/go.mod b/go.mod
index 98fdc4c0..78ed1b01 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,6 @@ module github.com/prometheus-community/windows_exporter
go 1.24
require (
- github.com/Microsoft/hcsshim v0.13.0
github.com/alecthomas/kingpin/v2 v2.4.0
github.com/bmatcuk/doublestar/v4 v4.8.1
github.com/dimchansky/utfbom v1.1.1
@@ -18,36 +17,24 @@ require (
)
require (
- github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
- github.com/containerd/cgroups/v3 v3.0.5 // indirect
- github.com/containerd/errdefs v1.0.0 // indirect
- github.com/containerd/errdefs/pkg v0.3.0 // indirect
- github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/gogo/protobuf v1.3.2 // indirect
- github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/mdlayher/socket v0.5.1 // indirect
github.com/mdlayher/vsock v1.2.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
- github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
- github.com/sirupsen/logrus v1.9.3 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
- go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/net v0.40.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/text v0.25.0 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 // indirect
- google.golang.org/grpc v1.72.1 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
diff --git a/go.sum b/go.sum
index c8b9cc9f..329804b7 100644
--- a/go.sum
+++ b/go.sum
@@ -1,9 +1,3 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
-github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
-github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA=
-github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok=
github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY=
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0=
@@ -12,19 +6,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
-github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
-github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
-github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
-github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
-github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
-github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
-github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -32,44 +15,13 @@ 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/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
-github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
-github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.3/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/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -86,14 +38,11 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.64.0 h1:pdZeA+g617P7oGv1CzdTzyeShxAGrTBsolKNOLQPGO4=
@@ -104,105 +53,31 @@ github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzM
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
-github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
-github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
-go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
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.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9 h1:IkAfh6J/yllPtpYFU0zZN1hUPYdT0ogkBT/9hMxHjvg=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20250512202823-5a2f75b736a9/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
-google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -213,5 +88,3 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/internal/collector/container/container.go b/internal/collector/container/container.go
index b119d71a..90407875 100644
--- a/internal/collector/container/container.go
+++ b/internal/collector/container/container.go
@@ -18,25 +18,48 @@
package container
import (
+ "encoding/json"
"errors"
"fmt"
+ "io/fs"
"log/slog"
+ "os"
+ "slices"
"strings"
+ "unsafe"
- "github.com/Microsoft/hcsshim"
"github.com/alecthomas/kingpin/v2"
+ "github.com/prometheus-community/windows_exporter/internal/headers/guid"
+ "github.com/prometheus-community/windows_exporter/internal/headers/hcn"
+ "github.com/prometheus-community/windows_exporter/internal/headers/hcs"
+ "github.com/prometheus-community/windows_exporter/internal/headers/iphlpapi"
+ "github.com/prometheus-community/windows_exporter/internal/headers/kernel32"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/pdh"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
+ "golang.org/x/sys/windows"
)
-const Name = "container"
+const (
+ Name = "container"
-type Config struct{}
+ subCollectorHCS = "hcs"
+ subCollectorHostprocess = "hostprocess"
+ containerDStateDir = `C:\ProgramData\containerd\state\io.containerd.runtime.v2.task\k8s.io\`
+)
+
+type Config struct {
+ CollectorsEnabled []string `yaml:"collectors_enabled"`
+}
//nolint:gochecknoglobals
-var ConfigDefaults = Config{}
+var ConfigDefaults = Config{
+ CollectorsEnabled: []string{
+ subCollectorHCS,
+ subCollectorHostprocess,
+ },
+}
// A Collector is a Prometheus Collector for containers metrics.
type Collector struct {
@@ -44,6 +67,9 @@ type Collector struct {
logger *slog.Logger
+ annotationsCacheHCS map[string]containerInfo
+ annotationsCacheJob map[string]containerInfo
+
// Presence
containerAvailable *prometheus.Desc
@@ -75,12 +101,27 @@ type Collector struct {
writeSizeBytes *prometheus.Desc
}
+type containerInfo struct {
+ id string
+ namespace string
+ pod string
+ container string
+}
+
+type ociSpec struct {
+ Annotations map[string]string `json:"annotations"`
+}
+
// New constructs a new Collector.
func New(config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
+ if config.CollectorsEnabled == nil {
+ config.CollectorsEnabled = ConfigDefaults.CollectorsEnabled
+ }
+
c := &Collector{
config: *config,
}
@@ -88,8 +129,26 @@ func New(config *Config) *Collector {
return c
}
-func NewWithFlags(_ *kingpin.Application) *Collector {
- return &Collector{}
+func NewWithFlags(app *kingpin.Application) *Collector {
+ c := &Collector{
+ config: ConfigDefaults,
+ }
+ c.config.CollectorsEnabled = make([]string, 0)
+
+ var collectorsEnabled string
+
+ app.Flag(
+ "collector.container.enabled",
+ "Comma-separated list of collectors to use. Defaults to all, if not specified.",
+ ).Default(strings.Join(ConfigDefaults.CollectorsEnabled, ",")).StringVar(&collectorsEnabled)
+
+ app.Action(func(*kingpin.ParseContext) error {
+ c.config.CollectorsEnabled = strings.Split(collectorsEnabled, ",")
+
+ return nil
+ })
+
+ return c
}
func (c *Collector) GetName() string {
@@ -103,10 +162,16 @@ func (c *Collector) Close() error {
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
c.logger = logger.With(slog.String("collector", Name))
+ for _, collector := range c.config.CollectorsEnabled {
+ if !slices.Contains([]string{subCollectorHCS, subCollectorHostprocess}, collector) {
+ return fmt.Errorf("unknown collector: %s", collector)
+ }
+ }
+
c.containerAvailable = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "available"),
"Available",
- []string{"container_id"},
+ []string{"container_id", "namespace", "pod", "container"},
nil,
)
c.containersCount = prometheus.NewDesc(
@@ -118,97 +183,97 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
c.usageCommitBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "memory_usage_commit_bytes"),
"Memory Usage Commit Bytes",
- []string{"container_id"},
+ []string{"container_id", "namespace", "pod", "container"},
nil,
)
c.usageCommitPeakBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "memory_usage_commit_peak_bytes"),
"Memory Usage Commit Peak Bytes",
- []string{"container_id"},
+ []string{"container_id", "namespace", "pod", "container"},
nil,
)
c.usagePrivateWorkingSetBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "memory_usage_private_working_set_bytes"),
"Memory Usage Private Working Set Bytes",
- []string{"container_id"},
+ []string{"container_id", "namespace", "pod", "container"},
nil,
)
c.runtimeTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "cpu_usage_seconds_total"),
"Total Run time in Seconds",
- []string{"container_id"},
+ []string{"container_id", "namespace", "pod", "container"},
nil,
)
c.runtimeUser = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "cpu_usage_seconds_usermode"),
"Run Time in User mode in Seconds",
- []string{"container_id"},
+ []string{"container_id", "namespace", "pod", "container"},
nil,
)
c.runtimeKernel = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "cpu_usage_seconds_kernelmode"),
"Run time in Kernel mode in Seconds",
- []string{"container_id"},
+ []string{"container_id", "namespace", "pod", "container"},
nil,
)
c.bytesReceived = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "network_receive_bytes_total"),
"Bytes Received on Interface",
- []string{"container_id", "interface"},
+ []string{"container_id", "namespace", "pod", "container", "interface"},
nil,
)
c.bytesSent = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "network_transmit_bytes_total"),
"Bytes Sent on Interface",
- []string{"container_id", "interface"},
+ []string{"container_id", "namespace", "pod", "container", "interface"},
nil,
)
c.packetsReceived = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "network_receive_packets_total"),
"Packets Received on Interface",
- []string{"container_id", "interface"},
+ []string{"container_id", "namespace", "pod", "container", "interface"},
nil,
)
c.packetsSent = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "network_transmit_packets_total"),
"Packets Sent on Interface",
- []string{"container_id", "interface"},
+ []string{"container_id", "namespace", "pod", "container", "interface"},
nil,
)
c.droppedPacketsIncoming = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "network_receive_packets_dropped_total"),
"Dropped Incoming Packets on Interface",
- []string{"container_id", "interface"},
+ []string{"container_id", "namespace", "pod", "container", "interface"},
nil,
)
c.droppedPacketsOutgoing = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "network_transmit_packets_dropped_total"),
"Dropped Outgoing Packets on Interface",
- []string{"container_id", "interface"},
+ []string{"container_id", "namespace", "pod", "container", "interface"},
nil,
)
c.readCountNormalized = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "storage_read_count_normalized_total"),
"Read Count Normalized",
- []string{"container_id"},
+ []string{"container_id", "namespace", "pod", "container"},
nil,
)
c.readSizeBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "storage_read_size_bytes_total"),
"Read Size Bytes",
- []string{"container_id"},
+ []string{"container_id", "namespace", "pod", "container"},
nil,
)
c.writeCountNormalized = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "storage_write_count_normalized_total"),
"Write Count Normalized",
- []string{"container_id"},
+ []string{"container_id", "namespace", "pod", "container"},
nil,
)
c.writeSizeBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "storage_write_size_bytes_total"),
"Write Size Bytes",
- []string{"container_id"},
+ []string{"container_id", "namespace", "pod", "container"},
nil,
)
@@ -218,39 +283,85 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
+ errs := make([]error, 0)
+
+ if slices.Contains(c.config.CollectorsEnabled, subCollectorHCS) {
+ if err := c.collectHCS(ch); err != nil {
+ errs = append(errs, err)
+ }
+ }
+
+ if slices.Contains(c.config.CollectorsEnabled, subCollectorHostprocess) {
+ if err := c.collectJobContainers(ch); err != nil {
+ errs = append(errs, err)
+ }
+ }
+
+ return errors.Join(errs...)
+}
+
+func (c *Collector) collectHCS(ch chan<- prometheus.Metric) error {
// Types Container is passed to get the containers compute systems only
- containers, err := hcsshim.GetContainers(hcsshim.ComputeSystemQuery{Types: []string{"Container"}})
+ containers, err := hcs.GetContainers()
if err != nil {
return fmt.Errorf("error in fetching containers: %w", err)
}
count := len(containers)
-
- ch <- prometheus.MustNewConstMetric(
- c.containersCount,
- prometheus.GaugeValue,
- float64(count),
- )
-
if count == 0 {
+ ch <- prometheus.MustNewConstMetric(
+ c.containersCount,
+ prometheus.GaugeValue,
+ 0,
+ )
+
return nil
}
- containerPrefixes := make(map[string]string)
+ var countersCount float64
+
+ containerIDs := make([]string, 0, len(containers))
collectErrors := make([]error, 0, len(containers))
- for _, containerDetails := range containers {
- containerIdWithPrefix := getContainerIdWithPrefix(containerDetails)
+ for _, container := range containers {
+ if container.State != "Running" {
+ continue
+ }
- if err = c.collectContainer(ch, containerDetails, containerIdWithPrefix); err != nil {
- if hcsshim.IsNotExist(err) {
+ containerIDs = append(containerIDs, container.ID)
+
+ countersCount++
+
+ var (
+ namespace string
+ podName string
+ containerName string
+ )
+
+ if _, ok := c.annotationsCacheHCS[container.ID]; !ok {
+ if spec, err := getContainerAnnotations(container.ID); err == nil {
+ namespace = spec.Annotations["io.kubernetes.cri.sandbox-namespace"]
+ podName = spec.Annotations["io.kubernetes.cri.sandbox-name"]
+ containerName = spec.Annotations["io.kubernetes.cri.container-name"]
+ }
+
+ c.annotationsCacheHCS[container.ID] = containerInfo{
+ id: getContainerIdWithPrefix(container),
+ namespace: namespace,
+ pod: podName,
+ container: containerName,
+ }
+ }
+
+ if err = c.collectHCSContainer(ch, container, c.annotationsCacheHCS[container.ID]); err != nil {
+ if errors.Is(err, hcs.ErrIDNotFound) {
c.logger.Debug("err in fetching container statistics",
- slog.String("container_id", containerDetails.ID),
+ slog.String("container_id", container.ID),
slog.Any("err", err),
)
} else {
c.logger.Error("err in fetching container statistics",
- slog.String("container_id", containerDetails.ID),
+ slog.String("container_id", container.ID),
slog.Any("err", err),
)
@@ -259,14 +370,25 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
continue
}
-
- containerPrefixes[containerDetails.ID] = containerIdWithPrefix
}
- if err = c.collectNetworkMetrics(ch, containerPrefixes); err != nil {
+ ch <- prometheus.MustNewConstMetric(
+ c.containersCount,
+ prometheus.GaugeValue,
+ countersCount,
+ )
+
+ if err = c.collectNetworkMetrics(ch); err != nil {
return fmt.Errorf("error in fetching container network statistics: %w", err)
}
+ // Remove containers that are no longer running
+ for _, containerID := range c.annotationsCacheHCS {
+ if !slices.Contains(containerIDs, containerID.id) {
+ delete(c.annotationsCacheHCS, containerID.id)
+ }
+ }
+
if len(collectErrors) > 0 {
return fmt.Errorf("errors while fetching container statistics: %w", errors.Join(collectErrors...))
}
@@ -274,94 +396,87 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
return nil
}
-func (c *Collector) collectContainer(ch chan<- prometheus.Metric, containerDetails hcsshim.ContainerProperties, containerIdWithPrefix string) error {
- container, err := hcsshim.OpenContainer(containerDetails.ID)
+func (c *Collector) collectHCSContainer(ch chan<- prometheus.Metric, containerDetails hcs.Properties, containerInfo containerInfo) error {
+ containerStats, err := hcs.GetContainerStatistics(containerDetails.ID)
if err != nil {
- return fmt.Errorf("error in opening container: %w", err)
- }
-
- defer func() {
- if container == nil {
- return
- }
-
- if err := container.Close(); err != nil {
- c.logger.Error("error in closing container",
- slog.Any("err", err),
- )
- }
- }()
-
- containerStats, err := container.Statistics()
- if err != nil {
- return fmt.Errorf("error in fetching container statistics: %w", err)
+ return fmt.Errorf("error fetching container statistics: %w", err)
}
ch <- prometheus.MustNewConstMetric(
c.containerAvailable,
prometheus.CounterValue,
1,
- containerIdWithPrefix,
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
)
ch <- prometheus.MustNewConstMetric(
c.usageCommitBytes,
prometheus.GaugeValue,
- float64(containerStats.Memory.UsageCommitBytes),
- containerIdWithPrefix,
+ float64(containerStats.Memory.MemoryUsageCommitBytes),
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
)
ch <- prometheus.MustNewConstMetric(
c.usageCommitPeakBytes,
prometheus.GaugeValue,
- float64(containerStats.Memory.UsageCommitPeakBytes),
- containerIdWithPrefix,
+ float64(containerStats.Memory.MemoryUsageCommitPeakBytes),
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
)
ch <- prometheus.MustNewConstMetric(
c.usagePrivateWorkingSetBytes,
prometheus.GaugeValue,
- float64(containerStats.Memory.UsagePrivateWorkingSetBytes),
- containerIdWithPrefix,
+ float64(containerStats.Memory.MemoryUsagePrivateWorkingSetBytes),
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
)
ch <- prometheus.MustNewConstMetric(
c.runtimeTotal,
prometheus.CounterValue,
float64(containerStats.Processor.TotalRuntime100ns)*pdh.TicksToSecondScaleFactor,
- containerIdWithPrefix,
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
)
ch <- prometheus.MustNewConstMetric(
c.runtimeUser,
prometheus.CounterValue,
float64(containerStats.Processor.RuntimeUser100ns)*pdh.TicksToSecondScaleFactor,
- containerIdWithPrefix,
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
)
ch <- prometheus.MustNewConstMetric(
c.runtimeKernel,
prometheus.CounterValue,
float64(containerStats.Processor.RuntimeKernel100ns)*pdh.TicksToSecondScaleFactor,
- containerIdWithPrefix,
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
)
ch <- prometheus.MustNewConstMetric(
c.readCountNormalized,
prometheus.CounterValue,
float64(containerStats.Storage.ReadCountNormalized),
- containerIdWithPrefix,
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
)
ch <- prometheus.MustNewConstMetric(
c.readSizeBytes,
prometheus.CounterValue,
float64(containerStats.Storage.ReadSizeBytes),
- containerIdWithPrefix,
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
)
ch <- prometheus.MustNewConstMetric(
c.writeCountNormalized,
prometheus.CounterValue,
float64(containerStats.Storage.WriteCountNormalized),
- containerIdWithPrefix,
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
)
ch <- prometheus.MustNewConstMetric(
c.writeSizeBytes,
prometheus.CounterValue,
float64(containerStats.Storage.WriteSizeBytes),
- containerIdWithPrefix,
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
)
return nil
@@ -371,73 +486,105 @@ func (c *Collector) collectContainer(ch chan<- prometheus.Metric, containerDetai
// With HNSv2, the network stats must be collected from hcsshim.HNSListEndpointRequest.
// Network statistics from the container.Statistics() are providing data only, if HNSv1 is used.
// Ref: https://github.com/prometheus-community/windows_exporter/pull/1218
-func (c *Collector) collectNetworkMetrics(ch chan<- prometheus.Metric, containerPrefixes map[string]string) error {
- hnsEndpoints, err := hcsshim.HNSListEndpointRequest()
+func (c *Collector) collectNetworkMetrics(ch chan<- prometheus.Metric) error {
+ endpoints, err := hcn.EnumerateEndpoints()
if err != nil {
- return fmt.Errorf("error in fetching HNS endpoints: %w", err)
+ return fmt.Errorf("error in fetching HCN endpoints: %w", err)
}
- if len(hnsEndpoints) == 0 {
- return errors.New("no network stats for containers to collect")
+ if len(endpoints) == 0 {
+ return nil
}
- for _, endpoint := range hnsEndpoints {
- endpointStats, err := hcsshim.GetHNSEndpointStats(endpoint.Id)
+ for _, endpoint := range endpoints {
+ properties, err := hcn.GetEndpointProperties(endpoint)
if err != nil {
- c.logger.Warn("Failed to collect network stats for interface "+endpoint.Id,
+ c.logger.Warn("Failed to collect properties for interface "+endpoint.String(),
slog.Any("err", err),
)
continue
}
- for _, containerId := range endpoint.SharedContainers {
- containerIdWithPrefix, ok := containerPrefixes[containerId]
+ var nicGUID *guid.GUID
+
+ for _, allocator := range properties.Resources.Allocators {
+ if allocator.AdapterNetCfgInstanceId != nil {
+ nicGUID = allocator.AdapterNetCfgInstanceId
+
+ break
+ }
+ }
+
+ if nicGUID == nil {
+ c.logger.Warn("Failed to get nic GUID for endpoint " + endpoint.String())
+
+ continue
+ }
+
+ luid, err := iphlpapi.ConvertInterfaceGUIDToLUID(*nicGUID)
+ if err != nil {
+ return fmt.Errorf("error in converting interface GUID to LUID: %w", err)
+ }
+
+ var endpointStats iphlpapi.MIB_IF_ROW2
+ endpointStats.InterfaceLuid = luid
+
+ if err := iphlpapi.GetIfEntry2Ex(&endpointStats); err != nil {
+ c.logger.Warn("Failed to get interface entry for endpoint "+endpoint.String(),
+ slog.Any("err", err),
+ )
+
+ continue
+ }
+
+ for _, containerId := range properties.SharedContainers {
+ containerInfo, ok := c.annotationsCacheHCS[containerId]
if !ok {
- c.logger.Debug("Failed to collect network stats for container " + containerId)
+ c.logger.Debug("Unknown container " + containerId + " for endpoint " + endpoint.String())
continue
}
- endpointId := strings.ToUpper(endpoint.Id)
+ endpointId := strings.ToUpper(endpoint.String())
ch <- prometheus.MustNewConstMetric(
c.bytesReceived,
prometheus.CounterValue,
- float64(endpointStats.BytesReceived),
- containerIdWithPrefix, endpointId,
+ float64(endpointStats.InOctets),
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
)
ch <- prometheus.MustNewConstMetric(
c.bytesSent,
prometheus.CounterValue,
- float64(endpointStats.BytesSent),
- containerIdWithPrefix, endpointId,
+ float64(endpointStats.OutOctets),
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
)
ch <- prometheus.MustNewConstMetric(
c.packetsReceived,
prometheus.CounterValue,
- float64(endpointStats.PacketsReceived),
- containerIdWithPrefix, endpointId,
+ float64(endpointStats.InUcastPkts+endpointStats.InNUcastPkts),
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
)
ch <- prometheus.MustNewConstMetric(
c.packetsSent,
prometheus.CounterValue,
- float64(endpointStats.PacketsSent),
- containerIdWithPrefix, endpointId,
+ float64(endpointStats.OutUcastPkts+endpointStats.OutNUcastPkts),
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
)
ch <- prometheus.MustNewConstMetric(
c.droppedPacketsIncoming,
prometheus.CounterValue,
- float64(endpointStats.DroppedPacketsIncoming),
- containerIdWithPrefix, endpointId,
+ float64(endpointStats.InDiscards+endpointStats.InErrors),
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
)
ch <- prometheus.MustNewConstMetric(
c.droppedPacketsOutgoing,
prometheus.CounterValue,
- float64(endpointStats.DroppedPacketsOutgoing),
- containerIdWithPrefix, endpointId,
+ float64(endpointStats.OutDiscards+endpointStats.OutErrors),
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container, endpointId,
)
}
}
@@ -445,12 +592,286 @@ func (c *Collector) collectNetworkMetrics(ch chan<- prometheus.Metric, container
return nil
}
-func getContainerIdWithPrefix(containerDetails hcsshim.ContainerProperties) string {
- switch containerDetails.Owner {
+// collectJobContainers collects container metrics for job containers.
+// Job container based on Win32 Job objects.
+// https://learn.microsoft.com/en-us/windows/win32/procthread/job-objects
+//
+// Job containers are containers that aren't managed by HCS, e.g host process containers.
+func (c *Collector) collectJobContainers(ch chan<- prometheus.Metric) error {
+ containerDStateFS := os.DirFS(containerDStateDir)
+
+ allContainerIDs := make([]string, 0, len(c.annotationsCacheJob)+len(c.annotationsCacheHCS))
+ jobContainerIDs := make([]string, 0, len(allContainerIDs))
+
+ if err := fs.WalkDir(containerDStateFS, ".", func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ if errors.Is(err, fs.ErrNotExist) {
+ c.logger.Warn("containerd state directory does not exist",
+ slog.String("path", containerDStateDir),
+ slog.Any("err", err),
+ )
+
+ return nil
+ }
+
+ return err
+ }
+
+ if !d.IsDir() {
+ return nil
+ }
+
+ if _, err := os.Stat(path + "\\config.json"); err != nil {
+ containerID := strings.TrimPrefix(strings.Replace(path, containerDStateDir, "", 1), `\`)
+ allContainerIDs = append(allContainerIDs, containerID)
+ }
+
+ // Skip the directory content
+ return fs.SkipDir
+ }); err != nil {
+ return fmt.Errorf("error in walking containerd state directory: %w", err)
+ }
+
+ errs := make([]error, 0)
+
+ for _, containerID := range allContainerIDs {
+ if err := c.collectJobContainer(ch, containerID); err != nil {
+ errs = append(errs, err)
+ } else {
+ jobContainerIDs = append(jobContainerIDs, containerID)
+ }
+ }
+
+ // Remove containers that are no longer running
+ for _, containerID := range c.annotationsCacheJob {
+ if !slices.Contains(jobContainerIDs, containerID.id) {
+ delete(c.annotationsCacheJob, containerID.id)
+ }
+ }
+
+ return errors.Join(errs...)
+}
+
+func (c *Collector) collectJobContainer(ch chan<- prometheus.Metric, containerID string) error {
+ jobObjectHandle, err := kernel32.OpenJobObject("JobContainer_" + containerID)
+ if err != nil {
+ if errors.Is(err, windows.ERROR_FILE_NOT_FOUND) {
+ return nil
+ }
+
+ return fmt.Errorf("error in opening job object: %w", err)
+ }
+
+ defer func(fd windows.Handle) {
+ _ = windows.Close(fd)
+ }(jobObjectHandle)
+
+ if _, ok := c.annotationsCacheJob[containerID]; !ok {
+ var (
+ namespace string
+ podName string
+ containerName string
+ )
+
+ if spec, err := getContainerAnnotations(containerID); err == nil {
+ namespace = spec.Annotations["io.kubernetes.cri.sandbox-namespace"]
+ podName = spec.Annotations["io.kubernetes.cri.sandbox-name"]
+ containerName = spec.Annotations["io.kubernetes.cri.container-name"]
+ }
+
+ c.annotationsCacheJob[containerID] = containerInfo{
+ id: "containerd://" + containerID,
+ namespace: namespace,
+ pod: podName,
+ container: containerName,
+ }
+ }
+
+ var jobInfo kernel32.JobObjectExtendedLimitInformation
+
+ retLen := uint32(unsafe.Sizeof(jobInfo))
+
+ if err := windows.QueryInformationJobObject(
+ jobObjectHandle,
+ windows.JobObjectExtendedLimitInformation,
+ uintptr(unsafe.Pointer(&jobInfo)),
+ retLen, &retLen); err != nil {
+ return err
+ }
+
+ privateWorkingSetBytes, err := calculatePrivateWorkingSetBytes(jobObjectHandle)
+ if err != nil {
+ c.logger.Debug("error in calculating private working set bytes", slog.Any("err", err))
+ }
+
+ containerInfo := c.annotationsCacheJob[containerID]
+
+ ch <- prometheus.MustNewConstMetric(
+ c.containerAvailable,
+ prometheus.CounterValue,
+ 1,
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
+ )
+ ch <- prometheus.MustNewConstMetric(
+ c.usageCommitBytes,
+ prometheus.GaugeValue,
+ float64(jobInfo.JobMemoryLimit),
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
+ )
+ ch <- prometheus.MustNewConstMetric(
+ c.usageCommitPeakBytes,
+ prometheus.GaugeValue,
+ float64(jobInfo.PeakProcessMemoryUsed),
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
+ )
+ ch <- prometheus.MustNewConstMetric(
+ c.usagePrivateWorkingSetBytes,
+ prometheus.GaugeValue,
+ float64(privateWorkingSetBytes),
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
+ )
+ ch <- prometheus.MustNewConstMetric(
+ c.runtimeTotal,
+ prometheus.CounterValue,
+ (float64(jobInfo.BasicInfo.ThisPeriodTotalKernelTime)+float64(jobInfo.BasicInfo.ThisPeriodTotalUserTime))*pdh.TicksToSecondScaleFactor,
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
+ )
+ ch <- prometheus.MustNewConstMetric(
+ c.runtimeUser,
+ prometheus.CounterValue,
+ float64(jobInfo.BasicInfo.ThisPeriodTotalUserTime)*pdh.TicksToSecondScaleFactor,
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
+ )
+ ch <- prometheus.MustNewConstMetric(
+ c.runtimeKernel,
+ prometheus.CounterValue,
+ float64(jobInfo.BasicInfo.ThisPeriodTotalKernelTime)*pdh.TicksToSecondScaleFactor,
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
+ )
+ ch <- prometheus.MustNewConstMetric(
+ c.readCountNormalized,
+ prometheus.CounterValue,
+ float64(jobInfo.IoInfo.ReadOperationCount),
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
+ )
+ ch <- prometheus.MustNewConstMetric(
+ c.readSizeBytes,
+ prometheus.CounterValue,
+ float64(jobInfo.IoInfo.ReadTransferCount),
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
+ )
+ ch <- prometheus.MustNewConstMetric(
+ c.writeCountNormalized,
+ prometheus.CounterValue,
+ float64(jobInfo.IoInfo.WriteOperationCount),
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
+ )
+ ch <- prometheus.MustNewConstMetric(
+ c.writeSizeBytes,
+ prometheus.CounterValue,
+ float64(jobInfo.IoInfo.WriteTransferCount),
+
+ containerInfo.id, containerInfo.namespace, containerInfo.pod, containerInfo.container,
+ )
+
+ return nil
+}
+
+func getContainerIdWithPrefix(container hcs.Properties) string {
+ switch container.Owner {
case "containerd-shim-runhcs-v1.exe":
- return "containerd://" + containerDetails.ID
+ return "containerd://" + container.ID
default:
// default to docker or if owner is not set
- return "docker://" + containerDetails.ID
+ return "docker://" + container.ID
}
}
+
+func getContainerAnnotations(containerID string) (ociSpec, error) {
+ configJSON, err := os.OpenFile(containerDStateDir+containerID+`\config.json`, os.O_RDONLY, 0)
+ if err != nil {
+ return ociSpec{}, fmt.Errorf("error in opening config.json file: %w", err)
+ }
+
+ var annotations ociSpec
+
+ if err = json.NewDecoder(configJSON).Decode(&annotations); err != nil {
+ return ociSpec{}, fmt.Errorf("error in decoding config.json file: %w", err)
+ }
+
+ return annotations, nil
+}
+
+func calculatePrivateWorkingSetBytes(jobObjectHandle windows.Handle) (uint64, error) {
+ var pidList kernel32.JobObjectBasicProcessIDList
+
+ retLen := uint32(unsafe.Sizeof(pidList))
+
+ if err := windows.QueryInformationJobObject(
+ jobObjectHandle,
+ windows.JobObjectBasicProcessIdList,
+ uintptr(unsafe.Pointer(&pidList)),
+ retLen, &retLen); err != nil {
+ return 0, err
+ }
+
+ var (
+ privateWorkingSetBytes uint64
+ vmCounters kernel32.PROCESS_VM_COUNTERS
+ )
+
+ retLen = uint32(unsafe.Sizeof(vmCounters))
+
+ getPrivateWorkingSetBytes := func(pid uint32) (uint64, error) {
+ processHandle, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, pid)
+ if err != nil {
+ return 0, fmt.Errorf("error in opening process: %w", err)
+ }
+
+ defer func(fd windows.Handle) {
+ _ = windows.Close(fd)
+ }(processHandle)
+
+ var isInJob bool
+
+ if err := kernel32.IsProcessInJob(processHandle, jobObjectHandle, &isInJob); err != nil {
+ return 0, fmt.Errorf("error in checking if process is in job: %w", err)
+ }
+
+ if !isInJob {
+ return 0, nil
+ }
+
+ if err := windows.NtQueryInformationProcess(
+ processHandle,
+ windows.ProcessVmCounters,
+ unsafe.Pointer(&vmCounters),
+ retLen,
+ &retLen,
+ ); err != nil {
+ return 0, fmt.Errorf("error in querying process information: %w", err)
+ }
+
+ return uint64(vmCounters.PrivateWorkingSetSize), nil
+ }
+
+ for _, pid := range pidList.PIDs() {
+ privateWorkingSetSize, err := getPrivateWorkingSetBytes(pid)
+ if err != nil {
+ return 0, fmt.Errorf("error in getting private working set bytes: %w", err)
+ }
+
+ privateWorkingSetBytes += privateWorkingSetSize
+ }
+
+ return privateWorkingSetBytes, nil
+}
diff --git a/internal/collector/hyperv/hyperv.go b/internal/collector/hyperv/hyperv.go
index 7ec3611d..56ca36c9 100644
--- a/internal/collector/hyperv/hyperv.go
+++ b/internal/collector/hyperv/hyperv.go
@@ -25,9 +25,9 @@ import (
"strings"
"sync"
- "github.com/Microsoft/hcsshim/osversion"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
+ "github.com/prometheus-community/windows_exporter/internal/osversion"
"github.com/prometheus/client_golang/prometheus"
)
diff --git a/internal/collector/hyperv/hyperv_dynamic_memory_balancer.go b/internal/collector/hyperv/hyperv_dynamic_memory_balancer.go
index 8849f93d..54aeca46 100644
--- a/internal/collector/hyperv/hyperv_dynamic_memory_balancer.go
+++ b/internal/collector/hyperv/hyperv_dynamic_memory_balancer.go
@@ -20,7 +20,7 @@ package hyperv
import (
"fmt"
- "github.com/Microsoft/hcsshim/osversion"
+ "github.com/prometheus-community/windows_exporter/internal/osversion"
"github.com/prometheus-community/windows_exporter/internal/pdh"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
diff --git a/internal/collector/hyperv/hyperv_dynamic_memory_vm.go b/internal/collector/hyperv/hyperv_dynamic_memory_vm.go
index b9e3fc16..b4b06698 100644
--- a/internal/collector/hyperv/hyperv_dynamic_memory_vm.go
+++ b/internal/collector/hyperv/hyperv_dynamic_memory_vm.go
@@ -20,7 +20,7 @@ package hyperv
import (
"fmt"
- "github.com/Microsoft/hcsshim/osversion"
+ "github.com/prometheus-community/windows_exporter/internal/osversion"
"github.com/prometheus-community/windows_exporter/internal/pdh"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
diff --git a/internal/collector/mscluster/mscluster_cluster.go b/internal/collector/mscluster/mscluster_cluster.go
index ca91e322..648b2ea5 100644
--- a/internal/collector/mscluster/mscluster_cluster.go
+++ b/internal/collector/mscluster/mscluster_cluster.go
@@ -20,8 +20,8 @@ package mscluster
import (
"fmt"
- "github.com/Microsoft/hcsshim/osversion"
"github.com/prometheus-community/windows_exporter/internal/mi"
+ "github.com/prometheus-community/windows_exporter/internal/osversion"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
diff --git a/internal/collector/mscluster/mscluster_node.go b/internal/collector/mscluster/mscluster_node.go
index f8ae3a0f..c5de0b89 100644
--- a/internal/collector/mscluster/mscluster_node.go
+++ b/internal/collector/mscluster/mscluster_node.go
@@ -20,8 +20,8 @@ package mscluster
import (
"fmt"
- "github.com/Microsoft/hcsshim/osversion"
"github.com/prometheus-community/windows_exporter/internal/mi"
+ "github.com/prometheus-community/windows_exporter/internal/osversion"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
diff --git a/internal/collector/os/os.go b/internal/collector/os/os.go
index 25d4ee4e..d84be008 100644
--- a/internal/collector/os/os.go
+++ b/internal/collector/os/os.go
@@ -30,6 +30,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/headers/netapi32"
"github.com/prometheus-community/windows_exporter/internal/headers/sysinfoapi"
"github.com/prometheus-community/windows_exporter/internal/mi"
+ "github.com/prometheus-community/windows_exporter/internal/osversion"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/sys/windows"
@@ -118,10 +119,10 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
return fmt.Errorf("failed to get Windows version: %w", err)
}
- version := windows.RtlGetVersion()
+ version := osversion.Get()
// Microsoft has decided to keep the major version as "10" for Windows 11, including the product name.
- if version.BuildNumber >= 22000 {
+ if version.Build >= osversion.V21H2Win11 {
productName = strings.Replace(productName, " 10 ", " 11 ", 1)
}
@@ -131,10 +132,10 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
nil,
prometheus.Labels{
"product": productName,
- "version": fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.BuildNumber),
+ "version": version.String(),
"major_version": strconv.FormatUint(uint64(version.MajorVersion), 10),
"minor_version": strconv.FormatUint(uint64(version.MinorVersion), 10),
- "build_number": strconv.FormatUint(uint64(version.BuildNumber), 10),
+ "build_number": strconv.FormatUint(uint64(version.Build), 10),
"revision": revision,
},
)
@@ -365,7 +366,9 @@ func (c *Collector) getWindowsVersion() (string, string, error) {
return "", "", fmt.Errorf("failed to open registry key: %w", err)
}
- defer ntKey.Close()
+ defer func(ntKey registry.Key) {
+ _ = ntKey.Close()
+ }(ntKey)
productName, _, err := ntKey.GetStringValue("ProductName")
if err != nil {
diff --git a/internal/collector/performancecounter/performancecounter_test_test.go b/internal/collector/performancecounter/performancecounter_test_test.go
index bc8c2180..a47038c3 100644
--- a/internal/collector/performancecounter/performancecounter_test_test.go
+++ b/internal/collector/performancecounter/performancecounter_test_test.go
@@ -30,7 +30,6 @@ import (
"github.com/prometheus-community/windows_exporter/internal/pdh"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -208,9 +207,9 @@ windows_performancecounter_processor_information_processor_time\{core="0,0",stat
promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorHandling: promhttp.ContinueOnError}).ServeHTTP(rw, &http.Request{})
got := rw.Body.String()
- assert.NotEmpty(t, got)
+ require.NotEmpty(t, got)
require.NotEmpty(t, tc.expectedMetrics)
- assert.Regexp(t, tc.expectedMetrics, got)
+ require.Regexp(t, tc.expectedMetrics, got)
})
}
}
diff --git a/internal/collector/performancecounter/types.go b/internal/collector/performancecounter/types.go
index 6a75bfcc..f5293a4f 100644
--- a/internal/collector/performancecounter/types.go
+++ b/internal/collector/performancecounter/types.go
@@ -28,7 +28,7 @@ type Object struct {
Type pdh.CounterType `json:"type" yaml:"type"`
Instances []string `json:"instances" yaml:"instances"`
Counters []Counter `json:"counters" yaml:"counters"`
- InstanceLabel string `json:"instance_label" yaml:"instance_label"` //nolint:tagliatelle
+ InstanceLabel string `json:"instance_label" yaml:"instance_label"`
collector *pdh.Collector
perfDataObject any
diff --git a/internal/collector/textfile/textfile_test_test.go b/internal/collector/textfile/textfile_test_test.go
index b8615bea..cb462139 100644
--- a/internal/collector/textfile/textfile_test_test.go
+++ b/internal/collector/textfile/textfile_test_test.go
@@ -28,7 +28,6 @@ import (
"github.com/prometheus-community/windows_exporter/pkg/collector"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -70,7 +69,7 @@ func TestMultipleDirectories(t *testing.T) {
require.NoError(t, <-errCh)
for _, f := range []string{"dir1", "dir2", "dir3", "dir3sub"} {
- assert.Contains(t, got, f)
+ require.Contains(t, got, f)
}
}
@@ -106,6 +105,6 @@ func TestDuplicateFileName(t *testing.T) {
require.ErrorContains(t, <-errCh, "duplicate filename detected")
- assert.Contains(t, got, "file")
- assert.NotContains(t, got, "sub_file")
+ require.Contains(t, got, "file")
+ require.NotContains(t, got, "sub_file")
}
diff --git a/internal/collector/time/time.go b/internal/collector/time/time.go
index dc10884c..8e2d77fa 100644
--- a/internal/collector/time/time.go
+++ b/internal/collector/time/time.go
@@ -25,10 +25,10 @@ import (
"strings"
"time"
- "github.com/Microsoft/hcsshim/osversion"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/headers/kernel32"
"github.com/prometheus-community/windows_exporter/internal/mi"
+ "github.com/prometheus-community/windows_exporter/internal/osversion"
"github.com/prometheus-community/windows_exporter/internal/pdh"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
diff --git a/internal/headers/guid/guid.go b/internal/headers/guid/guid.go
new file mode 100644
index 00000000..76e3e206
--- /dev/null
+++ b/internal/headers/guid/guid.go
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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 windows
+
+package guid
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "golang.org/x/sys/windows"
+)
+
+type GUID windows.GUID
+
+// FromString parses a string containing a GUID and returns the GUID. The only
+// format currently supported is the `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
+// format.
+func FromString(s string) (GUID, error) {
+ if len(s) != 36 {
+ return GUID{}, fmt.Errorf("invalid GUID %q", s)
+ }
+
+ if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
+ return GUID{}, fmt.Errorf("invalid GUID %q", s)
+ }
+
+ var g GUID
+
+ data1, err := strconv.ParseUint(s[0:8], 16, 32)
+ if err != nil {
+ return GUID{}, fmt.Errorf("invalid GUID %q", s)
+ }
+
+ g.Data1 = uint32(data1)
+
+ data2, err := strconv.ParseUint(s[9:13], 16, 16)
+ if err != nil {
+ return GUID{}, fmt.Errorf("invalid GUID %q", s)
+ }
+
+ g.Data2 = uint16(data2)
+
+ data3, err := strconv.ParseUint(s[14:18], 16, 16)
+ if err != nil {
+ return GUID{}, fmt.Errorf("invalid GUID %q", s)
+ }
+
+ g.Data3 = uint16(data3)
+
+ for i, x := range []int{19, 21, 24, 26, 28, 30, 32, 34} {
+ v, err := strconv.ParseUint(s[x:x+2], 16, 8)
+ if err != nil {
+ return GUID{}, fmt.Errorf("invalid GUID %q", s)
+ }
+
+ g.Data4[i] = uint8(v)
+ }
+
+ return g, nil
+}
+
+func (g *GUID) UnmarshalJSON(b []byte) error {
+ guid, err := FromString(strings.Trim(strings.Trim(string(b), `"`), `{}`))
+ if err != nil {
+ return err
+ }
+
+ *g = guid
+
+ return nil
+}
+
+func (g *GUID) String() string {
+ return fmt.Sprintf(
+ "%08x-%04x-%04x-%04x-%012x",
+ g.Data1,
+ g.Data2,
+ g.Data3,
+ g.Data4[:2],
+ g.Data4[2:])
+}
diff --git a/internal/headers/hcn/hcn.go b/internal/headers/hcn/hcn.go
new file mode 100644
index 00000000..61a7ac21
--- /dev/null
+++ b/internal/headers/hcn/hcn.go
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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 windows
+
+package hcn
+
+import (
+ "fmt"
+
+ "github.com/prometheus-community/windows_exporter/internal/headers/guid"
+ "github.com/prometheus-community/windows_exporter/internal/utils"
+ "golang.org/x/sys/windows"
+)
+
+//nolint:gochecknoglobals
+var (
+ defaultQuery = utils.Must(windows.UTF16PtrFromString(`{"SchemaVersion":{"Major": 2,"Minor": 0},"Flags":"None"}`))
+)
+
+func GetEndpointProperties(endpointID guid.GUID) (EndpointProperties, error) {
+ endpoint, err := OpenEndpoint(endpointID)
+ if err != nil {
+ return EndpointProperties{}, fmt.Errorf("failed to open endpoint: %w", err)
+ }
+
+ defer CloseEndpoint(endpoint)
+
+ result, err := QueryEndpointProperties(endpoint, defaultQuery)
+ if err != nil {
+ return EndpointProperties{}, fmt.Errorf("failed to query endpoint properties: %w", err)
+ }
+
+ return result, nil
+}
diff --git a/internal/headers/hcn/hcn_test.go b/internal/headers/hcn/hcn_test.go
new file mode 100644
index 00000000..77941541
--- /dev/null
+++ b/internal/headers/hcn/hcn_test.go
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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 windows
+
+package hcn_test
+
+import (
+ "testing"
+
+ "github.com/prometheus-community/windows_exporter/internal/headers/hcn"
+ "github.com/stretchr/testify/require"
+)
+
+func TestEnumerateEndpoints(t *testing.T) {
+ t.Parallel()
+
+ endpoints, err := hcn.EnumerateEndpoints()
+ require.NoError(t, err)
+ require.NotNil(t, endpoints)
+}
+
+func TestQueryEndpointProperties(t *testing.T) {
+ t.Parallel()
+
+ endpoints, err := hcn.EnumerateEndpoints()
+ require.NoError(t, err)
+
+ if len(endpoints) == 0 {
+ t.Skip("No endpoints found")
+ }
+
+ _, err = hcn.GetEndpointProperties(endpoints[0])
+ require.NoError(t, err)
+}
diff --git a/internal/headers/hcn/syscall.go b/internal/headers/hcn/syscall.go
new file mode 100644
index 00000000..2df638c8
--- /dev/null
+++ b/internal/headers/hcn/syscall.go
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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 windows
+
+package hcn
+
+import (
+ "encoding/json"
+ "fmt"
+ "unsafe"
+
+ "github.com/prometheus-community/windows_exporter/internal/headers/guid"
+ "github.com/prometheus-community/windows_exporter/internal/headers/hcs"
+ "golang.org/x/sys/windows"
+)
+
+//nolint:gochecknoglobals
+var (
+ modComputeNetwork = windows.NewLazySystemDLL("computenetwork.dll")
+
+ procHcnEnumerateEndpoints = modComputeNetwork.NewProc("HcnEnumerateEndpoints")
+ procHcnOpenEndpoint = modComputeNetwork.NewProc("HcnOpenEndpoint")
+ procHcnQueryEndpointProperties = modComputeNetwork.NewProc("HcnQueryEndpointProperties")
+ procHcnCloseEndpoint = modComputeNetwork.NewProc("HcnCloseEndpoint")
+)
+
+// EnumerateEndpoints enumerates the endpoints.
+//
+// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcnenumerateendpoints
+func EnumerateEndpoints() ([]guid.GUID, error) {
+ var (
+ endpointsJSON *uint16
+ errorRecord *uint16
+ )
+
+ r1, _, _ := procHcnEnumerateEndpoints.Call(
+ 0,
+ uintptr(unsafe.Pointer(&endpointsJSON)),
+ uintptr(unsafe.Pointer(&errorRecord)),
+ )
+
+ windows.CoTaskMemFree(unsafe.Pointer(errorRecord))
+ result := windows.UTF16PtrToString(endpointsJSON)
+
+ if r1 != 0 {
+ return nil, fmt.Errorf("HcnEnumerateEndpoints failed: HRESULT 0x%X: %w", r1, hcs.Win32FromHResult(r1))
+ }
+
+ var endpoints []guid.GUID
+
+ if err := json.Unmarshal([]byte(result), &endpoints); err != nil {
+ return nil, fmt.Errorf("failed to unmarshal JSON: %w", err)
+ }
+
+ return endpoints, nil
+}
+
+// OpenEndpoint opens an endpoint.
+//
+// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcnopenendpoint
+func OpenEndpoint(id guid.GUID) (Endpoint, error) {
+ var (
+ endpoint Endpoint
+ errorRecord *uint16
+ )
+
+ r1, _, _ := procHcnOpenEndpoint.Call(
+ uintptr(unsafe.Pointer(&id)),
+ uintptr(unsafe.Pointer(&endpoint)),
+ uintptr(unsafe.Pointer(&errorRecord)),
+ )
+
+ windows.CoTaskMemFree(unsafe.Pointer(errorRecord))
+
+ if r1 != 0 {
+ return 0, fmt.Errorf("HcnOpenEndpoint failed: HRESULT 0x%X: %w", r1, hcs.Win32FromHResult(r1))
+ }
+
+ return endpoint, nil
+}
+
+// QueryEndpointProperties queries the properties of an endpoint.
+//
+// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcnqueryendpointproperties
+func QueryEndpointProperties(endpoint Endpoint, propertyQuery *uint16) (EndpointProperties, error) {
+ var (
+ resultDocument *uint16
+ errorRecord *uint16
+ )
+
+ r1, _, _ := procHcnQueryEndpointProperties.Call(
+ uintptr(endpoint),
+ uintptr(unsafe.Pointer(&propertyQuery)),
+ uintptr(unsafe.Pointer(&resultDocument)),
+ uintptr(unsafe.Pointer(&errorRecord)),
+ )
+
+ windows.CoTaskMemFree(unsafe.Pointer(errorRecord))
+
+ result := windows.UTF16PtrToString(resultDocument)
+ windows.CoTaskMemFree(unsafe.Pointer(resultDocument))
+
+ if r1 != 0 {
+ return EndpointProperties{}, fmt.Errorf("HcsGetComputeSystemProperties failed: HRESULT 0x%X: %w", r1, hcs.Win32FromHResult(r1))
+ }
+
+ var properties EndpointProperties
+
+ if err := json.Unmarshal([]byte(result), &properties); err != nil {
+ return EndpointProperties{}, fmt.Errorf("failed to unmarshal JSON: %w", err)
+ }
+
+ return properties, nil
+}
+
+// CloseEndpoint close a handle to an Endpoint.
+//
+// https://learn.microsoft.com/en-us/virtualization/api/hcn/reference/hcncloseendpoint
+func CloseEndpoint(endpoint Endpoint) {
+ _, _, _ = procHcnCloseEndpoint.Call(uintptr(endpoint))
+}
diff --git a/internal/headers/hcn/types.go b/internal/headers/hcn/types.go
new file mode 100644
index 00000000..812f4733
--- /dev/null
+++ b/internal/headers/hcn/types.go
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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 windows
+
+package hcn
+
+import (
+ "github.com/prometheus-community/windows_exporter/internal/headers/guid"
+ "golang.org/x/sys/windows"
+)
+
+type Endpoint = windows.Handle
+
+// EndpointProperties contains the properties of an HCN endpoint.
+//
+// https://learn.microsoft.com/en-us/virtualization/api/hcn/hns_schema#HostComputeEndpoint
+type EndpointProperties struct {
+ ID string `json:"ID"`
+ State int `json:"State"`
+ SharedContainers []string `json:"SharedContainers"`
+ Resources EndpointPropertiesResources `json:"Resources"`
+}
+
+type EndpointPropertiesResources struct {
+ Allocators []EndpointPropertiesAllocators `json:"Allocators"`
+}
+type EndpointPropertiesAllocators struct {
+ AdapterNetCfgInstanceId *guid.GUID `json:"AdapterNetCfgInstanceId"`
+}
+
+type EndpointStats struct {
+ BytesReceived uint64 `json:"BytesReceived"`
+ BytesSent uint64 `json:"BytesSent"`
+ DroppedPacketsIncoming uint64 `json:"DroppedPacketsIncoming"`
+ DroppedPacketsOutgoing uint64 `json:"DroppedPacketsOutgoing"`
+ EndpointID string `json:"EndpointId"`
+ InstanceID string `json:"InstanceId"`
+ PacketsReceived uint64 `json:"PacketsReceived"`
+ PacketsSent uint64 `json:"PacketsSent"`
+}
diff --git a/internal/headers/hcs/hcs.go b/internal/headers/hcs/hcs.go
new file mode 100644
index 00000000..b4d06624
--- /dev/null
+++ b/internal/headers/hcs/hcs.go
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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 windows
+
+package hcs
+
+import (
+ "encoding/json"
+ "fmt"
+
+ "github.com/prometheus-community/windows_exporter/internal/utils"
+ "golang.org/x/sys/windows"
+)
+
+//nolint:gochecknoglobals
+var (
+ ContainerQuery = utils.Must(windows.UTF16PtrFromString(`{"Types":["Container"]}`))
+ StatisticsQuery = utils.Must(windows.UTF16PtrFromString(`{"PropertyTypes":["Statistics"]}`))
+)
+
+func GetContainers() ([]Properties, error) {
+ operation, err := CreateOperation()
+ if err != nil {
+ return nil, fmt.Errorf("failed to create operation: %w", err)
+ }
+
+ defer CloseOperation(operation)
+
+ if err := EnumerateComputeSystems(ContainerQuery, operation); err != nil {
+ return nil, fmt.Errorf("failed to enumerate compute systems: %w", err)
+ }
+
+ resultDocument, err := WaitForOperationResult(operation, 1000)
+ if err != nil {
+ return nil, fmt.Errorf("failed to wait and get for operation result: %w - %s", err, resultDocument)
+ } else if resultDocument == "" {
+ return nil, ErrEmptyResultDocument
+ }
+
+ var computeSystems []Properties
+ if err := json.Unmarshal([]byte(resultDocument), &computeSystems); err != nil {
+ return nil, fmt.Errorf("failed to unmarshal compute systems: %w", err)
+ }
+
+ return computeSystems, nil
+}
+
+func GetContainerStatistics(containerID string) (Statistics, error) {
+ computeSystem, err := OpenComputeSystem(containerID)
+ if err != nil {
+ return Statistics{}, fmt.Errorf("failed to open compute system: %w", err)
+ }
+
+ defer CloseComputeSystem(computeSystem)
+
+ operation, err := CreateOperation()
+ if err != nil {
+ return Statistics{}, fmt.Errorf("failed to create operation: %w", err)
+ }
+
+ defer CloseOperation(operation)
+
+ if err := GetComputeSystemProperties(computeSystem, operation, StatisticsQuery); err != nil {
+ return Statistics{}, fmt.Errorf("failed to enumerate compute systems: %w", err)
+ }
+
+ resultDocument, err := WaitForOperationResult(operation, 1000)
+ if err != nil {
+ return Statistics{}, fmt.Errorf("failed to get compute system properties: %w", err)
+ } else if resultDocument == "" {
+ return Statistics{}, ErrEmptyResultDocument
+ }
+
+ var properties Properties
+ if err := json.Unmarshal([]byte(resultDocument), &properties); err != nil {
+ return Statistics{}, fmt.Errorf("failed to unmarshal system properties: %w", err)
+ }
+
+ if properties.Statistics == nil {
+ return Statistics{}, fmt.Errorf("no statistics found for container %s", containerID)
+ }
+
+ return *properties.Statistics, nil
+}
diff --git a/internal/headers/hcs/hcs_test.go b/internal/headers/hcs/hcs_test.go
new file mode 100644
index 00000000..72715d12
--- /dev/null
+++ b/internal/headers/hcs/hcs_test.go
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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 windows
+
+package hcs_test
+
+import (
+ "testing"
+
+ "github.com/prometheus-community/windows_exporter/internal/headers/hcs"
+ "github.com/stretchr/testify/require"
+)
+
+func TestGetContainers(t *testing.T) {
+ t.Parallel()
+
+ containers, err := hcs.GetContainers()
+ require.NoError(t, err)
+ require.NotNil(t, containers)
+}
+
+func TestOpenContainer(t *testing.T) {
+ t.Parallel()
+
+ containers, err := hcs.GetContainers()
+ require.NoError(t, err)
+
+ if len(containers) == 0 {
+ t.Skip("No containers found")
+ }
+
+ statistics, err := hcs.GetContainerStatistics(containers[0].ID)
+ require.NoError(t, err)
+ require.NotNil(t, statistics)
+}
+
+func TestOpenContainerNotFound(t *testing.T) {
+ t.Parallel()
+
+ _, err := hcs.GetContainerStatistics("f3056b79b36ddfe203376473e2aeb4922a8ca7c5d8100764e5829eb5552fe09b")
+ require.ErrorIs(t, err, hcs.ErrIDNotFound)
+}
diff --git a/internal/headers/hcs/syscall.go b/internal/headers/hcs/syscall.go
new file mode 100644
index 00000000..71a95c75
--- /dev/null
+++ b/internal/headers/hcs/syscall.go
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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 windows
+
+package hcs
+
+import (
+ "fmt"
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+//nolint:gochecknoglobals
+var (
+ modComputeCore = windows.NewLazySystemDLL("computecore.dll")
+
+ procHcsCreateOperation = modComputeCore.NewProc("HcsCreateOperation")
+ procHcsWaitForOperationResult = modComputeCore.NewProc("HcsWaitForOperationResult")
+ procHcsCloseOperation = modComputeCore.NewProc("HcsCloseOperation")
+ procHcsEnumerateComputeSystems = modComputeCore.NewProc("HcsEnumerateComputeSystems")
+ procHcsOpenComputeSystem = modComputeCore.NewProc("HcsOpenComputeSystem")
+ procHcsGetComputeSystemProperties = modComputeCore.NewProc("HcsGetComputeSystemProperties")
+ procHcsCloseComputeSystem = modComputeCore.NewProc("HcsCloseComputeSystem")
+)
+
+// CreateOperation creates a new operation.
+func CreateOperation() (Operation, error) {
+ r1, r2, _ := procHcsCreateOperation.Call(0, 0)
+ if r2 != 0 {
+ return 0, fmt.Errorf("HcsCreateOperation failed: HRESULT 0x%X: %w", r2, Win32FromHResult(r2))
+ }
+
+ return Operation(r1), nil
+}
+
+func WaitForOperationResult(operation Operation, timeout uint32) (string, error) {
+ var resultDocument *uint16
+
+ r1, _, _ := procHcsWaitForOperationResult.Call(uintptr(operation), uintptr(timeout), uintptr(unsafe.Pointer(&resultDocument)))
+ if r1 != 0 {
+ return "", fmt.Errorf("HcsWaitForOperationResult failed: HRESULT 0x%X: %w", r1, Win32FromHResult(r1))
+ }
+
+ result := windows.UTF16PtrToString(resultDocument)
+ windows.CoTaskMemFree(unsafe.Pointer(resultDocument))
+
+ return result, nil
+}
+
+// CloseOperation closes an operation.
+func CloseOperation(operation Operation) {
+ _, _, _ = procHcsCloseOperation.Call(uintptr(operation))
+}
+
+// EnumerateComputeSystems enumerates compute systems.
+//
+// https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcsenumeratecomputesystems
+func EnumerateComputeSystems(query *uint16, operation Operation) error {
+ r1, _, _ := procHcsEnumerateComputeSystems.Call(uintptr(unsafe.Pointer(query)), uintptr(operation))
+ if r1 != 0 {
+ return fmt.Errorf("HcsEnumerateComputeSystems failed: HRESULT 0x%X: %w", r1, Win32FromHResult(r1))
+ }
+
+ return nil
+}
+
+// OpenComputeSystem opens a handle to an existing compute system.
+//
+// https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcsopencomputesystem
+func OpenComputeSystem(id string) (ComputeSystem, error) {
+ idPtr, err := windows.UTF16PtrFromString(id)
+ if err != nil {
+ return 0, err
+ }
+
+ var system ComputeSystem
+
+ r1, _, _ := procHcsOpenComputeSystem.Call(
+ uintptr(unsafe.Pointer(idPtr)),
+ uintptr(windows.GENERIC_ALL),
+ uintptr(unsafe.Pointer(&system)),
+ )
+ if r1 != 0 {
+ return 0, fmt.Errorf("HcsOpenComputeSystem failed: HRESULT 0x%X: %w", r1, Win32FromHResult(r1))
+ }
+
+ return system, nil
+}
+
+func GetComputeSystemProperties(system ComputeSystem, operation Operation, propertyQuery *uint16) error {
+ r1, _, err := procHcsGetComputeSystemProperties.Call(
+ uintptr(system),
+ uintptr(operation),
+ uintptr(unsafe.Pointer(propertyQuery)),
+ )
+ if r1 != 0 {
+ return fmt.Errorf("HcsGetComputeSystemProperties failed: HRESULT 0x%X: %w", r1, err)
+ }
+
+ return nil
+}
+
+// CloseComputeSystem closes a handle to a compute system.
+//
+// https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcsclosecomputesystem
+func CloseComputeSystem(system ComputeSystem) {
+ _, _, _ = procHcsCloseComputeSystem.Call(uintptr(system))
+}
+
+func Win32FromHResult(hr uintptr) windows.Errno {
+ if hr&0x1fff0000 == 0x00070000 {
+ return windows.Errno(hr & 0xffff)
+ }
+
+ return windows.Errno(hr)
+}
diff --git a/internal/headers/hcs/types.go b/internal/headers/hcs/types.go
new file mode 100644
index 00000000..f61e9332
--- /dev/null
+++ b/internal/headers/hcs/types.go
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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 windows
+
+package hcs
+
+import (
+ "errors"
+ "time"
+
+ "golang.org/x/sys/windows"
+)
+
+var (
+ ErrEmptyResultDocument = errors.New("empty result document")
+ ErrIDNotFound = windows.Errno(2151088398)
+)
+
+type (
+ Operation = windows.Handle
+ ComputeSystem = windows.Handle
+)
+
+type Properties struct {
+ ID string `json:"Id,omitempty"`
+ SystemType string `json:"SystemType,omitempty"`
+ Owner string `json:"Owner,omitempty"`
+ State string `json:"State,omitempty"`
+ Statistics *Statistics `json:"Statistics,omitempty"`
+ ProcessList []ProcessDetails `json:"ProcessList,omitempty"`
+}
+
+type ProcessDetails struct {
+ ProcessId int32 `json:"ProcessId,omitempty"`
+ ImageName string `json:"ImageName,omitempty"`
+ CreateTimestamp time.Time `json:"CreateTimestamp,omitempty"`
+ UserTime100ns int32 `json:"UserTime100ns,omitempty"`
+ KernelTime100ns int32 `json:"KernelTime100ns,omitempty"`
+ MemoryCommitBytes int32 `json:"MemoryCommitBytes,omitempty"`
+ MemoryWorkingSetPrivateBytes int32 `json:"MemoryWorkingSetPrivateBytes,omitempty"`
+ MemoryWorkingSetSharedBytes int32 `json:"MemoryWorkingSetSharedBytes,omitempty"`
+}
+
+type Statistics struct {
+ Timestamp time.Time `json:"Timestamp,omitempty"`
+ ContainerStartTime time.Time `json:"ContainerStartTime,omitempty"`
+ Uptime100ns uint64 `json:"Uptime100ns,omitempty"`
+ Processor *ProcessorStats `json:"Processor,omitempty"`
+ Memory *MemoryStats `json:"Memory,omitempty"`
+ Storage *StorageStats `json:"Storage,omitempty"`
+}
+
+type ProcessorStats struct {
+ TotalRuntime100ns uint64 `json:"TotalRuntime100ns,omitempty"`
+ RuntimeUser100ns uint64 `json:"RuntimeUser100ns,omitempty"`
+ RuntimeKernel100ns uint64 `json:"RuntimeKernel100ns,omitempty"`
+}
+
+type MemoryStats struct {
+ MemoryUsageCommitBytes uint64 `json:"MemoryUsageCommitBytes,omitempty"`
+ MemoryUsageCommitPeakBytes uint64 `json:"MemoryUsageCommitPeakBytes,omitempty"`
+ MemoryUsagePrivateWorkingSetBytes uint64 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"`
+}
+
+type StorageStats struct {
+ ReadCountNormalized uint64 `json:"ReadCountNormalized,omitempty"`
+ ReadSizeBytes uint64 `json:"ReadSizeBytes,omitempty"`
+ WriteCountNormalized uint64 `json:"WriteCountNormalized,omitempty"`
+ WriteSizeBytes uint64 `json:"WriteSizeBytes,omitempty"`
+}
diff --git a/internal/headers/iphlpapi/iphlpapi.go b/internal/headers/iphlpapi/iphlpapi.go
index 1dad32fc..8bd71167 100644
--- a/internal/headers/iphlpapi/iphlpapi.go
+++ b/internal/headers/iphlpapi/iphlpapi.go
@@ -22,13 +22,16 @@ import (
"fmt"
"unsafe"
+ "github.com/prometheus-community/windows_exporter/internal/headers/guid"
"golang.org/x/sys/windows"
)
//nolint:gochecknoglobals
var (
- modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
- procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable")
+ modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
+ procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable")
+ procGetIfEntry2Ex = modiphlpapi.NewProc("GetIfEntry2Ex")
+ procConvertInterfaceGuidToLuid = modiphlpapi.NewProc("ConvertInterfaceGuidToLuid")
)
func GetTCPConnectionStates(family uint32) (map[MIB_TCP_STATE]uint32, error) {
@@ -128,3 +131,37 @@ func getExtendedTcpTable[T any](ulAf uint32, tableClass uint32) ([]T, error) {
return unsafe.Slice((*T)(unsafe.Pointer(&buf[4])), binary.LittleEndian.Uint32(buf)), nil
}
+
+// GetIfEntry2Ex function retrieves the specified level of information for the specified interface on the local computer.
+//
+// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-getifentry2ex
+func GetIfEntry2Ex(row *MIB_IF_ROW2) error {
+ ret, _, _ := procGetIfEntry2Ex.Call(
+ uintptr(0),
+ uintptr(unsafe.Pointer(row)),
+ )
+
+ if ret != 0 {
+ return fmt.Errorf("GetIfEntry2Ex failed with code %d: %w", ret, windows.Errno(ret))
+ }
+
+ return nil
+}
+
+// ConvertInterfaceGUIDToLUID function converts a globally unique identifier (GUID) for a network interface to the
+// locally unique identifier (LUID) for the interface.
+//
+// https://learn.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-convertinterfaceguidtoluid
+func ConvertInterfaceGUIDToLUID(guid guid.GUID) (uint64, error) {
+ var luid uint64
+ ret, _, _ := procConvertInterfaceGuidToLuid.Call(
+ uintptr(unsafe.Pointer(&guid)),
+ uintptr(unsafe.Pointer(&luid)),
+ )
+
+ if ret != 0 {
+ return 0, fmt.Errorf("ConvertInterfaceGUIDToLUID failed with code %d: %w", ret, windows.Errno(ret))
+ }
+
+ return luid, nil
+}
diff --git a/internal/headers/iphlpapi/types.go b/internal/headers/iphlpapi/types.go
index cc2509fc..4719af23 100644
--- a/internal/headers/iphlpapi/types.go
+++ b/internal/headers/iphlpapi/types.go
@@ -20,6 +20,8 @@ package iphlpapi
import (
"encoding/binary"
"fmt"
+
+ "github.com/prometheus-community/windows_exporter/internal/headers/guid"
)
// MIB_TCPROW_OWNER_PID structure for IPv4.
@@ -107,3 +109,54 @@ func (b BigEndianUint32) uint16() uint16 {
return binary.LittleEndian.Uint16(data)
}
+
+// Constants from Windows headers
+const (
+ IF_MAX_STRING_SIZE = 256
+ IF_MAX_PHYS_ADDRESS_LENGTH = 32
+)
+
+// MIB_IF_ROW2 represents network interface statistics
+type MIB_IF_ROW2 struct {
+ InterfaceLuid uint64
+ InterfaceIndex uint32
+ InterfaceGuid guid.GUID
+ Alias [IF_MAX_STRING_SIZE + 1]uint16
+ Description [IF_MAX_STRING_SIZE + 1]uint16
+ PhysicalAddressLength uint32
+ PhysicalAddress [IF_MAX_PHYS_ADDRESS_LENGTH]byte
+ PermanentPhysicalAddress [IF_MAX_PHYS_ADDRESS_LENGTH]byte
+ Mtu uint32
+ Type uint32
+ TunnelType uint32
+ MediaType uint32
+ PhysicalMediumType uint32
+ AccessType uint32
+ DirectionType uint32
+ InterfaceAndOperStatusFlags uint8
+ OperStatus uint32
+ AdminStatus uint32
+ MediaConnectState uint32
+ NetworkGuid [16]byte
+ ConnectionType uint32
+ TransmitLinkSpeed uint64
+ ReceiveLinkSpeed uint64
+ InOctets uint64
+ InUcastPkts uint64
+ InNUcastPkts uint64
+ InDiscards uint64
+ InErrors uint64
+ InUnknownProtos uint64
+ InUcastOctets uint64
+ InMulticastOctets uint64
+ InBroadcastOctets uint64
+ OutOctets uint64
+ OutUcastPkts uint64
+ OutNUcastPkts uint64
+ OutDiscards uint64
+ OutErrors uint64
+ OutUcastOctets uint64
+ OutMulticastOctets uint64
+ OutBroadcastOctets uint64
+ OutQLen uint64
+}
diff --git a/internal/headers/kernel32/job.go b/internal/headers/kernel32/job.go
new file mode 100644
index 00000000..fb524a4b
--- /dev/null
+++ b/internal/headers/kernel32/job.go
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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 windows
+
+package kernel32
+
+import (
+ "unsafe"
+
+ "golang.org/x/sys/windows"
+)
+
+const (
+ // JobObjectQuery is required to retrieve certain information about a job object,
+ // such as attributes and accounting information (see QueryInformationJobObject and IsProcessInJob).
+ // https://learn.microsoft.com/en-us/windows/win32/procthread/job-object-security-and-access-rights
+ JobObjectQuery = 0x0004
+)
+
+func OpenJobObject(name string) (windows.Handle, error) {
+ handle, _, err := procOpenJobObject.Call(JobObjectQuery, 0, uintptr(unsafe.Pointer(&name)))
+ if handle == 0 {
+ return 0, err
+ }
+
+ return windows.Handle(handle), nil
+}
+
+func IsProcessInJob(process windows.Handle, job windows.Handle, result *bool) error {
+ ret, _, err := procIsProcessInJob.Call(
+ uintptr(process),
+ uintptr(job),
+ uintptr(unsafe.Pointer(&result)),
+ )
+ if ret == 0 {
+ return err
+ }
+
+ return nil
+}
diff --git a/internal/headers/kernel32/kernel32.go b/internal/headers/kernel32/kernel32.go
index a8375d56..17da14c7 100644
--- a/internal/headers/kernel32/kernel32.go
+++ b/internal/headers/kernel32/kernel32.go
@@ -30,6 +30,8 @@ var (
procGetDynamicTimeZoneInformationSys = modkernel32.NewProc("GetDynamicTimeZoneInformation")
procKernelLocalFileTimeToFileTime = modkernel32.NewProc("LocalFileTimeToFileTime")
procGetTickCount = modkernel32.NewProc("GetTickCount64")
+ procOpenJobObject = modkernel32.NewProc("OpenJobObjectW")
+ procIsProcessInJob = modkernel32.NewProc("IsProcessInJob")
)
// SYSTEMTIME contains a date and time.
diff --git a/internal/headers/kernel32/types.go b/internal/headers/kernel32/types.go
new file mode 100644
index 00000000..2f0d9d85
--- /dev/null
+++ b/internal/headers/kernel32/types.go
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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 windows
+
+package kernel32
+
+import "unsafe"
+
+type JobObjectBasicAccountingInformation struct {
+ TotalUserTime uint64
+ TotalKernelTime uint64
+ ThisPeriodTotalUserTime uint64
+ ThisPeriodTotalKernelTime uint64
+ TotalPageFaultCount uint32
+ TotalProcesses uint32
+ ActiveProcesses uint32
+ TotalTerminatedProcesses uint32
+}
+
+type IOCounters struct {
+ ReadOperationCount uint64
+ WriteOperationCount uint64
+ OtherOperationCount uint64
+ ReadTransferCount uint64
+ WriteTransferCount uint64
+ OtherTransferCount uint64
+}
+
+type JobObjectExtendedLimitInformation struct {
+ BasicInfo JobObjectBasicAccountingInformation
+ IoInfo IOCounters
+ ProcessMemoryLimit uint64
+ JobMemoryLimit uint64
+ PeakProcessMemoryUsed uint64
+ PeakJobMemoryUsed uint64
+}
+
+type JobObjectBasicProcessIDList struct {
+ NumberOfAssignedProcesses uint32
+ NumberOfProcessIdsInList uint32
+ ProcessIdList [1]uintptr
+}
+
+// PIDs returns all the process Ids in the job object.
+func (p *JobObjectBasicProcessIDList) PIDs() []uint32 {
+ return unsafe.Slice((*uint32)(unsafe.Pointer(&p.ProcessIdList[0])), int(p.NumberOfProcessIdsInList))
+}
+
+type PROCESS_VM_COUNTERS struct {
+ PeakVirtualSize uintptr
+ VirtualSize uintptr
+ PageFaultCount uint32
+ PeakWorkingSetSize uintptr
+ WorkingSetSize uintptr
+ QuotaPeakPagedPoolUsage uintptr
+ QuotaPagedPoolUsage uintptr
+ QuotaPeakNonPagedPoolUsage uintptr
+ QuotaNonPagedPoolUsage uintptr
+ PagefileUsage uintptr
+ PeakPagefileUsage uintptr
+ PrivateWorkingSetSize uintptr
+}
diff --git a/internal/osversion/osversion_windows.go b/internal/osversion/osversion_windows.go
new file mode 100644
index 00000000..0d719944
--- /dev/null
+++ b/internal/osversion/osversion_windows.go
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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 windows
+
+package osversion
+
+import (
+ "fmt"
+ "sync"
+
+ "golang.org/x/sys/windows"
+)
+
+// OSVersion is a wrapper for Windows version information
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx
+type OSVersion struct {
+ Version uint32
+ MajorVersion uint8
+ MinorVersion uint8
+ Build uint16
+}
+
+//nolint:gochecknoglobals
+var osv = sync.OnceValue(func() OSVersion {
+ v := *windows.RtlGetVersion()
+
+ return OSVersion{
+ MajorVersion: uint8(v.MajorVersion),
+ MinorVersion: uint8(v.MinorVersion),
+ Build: uint16(v.BuildNumber),
+ // Fill version value so that existing clients don't break
+ Version: v.BuildNumber<<16 | (v.MinorVersion << 8) | v.MajorVersion,
+ }
+})
+
+// Get gets the operating system version on Windows.
+// The calling application must be manifested to get the correct version information.
+func Get() OSVersion {
+ return osv()
+}
+
+// Build gets the build-number on Windows
+// The calling application must be manifested to get the correct version information.
+func Build() uint16 {
+ return Get().Build
+}
+
+// String returns the OSVersion formatted as a string. It implements the
+// [fmt.Stringer] interface.
+func (osv OSVersion) String() string {
+ return fmt.Sprintf("%d.%d.%d", osv.MajorVersion, osv.MinorVersion, osv.Build)
+}
diff --git a/internal/osversion/osversion_windows_test.go b/internal/osversion/osversion_windows_test.go
new file mode 100644
index 00000000..ddfe29c7
--- /dev/null
+++ b/internal/osversion/osversion_windows_test.go
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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 windows
+
+package osversion
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestOSVersionString(t *testing.T) {
+ v := OSVersion{
+ Version: 809042555,
+ MajorVersion: 123,
+ MinorVersion: 2,
+ Build: 12345,
+ }
+
+ require.Equal(t, "the version is: 123.2.12345", fmt.Sprintf("the version is: %s", v))
+}
diff --git a/internal/osversion/windowsbuilds.go b/internal/osversion/windowsbuilds.go
new file mode 100644
index 00000000..4aa4e2ea
--- /dev/null
+++ b/internal/osversion/windowsbuilds.go
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// 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 windows
+
+package osversion
+
+// Windows Client and Server build numbers.
+//
+// See:
+// https://learn.microsoft.com/en-us/windows/release-health/release-information
+// https://learn.microsoft.com/en-us/windows/release-health/windows-server-release-info
+// https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information
+const (
+ // RS1 (version 1607, codename "Redstone 1") corresponds to Windows Server
+ // 2016 (ltsc2016) and Windows 10 (Anniversary Update).
+ RS1 = 14393
+ // V1607 (version 1607, codename "Redstone 1") is an alias for [RS1].
+ V1607 = RS1
+ // LTSC2016 (Windows Server 2016) is an alias for [RS1].
+ LTSC2016 = RS1
+
+ // RS2 (version 1703, codename "Redstone 2") was a client-only update, and
+ // corresponds to Windows 10 (Creators Update).
+ RS2 = 15063
+ // V1703 (version 1703, codename "Redstone 2") is an alias for [RS2].
+ V1703 = RS2
+
+ // RS3 (version 1709, codename "Redstone 3") corresponds to Windows Server
+ // 1709 (Semi-Annual Channel (SAC)), and Windows 10 (Fall Creators Update).
+ RS3 = 16299
+ // V1709 (version 1709, codename "Redstone 3") is an alias for [RS3].
+ V1709 = RS3
+
+ // RS4 (version 1803, codename "Redstone 4") corresponds to Windows Server
+ // 1803 (Semi-Annual Channel (SAC)), and Windows 10 (April 2018 Update).
+ RS4 = 17134
+ // V1803 (version 1803, codename "Redstone 4") is an alias for [RS4].
+ V1803 = RS4
+
+ // RS5 (version 1809, codename "Redstone 5") corresponds to Windows Server
+ // 2019 (ltsc2019), and Windows 10 (October 2018 Update).
+ RS5 = 17763
+ // V1809 (version 1809, codename "Redstone 5") is an alias for [RS5].
+ V1809 = RS5
+ // LTSC2019 (Windows Server 2019) is an alias for [RS5].
+ LTSC2019 = RS5
+
+ // V19H1 (version 1903, codename 19H1) corresponds to Windows Server 1903 (semi-annual
+ // channel).
+ V19H1 = 18362
+ // V1903 (version 1903) is an alias for [V19H1].
+ V1903 = V19H1
+
+ // V19H2 (version 1909, codename 19H2) corresponds to Windows Server 1909 (semi-annual
+ // channel).
+ V19H2 = 18363
+ // V1909 (version 1909) is an alias for [V19H2].
+ V1909 = V19H2
+
+ // V20H1 (version 2004, codename 20H1) corresponds to Windows Server 2004 (semi-annual
+ // channel).
+ V20H1 = 19041
+ // V2004 (version 2004) is an alias for [V20H1].
+ V2004 = V20H1
+
+ // V20H2 corresponds to Windows Server 20H2 (semi-annual channel).
+ V20H2 = 19042
+
+ // V21H1 corresponds to Windows Server 21H1 (semi-annual channel).
+ V21H1 = 19043
+
+ // V21H2Win10 corresponds to Windows 10 (November 2021 Update).
+ V21H2Win10 = 19044
+
+ // V21H2Server corresponds to Windows Server 2022 (ltsc2022).
+ V21H2Server = 20348
+ // LTSC2022 (Windows Server 2022) is an alias for [V21H2Server]
+ LTSC2022 = V21H2Server
+
+ // V21H2Win11 corresponds to Windows 11 (original release).
+ V21H2Win11 = 22000
+
+ // V22H2Win10 corresponds to Windows 10 (2022 Update).
+ V22H2Win10 = 19045
+
+ // V22H2Win11 corresponds to Windows 11 (2022 Update).
+ V22H2Win11 = 22621
+)
diff --git a/internal/pdh/collector.go b/internal/pdh/collector.go
index 6b48dc42..c2dfe117 100644
--- a/internal/pdh/collector.go
+++ b/internal/pdh/collector.go
@@ -27,8 +27,8 @@ import (
"sync"
"unsafe"
- "github.com/Microsoft/hcsshim/osversion"
"github.com/prometheus-community/windows_exporter/internal/mi"
+ "github.com/prometheus-community/windows_exporter/internal/osversion"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/sys/windows"
)
diff --git a/internal/pdh/collector_test.go b/internal/pdh/collector_test.go
index 28505912..904786b9 100644
--- a/internal/pdh/collector_test.go
+++ b/internal/pdh/collector_test.go
@@ -22,7 +22,6 @@ import (
"time"
"github.com/prometheus-community/windows_exporter/internal/pdh"
- "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -66,7 +65,7 @@ func TestCollector(t *testing.T) {
continue
}
- assert.NotZerof(t, instance.ThreadCount, "object: %s, instance: %s, counter: %s", tc.object, instance, instance.ThreadCount)
+ require.NotZerof(t, instance.ThreadCount, "object: %s, instance: %s, counter: %s", tc.object, instance, instance.ThreadCount)
}
})
}
diff --git a/internal/pdh/registry/nametable.go b/internal/pdh/registry/nametable.go
index d09b337b..7532de84 100644
--- a/internal/pdh/registry/nametable.go
+++ b/internal/pdh/registry/nametable.go
@@ -19,7 +19,6 @@ package registry
import (
"bytes"
- "fmt"
"strconv"
"sync"
)
@@ -90,10 +89,6 @@ func (t *NameTable) initialize() {
break
}
- if err != nil {
- panic(fmt.Sprint("Invalid index ", index))
- }
-
indexInt, _ := strconv.Atoi(index)
t.table.index[uint32(indexInt)] = desc
diff --git a/renovate.json b/renovate.json
index 985389d8..68226db9 100644
--- a/renovate.json
+++ b/renovate.json
@@ -57,7 +57,7 @@
],
"ignoreDeps": [
"mcr.microsoft.com/oss/kubernetes/windows-host-process-containers-base-image",
- "gopkg.in/yaml.v2"
+ "gopkg.in/yaml.v3"
],
"customManagers": [
{