Get Stats From Docker Daemon via HTTP #1
372
Cargo.lock
generated
372
Cargo.lock
generated
@@ -136,7 +136,7 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
@@ -210,6 +210,12 @@ version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
@@ -398,6 +404,22 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.9"
|
||||
@@ -437,12 +459,48 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
|
||||
|
||||
[[package]]
|
||||
name = "finl_unicode"
|
||||
version = "1.2.0"
|
||||
@@ -465,6 +523,21 @@ version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.0"
|
||||
@@ -480,6 +553,21 @@ version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.28"
|
||||
@@ -496,6 +584,23 @@ version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.28"
|
||||
@@ -525,10 +630,13 @@ version = "0.3.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
@@ -684,6 +792,29 @@ dependencies = [
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"hyper",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
|
||||
dependencies = [
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
@@ -694,6 +825,12 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
|
||||
|
||||
[[package]]
|
||||
name = "iri-string"
|
||||
version = "0.4.1"
|
||||
@@ -730,6 +867,12 @@ version = "0.2.147"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.10"
|
||||
@@ -809,6 +952,24 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
@@ -853,6 +1014,50 @@ version = "1.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
@@ -932,6 +1137,12 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||
|
||||
[[package]]
|
||||
name = "postgres"
|
||||
version = "0.19.7"
|
||||
@@ -1091,7 +1302,7 @@ version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1103,6 +1314,43 @@ dependencies = [
|
||||
"bytecheck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e9ad3fe7488d7e34558a2033d45a0c90b72d97b4f80705666fea71472e2e6a1"
|
||||
dependencies = [
|
||||
"base64 0.21.4",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper-tls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rkyv"
|
||||
version = "0.7.42"
|
||||
@@ -1164,6 +1412,19 @@ version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662"
|
||||
dependencies = [
|
||||
"bitflags 2.4.0",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.14"
|
||||
@@ -1176,6 +1437,15 @@ version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
@@ -1188,6 +1458,29 @@ version = "4.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.188"
|
||||
@@ -1245,20 +1538,25 @@ dependencies = [
|
||||
name = "server_metrics"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
"clap",
|
||||
"futures",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"prometheus",
|
||||
"reqwest",
|
||||
"rust_decimal",
|
||||
"rust_decimal_macros",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"slog",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-http",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1302,6 +1600,12 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slog"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.11.0"
|
||||
@@ -1385,6 +1689,19 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.48"
|
||||
@@ -1450,6 +1767,16 @@ dependencies = [
|
||||
"syn 2.0.32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-postgres"
|
||||
version = "0.7.10"
|
||||
@@ -1528,7 +1855,7 @@ checksum = "f873044bf02dd1e8239e9c1293ea39dad76dc594ec16185d0a1bf31d8dc8d858"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"base64 0.13.1",
|
||||
"bitflags",
|
||||
"bitflags 1.3.2",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
@@ -1625,6 +1952,17 @@ dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
@@ -1640,6 +1978,12 @@ dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
@@ -1686,6 +2030,18 @@ dependencies = [
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.87"
|
||||
@@ -1823,6 +2179,16 @@ version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
|
||||
@@ -6,17 +6,22 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
async-trait = "0.1.61"
|
||||
axum = "0.6.7"
|
||||
clap = { version = "4.4.2", features = ["derive"] }
|
||||
futures = "0.3.28"
|
||||
http = "0.2.8"
|
||||
http-body = "0.4.5"
|
||||
hyper = { version = "0.14.24", features = ["full"] }
|
||||
hyper = { version = "0.14", features = ["full"] }
|
||||
prometheus = "0.13.3"
|
||||
reqwest = { version = "0.11.14", features = ["json"] }
|
||||
rust_decimal = { version = "1.29.1", features = ["serde", "db-postgres"] }
|
||||
rust_decimal_macros = "1.27"
|
||||
serde = { version = "1.0.152", features = ["derive"] }
|
||||
serde_json = "1.0.91"
|
||||
slog = "2.7.0"
|
||||
thiserror = "1.0.38"
|
||||
tokio = { version = "1.28.2", features = ["full"] }
|
||||
tower = { version = "0.4.13", features = ["full"] }
|
||||
tower-http = { version = "0.3.5", features = ["full"] }
|
||||
tower-http = { version = "0.3.5", features = ["full"] }
|
||||
url = "2.3.1"
|
||||
22
src/cli.rs
Normal file
22
src/cli.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use clap::Parser;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
pub struct Args {
|
||||
/// The address to bind the metrics server on
|
||||
#[arg(long, default_value = "0.0.0.0")]
|
||||
pub bind_address: String,
|
||||
/// The port to bind the metrics server to
|
||||
#[arg(long, default_value_t = 45454)]
|
||||
pub bind_port: u32,
|
||||
/// The metrics registry prefix
|
||||
#[arg(long, default_value = "system_exporter")]
|
||||
pub metrics_prefix: String,
|
||||
/// The interval to poll docker stats
|
||||
#[arg(long, default_value_t = 1)]
|
||||
pub docker_stats_seconds: u64,
|
||||
/// The fqdn to the docker server (e.g: unix:/var/run/docker.sock, http://localhost:2375)
|
||||
/// Note: unix sockets are not supported yet
|
||||
#[arg(long, default_value = "http://localhost:2375")]
|
||||
pub docker_server: String,
|
||||
}
|
||||
@@ -1,42 +1,58 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{docker::DockerStats, error::SystemExporterResult, metrics::Metrics};
|
||||
use crate::{
|
||||
docker::{
|
||||
method::{
|
||||
get_containers::models::GetContainersRequest,
|
||||
get_stats::models::{DockerStats, GetContainerStatsRequest},
|
||||
DockerClientContainersTrait, DockerClientStatsTrait,
|
||||
},
|
||||
DockerClient,
|
||||
},
|
||||
error::SystemExporterResult,
|
||||
metrics::Metrics,
|
||||
};
|
||||
|
||||
pub struct DockerCollector {
|
||||
metrics: Arc<Metrics>,
|
||||
client: DockerClient,
|
||||
}
|
||||
|
||||
impl DockerCollector {
|
||||
pub fn new(metrics: Arc<Metrics>) -> Self {
|
||||
Self { metrics }
|
||||
pub fn new(client: DockerClient, metrics: Arc<Metrics>) -> Self {
|
||||
Self { client, metrics }
|
||||
}
|
||||
|
||||
pub fn poll(&self) -> SystemExporterResult<Vec<DockerStats>> {
|
||||
let output = std::process::Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(
|
||||
r#"docker stats --format='{
|
||||
"BlockIO": "{{.BlockIO}}",
|
||||
"CPUPerc": "{{.CPUPerc}}",
|
||||
"Container": "{{.Container}}",
|
||||
"ID": "{{.ID}}",
|
||||
"MemPerc": "{{.MemPerc}}",
|
||||
"MemUsage": "{{.MemUsage}}",
|
||||
"Name": "{{.Name}}",
|
||||
"NetIO": "{{.NetIO}}",
|
||||
"PIDs": "{{.PIDs}}"
|
||||
}' --no-stream | jq -s '.'"#,
|
||||
)
|
||||
.output()
|
||||
.expect("Failed to execute command");
|
||||
pub async fn poll(&self) -> SystemExporterResult<Vec<DockerStats>> {
|
||||
let mut stats = vec![];
|
||||
|
||||
let output_str = String::from_utf8(output.stdout).expect("Not UTF-8");
|
||||
let get_containers_response = self.client.get_containers(GetContainersRequest {}).await?;
|
||||
|
||||
let res = serde_json::from_str::<Vec<DockerStats>>(&output_str)?;
|
||||
let container_ids = get_containers_response
|
||||
.containers
|
||||
.into_iter()
|
||||
.map(|e| e.id)
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
res.iter()
|
||||
.for_each(|e| e.post_metrics(&self.metrics.docker));
|
||||
let futures = container_ids.iter().map(|container_id| {
|
||||
self.client.get_container_stats(GetContainerStatsRequest {
|
||||
container_id: container_id.clone(),
|
||||
})
|
||||
});
|
||||
|
||||
Ok(res)
|
||||
// todo: should we batch these in case there are a lot of containers?
|
||||
// todo: do we care about failures? could be the case that a container terminated between getting IDs and stats
|
||||
for get_containers_response in futures::future::join_all(futures)
|
||||
.await
|
||||
.into_iter()
|
||||
.flatten()
|
||||
{
|
||||
get_containers_response
|
||||
.stats
|
||||
.post_metrics(&self.metrics.docker);
|
||||
stats.push(get_containers_response.stats);
|
||||
}
|
||||
|
||||
Ok(stats)
|
||||
}
|
||||
}
|
||||
|
||||
27
src/docker/method/get_containers/implementation.rs
Normal file
27
src/docker/method/get_containers/implementation.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use crate::{
|
||||
docker::{
|
||||
method::{
|
||||
get_containers::models::{
|
||||
DockerContainer, GetContainersRequest, GetContainersResponse,
|
||||
},
|
||||
DockerClientContainersTrait,
|
||||
},
|
||||
DockerClient,
|
||||
},
|
||||
error::SystemExporterResult,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
impl DockerClientContainersTrait for DockerClient {
|
||||
async fn get_containers(
|
||||
&self,
|
||||
_req: GetContainersRequest,
|
||||
) -> SystemExporterResult<GetContainersResponse> {
|
||||
let containers: Vec<DockerContainer> = self
|
||||
.send_request(http::Method::GET, "containers/json?all=false", _req)
|
||||
.await?;
|
||||
|
||||
Ok(GetContainersResponse { containers })
|
||||
}
|
||||
}
|
||||
2
src/docker/method/get_containers/mod.rs
Normal file
2
src/docker/method/get_containers/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
mod implementation;
|
||||
pub mod models;
|
||||
98
src/docker/method/get_containers/models.rs
Normal file
98
src/docker/method/get_containers/models.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GetContainersRequest {}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GetContainersResponse {
|
||||
pub containers: Vec<DockerContainer>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct DockerContainer {
|
||||
pub id: String,
|
||||
pub command: String,
|
||||
pub created: i64,
|
||||
pub host_config: HostConfig,
|
||||
pub image: String,
|
||||
#[serde(rename = "ImageID")]
|
||||
pub image_id: String,
|
||||
pub labels: HashMap<String, String>,
|
||||
pub mounts: Vec<Mount>,
|
||||
pub names: Vec<String>,
|
||||
pub network_settings: NetworkSettings,
|
||||
pub ports: Vec<Port>,
|
||||
pub state: String,
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct HostConfig {
|
||||
pub network_mode: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct Mount {
|
||||
pub destination: String,
|
||||
pub mode: String,
|
||||
pub propagation: String,
|
||||
#[serde(rename(deserialize = "RW"))]
|
||||
pub rw: bool,
|
||||
pub source: String,
|
||||
#[serde(rename(deserialize = "Type"))]
|
||||
pub type_: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct NetworkSettings {
|
||||
pub networks: Networks,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct Networks {
|
||||
#[serde(rename(deserialize = "bridge"))]
|
||||
pub bridge: Bridge,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct Bridge {
|
||||
#[serde(rename(deserialize = "EndpointID"))]
|
||||
pub endpoint_id: String,
|
||||
pub gateway: String,
|
||||
#[serde(rename(deserialize = "GlobalIPv6Address"))]
|
||||
pub global_ipv6_address: String,
|
||||
#[serde(rename(deserialize = "GlobalIPv6PrefixLen"))]
|
||||
pub global_ipv6_prefix_len: i32,
|
||||
#[serde(rename(deserialize = "IPAMConfig"))]
|
||||
pub ipam_config: Option<String>, // You might want to replace this with the appropriate type
|
||||
#[serde(rename(deserialize = "IPAddress"))]
|
||||
pub ip_address: String,
|
||||
#[serde(rename(deserialize = "IPPrefixLen"))]
|
||||
pub ip_prefix_len: i32,
|
||||
#[serde(rename(deserialize = "IPv6Gateway"))]
|
||||
pub ipv6_gateway: String,
|
||||
pub links: Option<String>, // You might want to replace this with the appropriate type
|
||||
pub mac_address: String,
|
||||
#[serde(rename(deserialize = "NetworkID"))]
|
||||
pub network_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct Port {
|
||||
#[serde(rename(deserialize = "IP"))]
|
||||
pub ip: String,
|
||||
pub private_port: i32,
|
||||
pub public_port: i32,
|
||||
#[serde(rename(deserialize = "Type"))]
|
||||
pub type_: String,
|
||||
}
|
||||
29
src/docker/method/get_stats/implementation.rs
Normal file
29
src/docker/method/get_stats/implementation.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use crate::{
|
||||
docker::{
|
||||
method::{
|
||||
get_stats::models::{DockerStats, GetContainerStatsRequest, GetContainerStatsResponse},
|
||||
DockerClientStatsTrait,
|
||||
},
|
||||
DockerClient,
|
||||
},
|
||||
error::SystemExporterResult,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
impl DockerClientStatsTrait for DockerClient {
|
||||
async fn get_container_stats(
|
||||
&self,
|
||||
req: GetContainerStatsRequest,
|
||||
) -> SystemExporterResult<GetContainerStatsResponse> {
|
||||
let stats: DockerStats = self
|
||||
.send_request(
|
||||
http::Method::GET,
|
||||
format!("containers/{}/stats?stream=false", req.container_id).as_str(),
|
||||
req,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(GetContainerStatsResponse { stats })
|
||||
}
|
||||
}
|
||||
2
src/docker/method/get_stats/mod.rs
Normal file
2
src/docker/method/get_stats/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
mod implementation;
|
||||
pub mod models;
|
||||
190
src/docker/method/get_stats/models.rs
Normal file
190
src/docker/method/get_stats/models.rs
Normal file
@@ -0,0 +1,190 @@
|
||||
use crate::docker::metrics::DockerMetrics;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GetContainerStatsRequest {
|
||||
pub container_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GetContainerStatsResponse {
|
||||
pub stats: DockerStats,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct DockerStats {
|
||||
id: String,
|
||||
name: String,
|
||||
cpu_stats: CpuStats,
|
||||
precpu_stats: CpuStats,
|
||||
blkio_stats: BlkioStats,
|
||||
memory_stats: MemoryStats,
|
||||
pids_stats: PidsStats,
|
||||
#[serde(default)]
|
||||
networks: HashMap<String, NetworkStats>,
|
||||
#[serde(default)]
|
||||
num_procs: u64,
|
||||
preread: String,
|
||||
read: String,
|
||||
// todo: add storage_stats type
|
||||
storage_stats: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct BlkioStats {
|
||||
io_merged_recursive: Option<serde_json::Value>,
|
||||
io_queue_recursive: Option<serde_json::Value>,
|
||||
io_service_bytes_recursive: Option<Vec<IoServiceBytesRecursive>>,
|
||||
io_service_time_recursive: Option<serde_json::Value>,
|
||||
io_serviced_recursive: Option<serde_json::Value>,
|
||||
io_time_recursive: Option<serde_json::Value>,
|
||||
io_wait_time_recursive: Option<serde_json::Value>,
|
||||
sectors_recursive: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all(deserialize = "lowercase"))]
|
||||
pub enum IoDirection {
|
||||
Read,
|
||||
Write,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct IoServiceBytesRecursive {
|
||||
major: u64,
|
||||
minor: u64,
|
||||
op: IoDirection,
|
||||
value: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct CpuStats {
|
||||
cpu_usage: CpuUsage,
|
||||
#[serde(default)]
|
||||
online_cpus: u64,
|
||||
#[serde(default)]
|
||||
system_cpu_usage: u64,
|
||||
throttling_data: ThrottlingData,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct CpuUsage {
|
||||
total_usage: u64,
|
||||
usage_in_kernelmode: u64,
|
||||
usage_in_usermode: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct ThrottlingData {
|
||||
periods: u64,
|
||||
throttled_periods: u64,
|
||||
throttled_time: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct MemoryStats {
|
||||
#[serde(default)]
|
||||
limit: u64,
|
||||
stats: Option<MemoryStatsDetails>,
|
||||
#[serde(default)]
|
||||
usage: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct MemoryStatsDetails {
|
||||
active_anon: u64,
|
||||
active_file: u64,
|
||||
// ... (Include all other fields in the memory_stats.stats here)
|
||||
workingset_refault: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct NetworkStats {
|
||||
rx_bytes: u64,
|
||||
rx_dropped: u64,
|
||||
rx_errors: u64,
|
||||
rx_packets: u64,
|
||||
tx_bytes: u64,
|
||||
tx_dropped: u64,
|
||||
tx_errors: u64,
|
||||
tx_packets: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct PidsStats {
|
||||
current: Option<u64>,
|
||||
limit: Option<u64>,
|
||||
}
|
||||
|
||||
impl DockerStats {
|
||||
pub fn post_metrics(&self, metrics: &DockerMetrics) {
|
||||
let cpu_delta =
|
||||
self.cpu_stats.cpu_usage.total_usage - self.precpu_stats.cpu_usage.total_usage;
|
||||
let system_cpu_delta = self.cpu_stats.system_cpu_usage - self.precpu_stats.system_cpu_usage;
|
||||
|
||||
let cpu_percent = if system_cpu_delta > 0 {
|
||||
(cpu_delta as f64 / system_cpu_delta as f64) * self.cpu_stats.online_cpus as f64 * 100.0
|
||||
} else {
|
||||
0_f64
|
||||
};
|
||||
|
||||
metrics
|
||||
.container_cpu_perc
|
||||
.with_label_values(&[&self.name])
|
||||
.set(cpu_percent);
|
||||
|
||||
let memoy_percent = if self.memory_stats.limit > 0 {
|
||||
(self.memory_stats.usage as f64 / self.memory_stats.limit as f64) * 100.0
|
||||
} else {
|
||||
0_f64
|
||||
};
|
||||
|
||||
metrics
|
||||
.container_mem_percentage
|
||||
.with_label_values(&[&self.name])
|
||||
.set(memoy_percent);
|
||||
|
||||
metrics
|
||||
.container_mem_used
|
||||
.with_label_values(&[&self.name])
|
||||
.set(self.memory_stats.usage as f64);
|
||||
|
||||
metrics
|
||||
.container_mem_available
|
||||
.with_label_values(&[&self.name])
|
||||
.set(self.memory_stats.limit as f64);
|
||||
|
||||
if let Some(io_service_bytes_recursive) = &self.blkio_stats.io_service_bytes_recursive {
|
||||
let (read, written) = io_service_bytes_recursive.iter().fold(
|
||||
(0, 0),
|
||||
|(read, written), io_service_bytes_recursive| match &io_service_bytes_recursive.op {
|
||||
IoDirection::Read => (read + io_service_bytes_recursive.value, written),
|
||||
IoDirection::Write => (read, written + io_service_bytes_recursive.value),
|
||||
},
|
||||
);
|
||||
|
||||
metrics
|
||||
.container_block_io_read
|
||||
.with_label_values(&[&self.name])
|
||||
.set(read as f64);
|
||||
metrics
|
||||
.container_block_io_write
|
||||
.with_label_values(&[&self.name])
|
||||
.set(written as f64);
|
||||
}
|
||||
|
||||
self.networks.iter().for_each(|(iface, stats)| {
|
||||
metrics
|
||||
.container_net_io_received
|
||||
.with_label_values(&[&self.name, iface])
|
||||
.set(stats.rx_bytes as f64);
|
||||
metrics
|
||||
.container_net_io_transmitted
|
||||
.with_label_values(&[&self.name, iface])
|
||||
.set(stats.tx_bytes as f64);
|
||||
})
|
||||
}
|
||||
}
|
||||
27
src/docker/method/mod.rs
Normal file
27
src/docker/method/mod.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use crate::{
|
||||
docker::method::{
|
||||
get_containers::models::{GetContainersRequest, GetContainersResponse},
|
||||
get_stats::models::{GetContainerStatsRequest, GetContainerStatsResponse},
|
||||
},
|
||||
error::SystemExporterResult,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
|
||||
pub mod get_containers;
|
||||
pub mod get_stats;
|
||||
|
||||
#[async_trait]
|
||||
pub trait DockerClientStatsTrait {
|
||||
async fn get_container_stats(
|
||||
&self,
|
||||
req: GetContainerStatsRequest,
|
||||
) -> SystemExporterResult<GetContainerStatsResponse>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait DockerClientContainersTrait {
|
||||
async fn get_containers(
|
||||
&self,
|
||||
req: GetContainersRequest,
|
||||
) -> SystemExporterResult<GetContainersResponse>;
|
||||
}
|
||||
@@ -39,14 +39,14 @@ impl DockerMetrics {
|
||||
"container_net_io_received",
|
||||
"Container Network I/O Received"
|
||||
),
|
||||
&["container"],
|
||||
&["container", "interface"],
|
||||
)?;
|
||||
let container_net_io_transmitted = GaugeVec::new(
|
||||
opts!(
|
||||
"container_net_io_transmitted",
|
||||
"Container Network I/O Transmitted"
|
||||
),
|
||||
&["container"],
|
||||
&["container", "interface"],
|
||||
)?;
|
||||
let container_cpu_perc = GaugeVec::new(
|
||||
opts!("container_cpu_perc", "Container CPU Percentage"),
|
||||
|
||||
@@ -1,46 +1,64 @@
|
||||
use rust_decimal::Decimal;
|
||||
use serde::Deserialize;
|
||||
use crate::{
|
||||
docker::method::{DockerClientContainersTrait, DockerClientStatsTrait},
|
||||
error::{SystemExporterError, SystemExporterResult},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use http::Method;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use url::Url;
|
||||
|
||||
pub mod collector;
|
||||
pub mod method;
|
||||
pub mod metrics;
|
||||
mod util;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct DockerStats {
|
||||
#[serde(rename = "BlockIO", with = "util::block_io_format")]
|
||||
pub block_io: BlockIo,
|
||||
#[serde(rename = "CPUPerc", with = "util::perc_format")]
|
||||
pub cpu_perc: Decimal,
|
||||
#[serde(rename = "Container")]
|
||||
pub container: String,
|
||||
#[serde(rename = "ID")]
|
||||
pub id: String,
|
||||
#[serde(rename = "MemPerc", with = "util::perc_format")]
|
||||
pub mem_perc: Decimal,
|
||||
#[serde(rename = "MemUsage", with = "util::mem_usage_format")]
|
||||
pub mem_usage: MemUsage,
|
||||
#[serde(rename = "Name")]
|
||||
pub name: String,
|
||||
#[serde(rename = "NetIO", with = "util::net_io_format")]
|
||||
pub net_io: NetIo,
|
||||
// #[serde(rename = "PIDs")]
|
||||
// pub pids: String,
|
||||
#[derive(Clone)]
|
||||
pub struct DockerClient {
|
||||
pub docker_daemon_host: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct BlockIo {
|
||||
pub read: Decimal,
|
||||
pub write: Decimal,
|
||||
impl DockerClient {
|
||||
pub fn from_addr(docker_daemon_host: &str) -> Self {
|
||||
DockerClient {
|
||||
docker_daemon_host: docker_daemon_host.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_request<T, U>(
|
||||
&self,
|
||||
method: Method,
|
||||
url: &str,
|
||||
body: U,
|
||||
) -> SystemExporterResult<T>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
U: Serialize,
|
||||
{
|
||||
// TODO: support unix sockets
|
||||
if self.docker_daemon_host.starts_with("unix:") {
|
||||
return Err(SystemExporterError::UnsupportedProtocol(
|
||||
self.docker_daemon_host.clone(),
|
||||
));
|
||||
}
|
||||
|
||||
let url = Url::parse(format!("{}/{url}", self.docker_daemon_host).as_str())?;
|
||||
|
||||
let res = reqwest::Client::new()
|
||||
.request(method, url)
|
||||
.body(serde_json::to_string(&body)?)
|
||||
.header(reqwest::header::CONTENT_TYPE, "application/json")
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
let json_value: serde_json::Value = res.json().await?;
|
||||
|
||||
match serde_json::from_value(json_value.clone()) {
|
||||
Ok(value) => Ok(value),
|
||||
Err(_) => Err(SystemExporterError::RemoteServerError(
|
||||
serde_json::from_value(json_value)?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct MemUsage {
|
||||
pub used: Decimal,
|
||||
pub total: Decimal,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct NetIo {
|
||||
pub received: Decimal,
|
||||
pub transmitted: Decimal,
|
||||
}
|
||||
#[async_trait]
|
||||
pub trait DockerClientTrait: DockerClientStatsTrait + DockerClientContainersTrait {}
|
||||
|
||||
@@ -1,193 +0,0 @@
|
||||
use crate::docker::{metrics::DockerMetrics, DockerStats};
|
||||
use rust_decimal::Decimal;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub(crate) mod block_io_format {
|
||||
use crate::docker::{util::parse_to_decimal, BlockIo};
|
||||
use serde::{self, Deserialize, Deserializer};
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<BlockIo, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
let parts: Vec<&str> = s.split(" / ").collect();
|
||||
if parts.len() == 2 {
|
||||
match (parse_to_decimal(parts[0]), parse_to_decimal(parts[1])) {
|
||||
(Ok(read), Ok(write)) => Ok(BlockIo { read, write }),
|
||||
_ => Err(serde::de::Error::custom("Invalid decimal format")),
|
||||
}
|
||||
} else {
|
||||
Err(serde::de::Error::custom("Invalid format"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod mem_usage_format {
|
||||
use crate::docker::{util::parse_to_decimal, MemUsage};
|
||||
use serde::{self, Deserialize, Deserializer};
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<MemUsage, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
let parts: Vec<&str> = s.split(" / ").collect();
|
||||
if parts.len() == 2 {
|
||||
match (parse_to_decimal(parts[0]), parse_to_decimal(parts[1])) {
|
||||
(Ok(used), Ok(total)) => Ok(MemUsage { used, total }),
|
||||
_ => Err(serde::de::Error::custom("Invalid decimal format")),
|
||||
}
|
||||
} else {
|
||||
Err(serde::de::Error::custom("Invalid format"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod net_io_format {
|
||||
use crate::docker::{util::parse_to_decimal, NetIo};
|
||||
use serde::{self, Deserialize, Deserializer};
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<NetIo, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
let parts: Vec<&str> = s.split(" / ").collect();
|
||||
if parts.len() == 2 {
|
||||
match (parse_to_decimal(parts[0]), parse_to_decimal(parts[1])) {
|
||||
(Ok(received), Ok(transmitted)) => Ok(NetIo {
|
||||
received,
|
||||
transmitted,
|
||||
}),
|
||||
_ => Err(serde::de::Error::custom("Invalid decimal format")),
|
||||
}
|
||||
} else {
|
||||
Err(serde::de::Error::custom("Invalid format"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod perc_format {
|
||||
use rust_decimal::Decimal;
|
||||
use serde::{self, Deserialize, Deserializer};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<Decimal, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
let number_part: &str = s.trim_end_matches('%');
|
||||
match Decimal::from_str(number_part) {
|
||||
Ok(value) => Ok(value),
|
||||
Err(_) => Err(serde::de::Error::custom("Invalid decimal format")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_to_decimal(input: &str) -> Result<Decimal, rust_decimal::Error> {
|
||||
let multiplier = if input.ends_with("kB") {
|
||||
1_000
|
||||
} else if input.ends_with("MB") {
|
||||
1_000_000
|
||||
} else if input.ends_with("GB") {
|
||||
1_000_000_000
|
||||
} else if input.ends_with("KiB") {
|
||||
1_000
|
||||
} else if input.ends_with("MiB") {
|
||||
1_000_000
|
||||
} else if input.ends_with("GiB") {
|
||||
1_000_000_000
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
let number_part: &str = input.trim_end_matches(|c: char| !c.is_numeric() && c != '.');
|
||||
let value = Decimal::from_str(number_part)?;
|
||||
|
||||
Ok(value * Decimal::from(multiplier))
|
||||
}
|
||||
|
||||
impl DockerStats {
|
||||
pub fn post_metrics(&self, metrics: &DockerMetrics) {
|
||||
metrics
|
||||
.container_cpu_perc
|
||||
.with_label_values(&[&self.name])
|
||||
.set(
|
||||
self.cpu_perc
|
||||
.to_string()
|
||||
.parse::<f64>()
|
||||
.expect("Failed to parse"),
|
||||
);
|
||||
metrics
|
||||
.container_mem_percentage
|
||||
.with_label_values(&[&self.name])
|
||||
.set(
|
||||
self.mem_perc
|
||||
.to_string()
|
||||
.parse::<f64>()
|
||||
.expect("Failed to parse"),
|
||||
);
|
||||
metrics
|
||||
.container_mem_used
|
||||
.with_label_values(&[&self.name])
|
||||
.set(
|
||||
self.mem_usage
|
||||
.used
|
||||
.to_string()
|
||||
.parse::<f64>()
|
||||
.expect("Failed to parse"),
|
||||
);
|
||||
metrics
|
||||
.container_mem_available
|
||||
.with_label_values(&[&self.name])
|
||||
.set(
|
||||
self.mem_usage
|
||||
.total
|
||||
.to_string()
|
||||
.parse::<f64>()
|
||||
.expect("Failed to parse"),
|
||||
);
|
||||
metrics
|
||||
.container_block_io_read
|
||||
.with_label_values(&[&self.name])
|
||||
.set(
|
||||
self.block_io
|
||||
.read
|
||||
.to_string()
|
||||
.parse::<f64>()
|
||||
.expect("Failed to parse"),
|
||||
);
|
||||
metrics
|
||||
.container_block_io_write
|
||||
.with_label_values(&[&self.name])
|
||||
.set(
|
||||
self.block_io
|
||||
.write
|
||||
.to_string()
|
||||
.parse::<f64>()
|
||||
.expect("Failed to parse"),
|
||||
);
|
||||
metrics
|
||||
.container_net_io_received
|
||||
.with_label_values(&[&self.name])
|
||||
.set(
|
||||
self.net_io
|
||||
.received
|
||||
.to_string()
|
||||
.parse::<f64>()
|
||||
.expect("Failed to parse"),
|
||||
);
|
||||
metrics
|
||||
.container_net_io_transmitted
|
||||
.with_label_values(&[&self.name])
|
||||
.set(
|
||||
self.net_io
|
||||
.transmitted
|
||||
.to_string()
|
||||
.parse::<f64>()
|
||||
.expect("Failed to parse"),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,14 @@ pub enum SystemExporterError {
|
||||
Prometheus(#[from] prometheus::Error),
|
||||
#[error(transparent)]
|
||||
AddrParse(#[from] std::net::AddrParseError),
|
||||
#[error(transparent)]
|
||||
UrlParse(#[from] url::ParseError),
|
||||
#[error(transparent)]
|
||||
Reqwest(#[from] reqwest::Error),
|
||||
#[error("The remote server responded with error body: {0}")]
|
||||
RemoteServerError(serde_json::Value),
|
||||
#[error("The specified protocol: {0} is not supported")]
|
||||
UnsupportedProtocol(String),
|
||||
}
|
||||
|
||||
pub type SystemExporterResult<T, E = SystemExporterError> = Result<T, E>;
|
||||
|
||||
34
src/main.rs
34
src/main.rs
@@ -1,42 +1,32 @@
|
||||
use crate::{error::SystemExporterResult, metrics::Metrics};
|
||||
use crate::{
|
||||
cli::Args,
|
||||
docker::{collector::DockerCollector, DockerClient},
|
||||
error::SystemExporterResult,
|
||||
metrics::Metrics,
|
||||
server::Server,
|
||||
};
|
||||
use clap::Parser;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
use crate::docker::collector::DockerCollector;
|
||||
use crate::server::Server;
|
||||
|
||||
mod cli;
|
||||
mod docker;
|
||||
mod error;
|
||||
mod metrics;
|
||||
mod server;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
pub struct Args {
|
||||
/// The address to bind the metrics server on
|
||||
#[arg(long, default_value = "0.0.0.0")]
|
||||
pub bind_address: String,
|
||||
/// The port to bind the metrics server to
|
||||
#[arg(long, default_value_t = 45454)]
|
||||
pub bind_port: u32,
|
||||
/// The metrics registry prefix
|
||||
#[arg(long, default_value = "system_exporter")]
|
||||
pub metrics_prefix: String,
|
||||
/// The interval to poll docker stats
|
||||
#[arg(long, default_value_t = 5)]
|
||||
pub docker_stats_seconds: u64,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> SystemExporterResult<()> {
|
||||
let args = Args::parse();
|
||||
|
||||
let client = DockerClient::from_addr(args.docker_server.as_ref());
|
||||
|
||||
let metrics = Arc::new(Metrics::new(args.metrics_prefix.as_str())?);
|
||||
let server = Server::new(&args, metrics.clone())?;
|
||||
let collector = DockerCollector::new(metrics);
|
||||
let collector = DockerCollector::new(client, metrics);
|
||||
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
let _ = collector.poll();
|
||||
let _ = collector.poll().await;
|
||||
tokio::time::sleep(Duration::from_secs(args.docker_stats_seconds)).await;
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user