tools/unitctl: Initial Docker Procedures
* move UnitdProcess serialization logic into UnitdProcess * filter out docker processes from process output on Linux * initial implementation of a UnitdContainer type * initial implementation of a docker container search for unitd * pull out custom openapi future executor and use same tokio runtime as docker client * refactor openapi client to not manage its own tokio runtime * process mount points per docker container * correctly output docker container info in relevant unitd instances * create UnitdProcess from UnitdContainer * UnitdProcess now owns UnitdContainer * get and parse container details from docker API * introduce procedure to rewrite file paths based on docker container mounts * test path rewrite facilities * apply path rewrite to unix socket Signed-off-by: Ava Hahn <a.hahn@f5.com>
This commit is contained in:
parent
5d1ce5c447
commit
6e8f7bbb91
17 changed files with 989 additions and 184 deletions
474
tools/unitctl/Cargo.lock
generated
474
tools/unitctl/Cargo.lock
generated
|
@ -26,6 +26,21 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.5"
|
||||
|
@ -128,6 +143,12 @@ version = "0.21.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.69.4"
|
||||
|
@ -172,6 +193,56 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bollard"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0aed08d3adb6ebe0eff737115056652670ae290f177759aac19c30456135f94c"
|
||||
dependencies = [
|
||||
"base64 0.22.0",
|
||||
"bollard-stubs",
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"hex",
|
||||
"http 1.1.0",
|
||||
"http-body-util",
|
||||
"hyper 1.3.1",
|
||||
"hyper-named-pipe",
|
||||
"hyper-util",
|
||||
"hyperlocal-next",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"serde_urlencoded",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tower-service",
|
||||
"url",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bollard-stubs"
|
||||
version = "1.44.0-rc.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "709d9aa1c37abb89d40f19f5d0ad6f0d88cb1581264e571c9350fc5bb89cf1c5"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"serde_with",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.2.1"
|
||||
|
@ -204,6 +275,19 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
|
||||
dependencies = [
|
||||
"android-tzdata",
|
||||
"iana-time-zone",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.7.0"
|
||||
|
@ -355,6 +439,16 @@ version = "1.9.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f8a51dd197fa6ba5b4dc98a990a43cc13693c23eb0089ebb0fcc1f04152bca6"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4"
|
||||
dependencies = [
|
||||
"powerfmt",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.6"
|
||||
|
@ -377,6 +471,12 @@ version = "1.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.1"
|
||||
|
@ -568,6 +668,12 @@ version = "0.12.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.0"
|
||||
|
@ -609,6 +715,17 @@ dependencies = [
|
|||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.5"
|
||||
|
@ -616,7 +733,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
"http 0.2.8",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body-util"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
|
@ -642,8 +782,8 @@ dependencies = [
|
|||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"http 0.2.8",
|
||||
"http-body 0.4.5",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
|
@ -655,6 +795,40 @@ dependencies = [
|
|||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"httparse",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-named-pipe"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"hyper 1.3.1",
|
||||
"hyper-util",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
|
@ -662,12 +836,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"hyper",
|
||||
"hyper 0.14.27",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-util",
|
||||
"http 1.1.0",
|
||||
"http-body 1.0.0",
|
||||
"hyper 1.3.1",
|
||||
"pin-project-lite",
|
||||
"socket2 0.5.5",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyperlocal"
|
||||
version = "0.8.0"
|
||||
|
@ -676,11 +870,49 @@ checksum = "0fafdf7b2b2de7c9784f76e02c0935e65a8117ec3b768644379983ab333ac98c"
|
|||
dependencies = [
|
||||
"futures-util",
|
||||
"hex",
|
||||
"hyper",
|
||||
"hyper 0.14.27",
|
||||
"pin-project",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyperlocal-next"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acf569d43fa9848e510358c07b80f4adf34084ddc28c6a4a651ee8474c070dcc"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"http-body-util",
|
||||
"hyper 1.3.1",
|
||||
"hyper-util",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone-haiku"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.3.0"
|
||||
|
@ -698,7 +930,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"hashbrown 0.12.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.14.3",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -725,6 +969,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "json5"
|
||||
version = "0.4.1"
|
||||
|
@ -883,6 +1136,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
|
@ -1055,6 +1314,12 @@ version = "0.3.26"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||
|
||||
[[package]]
|
||||
name = "powerfmt"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.17"
|
||||
|
@ -1238,7 +1503,7 @@ version = "2.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35e4980fa29e4c4b212ffb3db068a564cbf560e51d3944b7c88bd8bf5bec64f4"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.21.5",
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
|
@ -1316,22 +1581,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.147"
|
||||
version = "1.0.198"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
|
||||
checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.147"
|
||||
version = "1.0.198"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
|
||||
checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.103",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1340,19 +1605,59 @@ version = "1.0.87"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce777b7b150d76b9cf60d28b55f5847135a003f7d7350c6be7a773508ce7d45"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"indexmap 1.9.1",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_with"
|
||||
version = "3.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c85f8e96d1d6857f13768fcbd895fcb06225510022a2774ed8b5150581847b0"
|
||||
dependencies = [
|
||||
"base64 0.22.0",
|
||||
"chrono",
|
||||
"hex",
|
||||
"indexmap 1.9.1",
|
||||
"indexmap 2.2.6",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_yaml"
|
||||
version = "0.9.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d232d893b10de3eb7258ff01974d6ee20663d8e833263c99409d4b13a0209da"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"indexmap 1.9.1",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
|
@ -1385,6 +1690,12 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.7"
|
||||
|
@ -1493,6 +1804,37 @@ dependencies = [
|
|||
"syn 1.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"itoa",
|
||||
"num-conv",
|
||||
"powerfmt",
|
||||
"serde",
|
||||
"time-core",
|
||||
"time-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time-core"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||
|
||||
[[package]]
|
||||
name = "time-macros"
|
||||
version = "0.2.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf"
|
||||
dependencies = [
|
||||
"num-conv",
|
||||
"time-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.6.0"
|
||||
|
@ -1515,6 +1857,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
|
@ -1545,6 +1888,42 @@ dependencies = [
|
|||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-layer"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.2"
|
||||
|
@ -1558,6 +1937,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"log",
|
||||
"pin-project-lite",
|
||||
"tracing-core",
|
||||
]
|
||||
|
@ -1614,13 +1994,15 @@ dependencies = [
|
|||
name = "unit-client-rs"
|
||||
version = "0.4.0-beta"
|
||||
dependencies = [
|
||||
"bollard",
|
||||
"custom_error",
|
||||
"futures",
|
||||
"hex",
|
||||
"hyper",
|
||||
"hyper 0.14.27",
|
||||
"hyper-tls",
|
||||
"hyperlocal",
|
||||
"rand",
|
||||
"regex",
|
||||
"rustls",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -1634,10 +2016,10 @@ dependencies = [
|
|||
name = "unit-openapi"
|
||||
version = "0.4.0-beta"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.21.5",
|
||||
"futures",
|
||||
"http",
|
||||
"hyper",
|
||||
"http 0.2.8",
|
||||
"hyper 0.14.27",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
|
@ -1652,7 +2034,7 @@ dependencies = [
|
|||
"colored_json",
|
||||
"custom_error",
|
||||
"futures",
|
||||
"hyper",
|
||||
"hyper 0.14.27",
|
||||
"hyper-tls",
|
||||
"hyperlocal",
|
||||
"json5",
|
||||
|
@ -1735,6 +2117,60 @@ version = "0.11.0+wasi-snapshot-preview1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.60",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.2"
|
||||
|
|
|
@ -27,6 +27,8 @@ which = "5.0"
|
|||
|
||||
unit-openapi = { path = "../unit-openapi" }
|
||||
rustls = "0.23.5"
|
||||
bollard = "0.16.1"
|
||||
regex = "1.10.4"
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8.5"
|
||||
|
|
|
@ -10,6 +10,7 @@ mod runtime_flags;
|
|||
pub mod unit_client;
|
||||
mod unitd_cmd;
|
||||
pub mod unitd_configure_options;
|
||||
pub mod unitd_docker;
|
||||
pub mod unitd_instance;
|
||||
pub mod unitd_process;
|
||||
mod unitd_process_user;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::collections::HashMap;
|
||||
use std::error::Error as StdError;
|
||||
use std::fmt::Debug;
|
||||
use std::future::Future;
|
||||
use std::rc::Rc;
|
||||
use std::{fmt, io};
|
||||
|
||||
|
@ -13,7 +12,6 @@ use hyper::{http, Body, Client, Request};
|
|||
use hyper_tls::HttpsConnector;
|
||||
use hyperlocal::{UnixClientExt, UnixConnector};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
use crate::control_socket_address::ControlSocket;
|
||||
use unit_openapi::apis::configuration::Configuration;
|
||||
|
@ -168,51 +166,38 @@ where
|
|||
#[derive(Debug)]
|
||||
pub struct UnitClient {
|
||||
pub control_socket: ControlSocket,
|
||||
/// A `current_thread` runtime for executing operations on the
|
||||
/// asynchronous client in a blocking manner.
|
||||
rt: Runtime,
|
||||
/// Client for communicating with the control API over the UNIX domain socket
|
||||
client: Box<RemoteClient<Body>>,
|
||||
}
|
||||
|
||||
impl UnitClient {
|
||||
pub fn new_with_runtime(control_socket: ControlSocket, runtime: Runtime) -> Self {
|
||||
pub fn new(control_socket: ControlSocket) -> Self {
|
||||
if control_socket.is_local_socket() {
|
||||
Self::new_unix(control_socket, runtime)
|
||||
Self::new_unix(control_socket)
|
||||
} else {
|
||||
Self::new_http(control_socket, runtime)
|
||||
Self::new_http(control_socket)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(control_socket: ControlSocket) -> Self {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("Unable to create a current_thread runtime");
|
||||
Self::new_with_runtime(control_socket, runtime)
|
||||
}
|
||||
|
||||
pub fn new_http(control_socket: ControlSocket, runtime: Runtime) -> Self {
|
||||
pub fn new_http(control_socket: ControlSocket) -> Self {
|
||||
let remote_client = Client::builder().build(HttpsConnector::new());
|
||||
Self {
|
||||
control_socket,
|
||||
rt: runtime,
|
||||
client: Box::from(RemoteClient::Tcp { client: remote_client }),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_unix(control_socket: ControlSocket, runtime: Runtime) -> UnitClient {
|
||||
pub fn new_unix(control_socket: ControlSocket) -> UnitClient {
|
||||
let remote_client = Client::unix();
|
||||
|
||||
Self {
|
||||
control_socket,
|
||||
rt: runtime,
|
||||
client: Box::from(RemoteClient::Unix { client: remote_client }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a request to UNIT and deserializes the JSON response body into the value of type `RESPONSE`.
|
||||
pub fn send_request_and_deserialize_response<RESPONSE: for<'de> serde::Deserialize<'de>>(
|
||||
pub async fn send_request_and_deserialize_response<RESPONSE: for<'de> serde::Deserialize<'de>>(
|
||||
&self,
|
||||
mut request: Request<Body>,
|
||||
) -> Result<RESPONSE, UnitClientError> {
|
||||
|
@ -223,34 +208,32 @@ impl UnitClient {
|
|||
|
||||
let response_future = self.client.request(request);
|
||||
|
||||
self.rt.block_on(async {
|
||||
let response = response_future
|
||||
.await
|
||||
.map_err(|error| UnitClientError::new(error, self.control_socket.to_string(), path.to_string()))?;
|
||||
let response = response_future
|
||||
.await
|
||||
.map_err(|error| UnitClientError::new(error, self.control_socket.to_string(), path.to_string()))?;
|
||||
|
||||
let status = response.status();
|
||||
let body = hyper::body::aggregate(response)
|
||||
.await
|
||||
.map_err(|error| UnitClientError::new(error, self.control_socket.to_string(), path.to_string()))?;
|
||||
let reader = &mut body.reader();
|
||||
if !status.is_success() {
|
||||
let error: HashMap<String, String> =
|
||||
serde_json::from_reader(reader).map_err(|error| UnitClientError::JsonError {
|
||||
source: error,
|
||||
path: path.to_string(),
|
||||
})?;
|
||||
|
||||
return Err(UnitClientError::HttpResponseJsonBodyError {
|
||||
status,
|
||||
let status = response.status();
|
||||
let body = hyper::body::aggregate(response)
|
||||
.await
|
||||
.map_err(|error| UnitClientError::new(error, self.control_socket.to_string(), path.to_string()))?;
|
||||
let reader = &mut body.reader();
|
||||
if !status.is_success() {
|
||||
let error: HashMap<String, String> =
|
||||
serde_json::from_reader(reader).map_err(|error| UnitClientError::JsonError {
|
||||
source: error,
|
||||
path: path.to_string(),
|
||||
error: error.get("error").unwrap_or(&"Unknown error".into()).to_string(),
|
||||
detail: error.get("detail").unwrap_or(&"".into()).to_string(),
|
||||
});
|
||||
}
|
||||
serde_json::from_reader(reader).map_err(|error| UnitClientError::JsonError {
|
||||
source: error,
|
||||
})?;
|
||||
|
||||
return Err(UnitClientError::HttpResponseJsonBodyError {
|
||||
status,
|
||||
path: path.to_string(),
|
||||
})
|
||||
error: error.get("error").unwrap_or(&"Unknown error".into()).to_string(),
|
||||
detail: error.get("detail").unwrap_or(&"".into()).to_string(),
|
||||
});
|
||||
}
|
||||
serde_json::from_reader(reader).map_err(|error| UnitClientError::JsonError {
|
||||
source: error,
|
||||
path: path.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -258,23 +241,17 @@ impl UnitClient {
|
|||
new_openapi_client!(self, ListenersApiClient, ListenersApi)
|
||||
}
|
||||
|
||||
pub fn listeners(&self) -> Result<HashMap<String, ConfigListener>, Box<UnitClientError>> {
|
||||
let list_listeners = self.listeners_api().get_listeners();
|
||||
self.execute_openapi_future(list_listeners)
|
||||
}
|
||||
|
||||
pub fn execute_openapi_future<F: Future<Output = Result<R, OpenAPIError>>, R: for<'de> serde::Deserialize<'de>>(
|
||||
&self,
|
||||
future: F,
|
||||
) -> Result<R, Box<UnitClientError>> {
|
||||
self.rt.block_on(future).map_err(|error| {
|
||||
let remapped_error = if let OpenAPIError::Hyper(hyper_error) = error {
|
||||
UnitClientError::new(hyper_error, self.control_socket.to_string(), "".to_string())
|
||||
pub async fn listeners(&self) -> Result<HashMap<String, ConfigListener>, Box<UnitClientError>> {
|
||||
self.listeners_api().get_listeners().await.or_else(|err| {
|
||||
if let OpenAPIError::Hyper(hyper_error) = err {
|
||||
Err(Box::new(UnitClientError::new(
|
||||
hyper_error,
|
||||
self.control_socket.to_string(),
|
||||
"".to_string(),
|
||||
)))
|
||||
} else {
|
||||
UnitClientError::OpenAPIError { source: error }
|
||||
};
|
||||
|
||||
Box::new(remapped_error)
|
||||
Err(Box::new(UnitClientError::OpenAPIError { source: err }))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -282,13 +259,22 @@ impl UnitClient {
|
|||
new_openapi_client!(self, StatusApiClient, StatusApi)
|
||||
}
|
||||
|
||||
pub fn status(&self) -> Result<Status, Box<UnitClientError>> {
|
||||
let status = self.status_api().get_status();
|
||||
self.execute_openapi_future(status)
|
||||
pub async fn status(&self) -> Result<Status, Box<UnitClientError>> {
|
||||
self.status_api().get_status().await.or_else(|err| {
|
||||
if let OpenAPIError::Hyper(hyper_error) = err {
|
||||
Err(Box::new(UnitClientError::new(
|
||||
hyper_error,
|
||||
self.control_socket.to_string(),
|
||||
"".to_string(),
|
||||
)))
|
||||
} else {
|
||||
Err(Box::new(UnitClientError::OpenAPIError { source: err }))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_running(&self) -> bool {
|
||||
self.status().is_ok()
|
||||
pub async fn is_running(&self) -> bool {
|
||||
self.status().await.is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -336,9 +322,9 @@ mod tests {
|
|||
use super::*;
|
||||
// Integration tests
|
||||
|
||||
#[test]
|
||||
fn can_connect_to_unit_api() {
|
||||
match UnitdInstance::running_unitd_instances().first() {
|
||||
#[tokio::test]
|
||||
async fn can_connect_to_unit_api() {
|
||||
match UnitdInstance::running_unitd_instances().await.first() {
|
||||
Some(unit_instance) => {
|
||||
let control_api_socket_address = unit_instance
|
||||
.control_api_socket_address()
|
||||
|
@ -346,7 +332,7 @@ mod tests {
|
|||
let control_socket = ControlSocket::try_from(control_api_socket_address)
|
||||
.expect("Unable to parse control socket address");
|
||||
let unit_client = UnitClient::new(control_socket);
|
||||
assert!(unit_client.is_running());
|
||||
assert!(unit_client.is_running().await);
|
||||
}
|
||||
None => {
|
||||
eprintln!("No running unitd instances found - skipping test");
|
||||
|
@ -354,9 +340,9 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_get_unit_status() {
|
||||
match UnitdInstance::running_unitd_instances().first() {
|
||||
#[tokio::test]
|
||||
async fn can_get_unit_status() {
|
||||
match UnitdInstance::running_unitd_instances().await.first() {
|
||||
Some(unit_instance) => {
|
||||
let control_api_socket_address = unit_instance
|
||||
.control_api_socket_address()
|
||||
|
@ -364,7 +350,7 @@ mod tests {
|
|||
let control_socket = ControlSocket::try_from(control_api_socket_address)
|
||||
.expect("Unable to parse control socket address");
|
||||
let unit_client = UnitClient::new(control_socket);
|
||||
let status = unit_client.status().expect("Unable to get unit status");
|
||||
let status = unit_client.status().await.expect("Unable to get unit status");
|
||||
println!("Unit status: {:?}", status);
|
||||
}
|
||||
None => {
|
||||
|
@ -373,9 +359,9 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_get_unit_listeners() {
|
||||
match UnitdInstance::running_unitd_instances().first() {
|
||||
#[tokio::test]
|
||||
async fn can_get_unit_listeners() {
|
||||
match UnitdInstance::running_unitd_instances().await.first() {
|
||||
Some(unit_instance) => {
|
||||
let control_api_socket_address = unit_instance
|
||||
.control_api_socket_address()
|
||||
|
@ -383,7 +369,7 @@ mod tests {
|
|||
let control_socket = ControlSocket::try_from(control_api_socket_address)
|
||||
.expect("Unable to parse control socket address");
|
||||
let unit_client = UnitClient::new(control_socket);
|
||||
unit_client.listeners().expect("Unable to get Unit listeners");
|
||||
unit_client.listeners().await.expect("Unable to get Unit listeners");
|
||||
}
|
||||
None => {
|
||||
eprintln!("No running unitd instances found - skipping test");
|
||||
|
|
|
@ -28,11 +28,13 @@ impl UnitdCmd {
|
|||
.expect("Unable to parse cmd")
|
||||
.splitn(2, " [")
|
||||
.collect::<Vec<&str>>();
|
||||
|
||||
if parts.len() != 2 {
|
||||
let msg = format!("cmd does not have the expected format: {}", process_cmd);
|
||||
return Err(IoError::new(ErrorKind::InvalidInput, msg).into());
|
||||
}
|
||||
let version: Option<String> = Some(parts[0].to_string());
|
||||
|
||||
let version = Some(parts[0].to_string());
|
||||
let executable_path = UnitdCmd::parse_executable_path_from_cmd(parts[1], binary_name);
|
||||
let flags = UnitdCmd::parse_runtime_flags_from_cmd(parts[1]);
|
||||
|
||||
|
@ -69,6 +71,7 @@ impl UnitdCmd {
|
|||
if cmd.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Split out everything in between the brackets [ and ]
|
||||
let split = cmd.trim_end_matches(']').splitn(2, '[').collect::<Vec<&str>>();
|
||||
if split.is_empty() {
|
||||
|
|
282
tools/unitctl/unit-client-rs/src/unitd_docker.rs
Normal file
282
tools/unitctl/unit-client-rs/src/unitd_docker.rs
Normal file
|
@ -0,0 +1,282 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fs::read_to_string;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::unitd_process::UnitdProcess;
|
||||
|
||||
use bollard::secret::ContainerInspectResponse;
|
||||
use regex::Regex;
|
||||
use serde::ser::SerializeMap;
|
||||
use serde::{Serialize, Serializer};
|
||||
|
||||
use bollard::{models::ContainerSummary, Docker};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UnitdContainer {
|
||||
pub container_id: Option<String>,
|
||||
pub container_image: String,
|
||||
pub command: Option<String>,
|
||||
pub mounts: HashMap<PathBuf, PathBuf>,
|
||||
pub platform: String,
|
||||
details: Option<ContainerInspectResponse>,
|
||||
}
|
||||
|
||||
impl From<&ContainerSummary> for UnitdContainer {
|
||||
fn from(ctr: &ContainerSummary) -> Self {
|
||||
// we assume paths from the docker api are absolute
|
||||
// they certainly have to be later...
|
||||
let mut mounts = HashMap::new();
|
||||
if let Some(mts) = &ctr.mounts {
|
||||
for i in mts {
|
||||
if let Some(ref src) = i.source {
|
||||
if let Some(ref dest) = i.destination {
|
||||
mounts.insert(PathBuf::from(dest.clone()), PathBuf::from(src.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnitdContainer {
|
||||
container_id: ctr.id.clone(),
|
||||
container_image: format!(
|
||||
"{} (docker)",
|
||||
ctr.image.clone().unwrap_or(String::from("unknown container")),
|
||||
),
|
||||
command: ctr.command.clone(),
|
||||
mounts: mounts,
|
||||
platform: String::from("Docker"),
|
||||
details: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&UnitdContainer> for UnitdProcess {
|
||||
fn from(ctr: &UnitdContainer) -> Self {
|
||||
let version = ctr.details.as_ref().and_then(|details| {
|
||||
details.config.as_ref().and_then(|conf| {
|
||||
conf.labels.as_ref().and_then(|labels| {
|
||||
labels
|
||||
.get("org.opencontainers.image.version")
|
||||
.and_then(|version| Some(version.clone()))
|
||||
})
|
||||
})
|
||||
});
|
||||
let command = ctr.command.clone().and_then(|cmd| {
|
||||
Some(format!(
|
||||
"{}{} [{}{}]",
|
||||
"unit: main v",
|
||||
version.or(Some(String::from(""))).unwrap(),
|
||||
ctr.container_image,
|
||||
ctr.rewrite_socket(
|
||||
cmd.strip_prefix("/usr/local/bin/docker-entrypoint.sh")
|
||||
.or_else(|| Some(""))
|
||||
.unwrap()
|
||||
.to_string())
|
||||
))
|
||||
});
|
||||
let mut cmds = vec![];
|
||||
let _ = command.map_or((), |cmd| cmds.push(cmd));
|
||||
UnitdProcess {
|
||||
all_cmds: cmds,
|
||||
binary_name: ctr.container_image.clone(),
|
||||
process_id: ctr
|
||||
.details
|
||||
.as_ref()
|
||||
.and_then(|details| {
|
||||
details
|
||||
.state
|
||||
.as_ref()
|
||||
.and_then(|state| state.pid.and_then(|pid| Some(pid.clone() as u64)))
|
||||
})
|
||||
.or(Some(0 as u64))
|
||||
.unwrap(),
|
||||
executable_path: None,
|
||||
environ: vec![],
|
||||
working_dir: ctr.details.as_ref().and_then(|details| {
|
||||
details.config.as_ref().and_then(|conf| {
|
||||
Some(
|
||||
PathBuf::from(
|
||||
conf.working_dir
|
||||
.as_ref()
|
||||
.map_or(String::new(), |dir| ctr.host_path(dir.clone())),
|
||||
)
|
||||
.into_boxed_path(),
|
||||
)
|
||||
})
|
||||
}),
|
||||
child_pids: vec![],
|
||||
user: None,
|
||||
effective_user: None,
|
||||
container: Some(ctr.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for UnitdContainer {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_map(Some(5))?;
|
||||
state.serialize_entry("container_id", &self.container_id)?;
|
||||
state.serialize_entry("container_image", &self.container_image)?;
|
||||
state.serialize_entry("command", &self.command)?;
|
||||
state.serialize_entry("mounts", &self.mounts)?;
|
||||
state.serialize_entry("platform", &self.platform)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl UnitdContainer {
|
||||
pub async fn find_unitd_containers() -> Vec<UnitdContainer> {
|
||||
if let Ok(docker) = Docker::connect_with_local_defaults() {
|
||||
match docker.list_containers::<String>(None).await {
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
vec![]
|
||||
}
|
||||
Ok(summary) => {
|
||||
// cant do this functionally because of the async call
|
||||
let mut mapped = vec![];
|
||||
for ctr in summary {
|
||||
if ctr.clone().image.or(Some(String::new())).unwrap().contains("unit") {
|
||||
let mut c = UnitdContainer::from(&ctr);
|
||||
if let Some(names) = ctr.names {
|
||||
if names.len() > 0 {
|
||||
let name = names[0].strip_prefix("/").or(Some(names[0].as_str())).unwrap();
|
||||
if let Ok(cir) = docker.inspect_container(name, None).await {
|
||||
c.details = Some(cir);
|
||||
}
|
||||
}
|
||||
}
|
||||
mapped.push(c);
|
||||
}
|
||||
}
|
||||
mapped
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn host_path(&self, container_path: String) -> String {
|
||||
let cp = PathBuf::from(container_path);
|
||||
|
||||
// get only possible mount points
|
||||
// sort to deepest mountpoint first
|
||||
// assumed deepest possible mount point takes precedence
|
||||
let mut keys = self
|
||||
.mounts
|
||||
.clone()
|
||||
.into_keys()
|
||||
.filter(|mp| cp.as_path().starts_with(mp))
|
||||
.collect::<Vec<_>>();
|
||||
keys.sort_by_key(|a| 0 as isize - a.ancestors().count() as isize);
|
||||
|
||||
// either return translated path or original prefixed with "container"
|
||||
if keys.len() > 0 {
|
||||
self.mounts[&keys[0]]
|
||||
.clone()
|
||||
.join(
|
||||
cp.as_path()
|
||||
.strip_prefix(keys[0].clone())
|
||||
.expect("error checking path prefix"),
|
||||
)
|
||||
.to_string_lossy()
|
||||
.to_string()
|
||||
} else {
|
||||
format!("<container>:{}", cp.display())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rewrite_socket(&self, command: String) -> String {
|
||||
command
|
||||
.split(" ")
|
||||
.map(|tok| if tok.starts_with("unix:") {
|
||||
format!("unix:{}", self.host_path(
|
||||
tok.strip_prefix("unix:")
|
||||
.unwrap()
|
||||
.to_string()))
|
||||
} else {
|
||||
tok.to_string()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
pub fn container_is_running(&self) -> Option<bool> {
|
||||
self.details
|
||||
.as_ref()
|
||||
.and_then(|details| details.state.as_ref().and_then(|state| state.running))
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns either 64 char docker container ID or None */
|
||||
pub fn pid_is_dockerized(pid: u64) -> bool {
|
||||
let cg_filepath = format!("/proc/{}/cgroup", pid);
|
||||
match read_to_string(cg_filepath) {
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
false
|
||||
}
|
||||
Ok(contents) => {
|
||||
let docker_re = Regex::new(r"docker-([a-zA-Z0-9]{64})").unwrap();
|
||||
docker_re.is_match(contents.as_str())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_path_translation() {
|
||||
let mut mounts = HashMap::new();
|
||||
mounts.insert("/1/2/3/4/5/6/7".into(), "/0".into());
|
||||
mounts.insert("/root".into(), "/1".into());
|
||||
mounts.insert("/root/mid".into(), "/2".into());
|
||||
mounts.insert("/root/mid/child".into(), "/3".into());
|
||||
mounts.insert("/mid/child".into(), "/4".into());
|
||||
mounts.insert("/child".into(), "/5".into());
|
||||
|
||||
let ctr = UnitdContainer {
|
||||
container_id: None,
|
||||
container_image: String::from(""),
|
||||
command: None,
|
||||
platform: "test".to_string(),
|
||||
details: None,
|
||||
mounts: mounts,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
"/3/c2/test".to_string(),
|
||||
ctr.host_path("/root/mid/child/c2/test".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
"<container>:/path/to/conf".to_string(),
|
||||
ctr.host_path("/path/to/conf".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unix_sock_path_translate() {
|
||||
let mut mounts = HashMap::new();
|
||||
mounts.insert("/var/run".into(), "/tmp".into());
|
||||
|
||||
let ctr = UnitdContainer {
|
||||
container_id: None,
|
||||
container_image: String::from(""),
|
||||
command: None,
|
||||
platform: "test".to_string(),
|
||||
details: None,
|
||||
mounts: mounts,
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
ctr.rewrite_socket("unitd --no-daemon --control unix:/var/run/control.unit.sock".to_string()),
|
||||
"unitd --no-daemon --control unix:/tmp/control.unit.sock".to_string());
|
||||
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
use crate::unit_client::UnitClientError;
|
||||
use crate::unitd_docker::UnitdContainer;
|
||||
use serde::ser::SerializeMap;
|
||||
use serde::{Serialize, Serializer};
|
||||
use std::error::Error as StdError;
|
||||
|
@ -25,7 +26,7 @@ impl Serialize for UnitdInstance {
|
|||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_map(Some(15))?;
|
||||
let mut state = serializer.serialize_map(Some(11))?;
|
||||
let runtime_flags = self
|
||||
.process
|
||||
.cmd()
|
||||
|
@ -34,13 +35,9 @@ impl Serialize for UnitdInstance {
|
|||
|
||||
let configure_flags = self.configure_options.as_ref().map(|opts| opts.all_flags.clone());
|
||||
|
||||
state.serialize_entry("pid", &self.process.process_id)?;
|
||||
state.serialize_entry("process", &self.process)?;
|
||||
state.serialize_entry("version", &self.version())?;
|
||||
state.serialize_entry("user", &self.process.user)?;
|
||||
state.serialize_entry("effective_user", &self.process.effective_user)?;
|
||||
state.serialize_entry("executable", &self.process.executable_path())?;
|
||||
state.serialize_entry("control_socket", &self.control_api_socket_address())?;
|
||||
state.serialize_entry("child_pids", &self.process.child_pids)?;
|
||||
state.serialize_entry("log_path", &self.log_path())?;
|
||||
state.serialize_entry("pid_path", &self.pid_path())?;
|
||||
state.serialize_entry("modules_directory", &self.modules_directory())?;
|
||||
|
@ -56,8 +53,19 @@ impl Serialize for UnitdInstance {
|
|||
}
|
||||
|
||||
impl UnitdInstance {
|
||||
pub fn running_unitd_instances() -> Vec<UnitdInstance> {
|
||||
Self::collect_unitd_processes(UnitdProcess::find_unitd_processes())
|
||||
pub async fn running_unitd_instances() -> Vec<UnitdInstance> {
|
||||
Self::collect_unitd_processes(
|
||||
UnitdProcess::find_unitd_processes()
|
||||
.into_iter()
|
||||
.chain(
|
||||
UnitdContainer::find_unitd_containers()
|
||||
.await
|
||||
.into_iter()
|
||||
.map(|x| UnitdProcess::from(&x))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Find all running unitd processes and convert them into UnitdInstances and filter
|
||||
|
@ -91,11 +99,14 @@ impl UnitdInstance {
|
|||
pid: process.process_id,
|
||||
})?;
|
||||
Ok(new_path)
|
||||
} else {
|
||||
} else if process.container.is_none() {
|
||||
Err(UnitClientError::UnitdProcessParseError {
|
||||
message: "Unable to get absolute unitd executable path from process".to_string(),
|
||||
pid: process.process_id,
|
||||
})
|
||||
} else {
|
||||
// container case
|
||||
Ok(PathBuf::from("/").into_boxed_path())
|
||||
}
|
||||
}
|
||||
None => Err(UnitClientError::UnitdProcessParseError {
|
||||
|
@ -107,7 +118,30 @@ impl UnitdInstance {
|
|||
|
||||
fn map_process_to_unitd_instance(process: &UnitdProcess) -> UnitdInstance {
|
||||
match unitd_path_from_process(process) {
|
||||
Ok(unitd_path) => match UnitdConfigureOptions::new(&unitd_path.clone().into_path_buf()) {
|
||||
Ok(_) if process.container.is_some() => {
|
||||
let mut err = vec![];
|
||||
// double check that it is running
|
||||
let running = process.container
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.container_is_running();
|
||||
|
||||
if running.is_none() || !running.unwrap() {
|
||||
err.push(UnitClientError::UnitdProcessParseError{
|
||||
message: "process container is not running".to_string(),
|
||||
pid: process.process_id,
|
||||
});
|
||||
}
|
||||
|
||||
UnitdInstance {
|
||||
process: process.to_owned(),
|
||||
configure_options: None,
|
||||
errors: err,
|
||||
}
|
||||
},
|
||||
Ok(unitd_path) => match UnitdConfigureOptions::new(
|
||||
&unitd_path.clone()
|
||||
.into_path_buf()) {
|
||||
Ok(configure_options) => UnitdInstance {
|
||||
process: process.to_owned(),
|
||||
configure_options: Some(configure_options),
|
||||
|
@ -250,10 +284,22 @@ impl fmt::Display for UnitdInstance {
|
|||
writeln!(f, " API control unix socket: {}", socket_address)?;
|
||||
writeln!(f, " Child processes ids: {}", child_pids)?;
|
||||
writeln!(f, " Runtime flags: {}", runtime_flags)?;
|
||||
write!(f, " Configure options: {}", configure_flags)?;
|
||||
writeln!(f, " Configure options: {}", configure_flags)?;
|
||||
|
||||
if let Some(ctr) = &self.process.container {
|
||||
writeln!(f, " Container:")?;
|
||||
writeln!(f, " Platform: {}", ctr.platform)?;
|
||||
if let Some(id) = ctr.container_id.clone() {
|
||||
writeln!(f, " Container ID: {}", id)?;
|
||||
}
|
||||
writeln!(f, " Mounts:")?;
|
||||
for (k, v) in &ctr.mounts {
|
||||
writeln!(f, " {} => {}", k.to_string_lossy(), v.to_string_lossy())?;
|
||||
}
|
||||
}
|
||||
|
||||
if !self.errors.is_empty() {
|
||||
write!(f, "\n Errors:")?;
|
||||
write!(f, " Errors:")?;
|
||||
for error in &self.errors {
|
||||
write!(f, "\n {}", error)?;
|
||||
}
|
||||
|
@ -302,9 +348,9 @@ mod tests {
|
|||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
||||
30, 31,
|
||||
];
|
||||
#[test]
|
||||
fn can_find_unitd_instances() {
|
||||
UnitdInstance::running_unitd_instances().iter().for_each(|p| {
|
||||
#[tokio::test]
|
||||
async fn can_find_unitd_instances() {
|
||||
UnitdInstance::running_unitd_instances().await.iter().for_each(|p| {
|
||||
println!("{:?}", p);
|
||||
println!("Runtime Flags: {:?}", p.process.cmd().map(|c| c.flags));
|
||||
println!("Temp directory: {:?}", p.tmp_directory());
|
||||
|
@ -326,6 +372,7 @@ mod tests {
|
|||
child_pids: vec![],
|
||||
user: None,
|
||||
effective_user: None,
|
||||
container: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use crate::unitd_cmd::UnitdCmd;
|
||||
use crate::unitd_docker::{pid_is_dockerized, UnitdContainer};
|
||||
use crate::unitd_instance::UNITD_BINARY_NAMES;
|
||||
use crate::unitd_process_user::UnitdProcessUser;
|
||||
use serde::ser::SerializeMap;
|
||||
use serde::{Serialize, Serializer};
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use sysinfo::{Pid, Process, ProcessRefreshKind, System, UpdateKind, Users};
|
||||
|
@ -16,6 +19,23 @@ pub struct UnitdProcess {
|
|||
pub child_pids: Vec<u64>,
|
||||
pub user: Option<UnitdProcessUser>,
|
||||
pub effective_user: Option<UnitdProcessUser>,
|
||||
pub container: Option<UnitdContainer>,
|
||||
}
|
||||
|
||||
impl Serialize for UnitdProcess {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let mut state = serializer.serialize_map(Some(6))?;
|
||||
state.serialize_entry("pid", &self.process_id)?;
|
||||
state.serialize_entry("user", &self.user)?;
|
||||
state.serialize_entry("effective_user", &self.effective_user)?;
|
||||
state.serialize_entry("executable", &self.executable_path())?;
|
||||
state.serialize_entry("child_pids", &self.child_pids)?;
|
||||
state.serialize_entry("container", &self.container)?;
|
||||
state.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl UnitdProcess {
|
||||
|
@ -41,10 +61,15 @@ impl UnitdProcess {
|
|||
.iter()
|
||||
// Filter out child processes
|
||||
.filter(|p| {
|
||||
let parent_pid = p.1.parent();
|
||||
match parent_pid {
|
||||
Some(pid) => !unitd_processes.contains_key(&pid),
|
||||
None => false,
|
||||
#[cfg(target_os = "linux")]
|
||||
if pid_is_dockerized(p.0.as_u32().into()) {
|
||||
false
|
||||
} else {
|
||||
let parent_pid = p.1.parent();
|
||||
match parent_pid {
|
||||
Some(pid) => !unitd_processes.contains_key(&pid),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
})
|
||||
.map(|p| {
|
||||
|
@ -85,6 +110,7 @@ impl UnitdProcess {
|
|||
child_pids,
|
||||
user,
|
||||
effective_user,
|
||||
container: None,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<UnitdProcess>>()
|
||||
|
|
|
@ -18,11 +18,11 @@ const EDITOR_KNOWN_LIST: [&str; 8] = [
|
|||
"emacs",
|
||||
];
|
||||
|
||||
pub(crate) fn cmd(cli: &UnitCtl, output_format: OutputFormat) -> Result<(), UnitctlError> {
|
||||
let control_socket = wait::wait_for_socket(cli)?;
|
||||
pub(crate) async fn cmd(cli: &UnitCtl, output_format: OutputFormat) -> Result<(), UnitctlError> {
|
||||
let control_socket = wait::wait_for_socket(cli).await?;
|
||||
let client = UnitClient::new(control_socket);
|
||||
// Get latest configuration
|
||||
let current_config = send_empty_body_deserialize_response(&client, "GET", "/config")?;
|
||||
let current_config = send_empty_body_deserialize_response(&client, "GET", "/config").await?;
|
||||
|
||||
// Write JSON to temporary file - this file will automatically be deleted by the OS when
|
||||
// the last file handle to it is removed.
|
||||
|
@ -54,6 +54,7 @@ pub(crate) fn cmd(cli: &UnitCtl, output_format: OutputFormat) -> Result<(), Unit
|
|||
|
||||
// Send edited file to UNIT to overwrite current configuration
|
||||
send_and_validate_config_deserialize_response(&client, "PUT", "/config", Some(&inputfile))
|
||||
.await
|
||||
.and_then(|status| output_format.write_to_stdout(&status))
|
||||
}
|
||||
|
||||
|
|
|
@ -8,14 +8,14 @@ use crate::wait;
|
|||
use crate::{OutputFormat, UnitctlError};
|
||||
use unit_client_rs::unit_client::UnitClient;
|
||||
|
||||
pub(crate) fn cmd(
|
||||
pub(crate) async fn cmd(
|
||||
cli: &UnitCtl,
|
||||
output_format: &OutputFormat,
|
||||
input_file: &Option<String>,
|
||||
method: &str,
|
||||
path: &str,
|
||||
) -> Result<(), UnitctlError> {
|
||||
let control_socket = wait::wait_for_socket(cli)?;
|
||||
let control_socket = wait::wait_for_socket(cli).await?;
|
||||
let client = UnitClient::new(control_socket);
|
||||
|
||||
let path_trimmed = path.trim();
|
||||
|
@ -28,10 +28,10 @@ pub(crate) fn cmd(
|
|||
eprintln!("Cannot use GET method with input file - ignoring input file");
|
||||
}
|
||||
|
||||
send_and_deserialize(client, method_upper, input_file_arg, path_trimmed, output_format)
|
||||
send_and_deserialize(client, method_upper, input_file_arg, path_trimmed, output_format).await
|
||||
}
|
||||
|
||||
fn send_and_deserialize(
|
||||
async fn send_and_deserialize(
|
||||
client: UnitClient,
|
||||
method: String,
|
||||
input_file: Option<InputFile>,
|
||||
|
@ -43,7 +43,8 @@ fn send_and_deserialize(
|
|||
// If we are sending a GET request to a JS modules directory, we want to print the contents of the JS file
|
||||
// instead of the JSON response
|
||||
if method.eq("GET") && is_js_modules_dir && path.ends_with(".js") {
|
||||
let script = send_body_deserialize_response::<String>(&client, method.as_str(), path, input_file.as_ref())?;
|
||||
let script =
|
||||
send_body_deserialize_response::<String>(&client, method.as_str(), path, input_file.as_ref()).await?;
|
||||
println!("{}", script);
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -52,17 +53,17 @@ fn send_and_deserialize(
|
|||
match input_file {
|
||||
Some(input_file) => {
|
||||
if input_file.is_config() {
|
||||
send_and_validate_config_deserialize_response(&client, method.as_str(), path, Some(&input_file))
|
||||
send_and_validate_config_deserialize_response(&client, method.as_str(), path, Some(&input_file)).await
|
||||
// TLS certificate data
|
||||
} else if input_file.is_pem_bundle() {
|
||||
send_and_validate_pem_data_deserialize_response(&client, method.as_str(), path, &input_file)
|
||||
send_and_validate_pem_data_deserialize_response(&client, method.as_str(), path, &input_file).await
|
||||
// This is unknown data
|
||||
} else {
|
||||
panic!("Unknown input file type")
|
||||
}
|
||||
}
|
||||
// A none value for an input file can be considered a request to send an empty body
|
||||
None => send_empty_body_deserialize_response(&client, method.as_str(), path),
|
||||
None => send_empty_body_deserialize_response(&client, method.as_str(), path).await,
|
||||
}
|
||||
.and_then(|status| output_format.write_to_stdout(&status))
|
||||
}
|
||||
|
|
|
@ -43,24 +43,25 @@ impl UploadFormat {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn cmd(cli: &UnitCtl, directory: &PathBuf) -> Result<(), UnitctlError> {
|
||||
pub async fn cmd(cli: &UnitCtl, directory: &PathBuf) -> Result<(), UnitctlError> {
|
||||
if !directory.exists() {
|
||||
return Err(UnitctlError::PathNotFound {
|
||||
path: directory.to_string_lossy().into(),
|
||||
});
|
||||
}
|
||||
|
||||
let control_socket = wait::wait_for_socket(cli)?;
|
||||
let control_socket = wait::wait_for_socket(cli).await?;
|
||||
let client = UnitClient::new(control_socket);
|
||||
|
||||
let results: Vec<Result<(), UnitctlError>> = WalkDir::new(directory)
|
||||
let mut results = vec![];
|
||||
for i in WalkDir::new(directory)
|
||||
.follow_links(true)
|
||||
.sort_by_file_name()
|
||||
.into_iter()
|
||||
.filter_map(Result::ok)
|
||||
.filter(|e| !e.path().is_dir())
|
||||
.map(|pe| process_entry(pe, &client))
|
||||
.collect();
|
||||
{
|
||||
results.push(process_entry(i, &client).await);
|
||||
}
|
||||
|
||||
if results.iter().filter(|r| r.is_err()).count() == results.len() {
|
||||
Err(UnitctlError::NoFilesImported)
|
||||
|
@ -70,7 +71,7 @@ pub fn cmd(cli: &UnitCtl, directory: &PathBuf) -> Result<(), UnitctlError> {
|
|||
}
|
||||
}
|
||||
|
||||
fn process_entry(entry: DirEntry, client: &UnitClient) -> Result<(), UnitctlError> {
|
||||
async fn process_entry(entry: DirEntry, client: &UnitClient) -> Result<(), UnitctlError> {
|
||||
let input_file = InputFile::from(entry.path());
|
||||
if input_file.format() == InputFormat::Unknown {
|
||||
println!(
|
||||
|
@ -86,25 +87,34 @@ fn process_entry(entry: DirEntry, client: &UnitClient) -> Result<(), UnitctlErro
|
|||
|
||||
// We can't overwrite JS or PEM files, so we delete them first
|
||||
if !upload_format.can_be_overwritten() {
|
||||
let _ = requests::send_empty_body_deserialize_response(client, "DELETE", upload_path.as_str()).ok();
|
||||
let _ = requests::send_empty_body_deserialize_response(client, "DELETE", upload_path.as_str())
|
||||
.await
|
||||
.ok();
|
||||
}
|
||||
|
||||
let result = match upload_format {
|
||||
UploadFormat::Config => requests::send_and_validate_config_deserialize_response(
|
||||
client,
|
||||
"PUT",
|
||||
upload_path.as_str(),
|
||||
Some(&input_file),
|
||||
),
|
||||
UploadFormat::Config => {
|
||||
requests::send_and_validate_config_deserialize_response(
|
||||
client,
|
||||
"PUT",
|
||||
upload_path.as_str(),
|
||||
Some(&input_file),
|
||||
)
|
||||
.await
|
||||
}
|
||||
UploadFormat::PemBundle => {
|
||||
requests::send_and_validate_pem_data_deserialize_response(client, "PUT", upload_path.as_str(), &input_file)
|
||||
.await
|
||||
}
|
||||
UploadFormat::Javascript => {
|
||||
requests::send_body_deserialize_response::<UnitSerializableMap>(
|
||||
client,
|
||||
"PUT",
|
||||
upload_path.as_str(),
|
||||
Some(&input_file),
|
||||
)
|
||||
.await
|
||||
}
|
||||
UploadFormat::Javascript => requests::send_body_deserialize_response::<UnitSerializableMap>(
|
||||
client,
|
||||
"PUT",
|
||||
upload_path.as_str(),
|
||||
Some(&input_file),
|
||||
),
|
||||
};
|
||||
|
||||
match result {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use crate::{OutputFormat, UnitctlError};
|
||||
use unit_client_rs::unitd_instance::UnitdInstance;
|
||||
|
||||
pub(crate) fn cmd(output_format: OutputFormat) -> Result<(), UnitctlError> {
|
||||
let instances = UnitdInstance::running_unitd_instances();
|
||||
pub(crate) async fn cmd(output_format: OutputFormat) -> Result<(), UnitctlError> {
|
||||
let instances = UnitdInstance::running_unitd_instances().await;
|
||||
if instances.is_empty() {
|
||||
Err(UnitctlError::NoUnitInstancesError)
|
||||
} else if output_format.eq(&OutputFormat::Text) {
|
||||
|
|
|
@ -3,11 +3,12 @@ use crate::wait;
|
|||
use crate::{OutputFormat, UnitctlError};
|
||||
use unit_client_rs::unit_client::UnitClient;
|
||||
|
||||
pub fn cmd(cli: &UnitCtl, output_format: OutputFormat) -> Result<(), UnitctlError> {
|
||||
let control_socket = wait::wait_for_socket(cli)?;
|
||||
pub async fn cmd(cli: &UnitCtl, output_format: OutputFormat) -> Result<(), UnitctlError> {
|
||||
let control_socket = wait::wait_for_socket(cli).await?;
|
||||
let client = UnitClient::new(control_socket);
|
||||
client
|
||||
.listeners()
|
||||
.await
|
||||
.map_err(|e| UnitctlError::UnitClientError { source: *e })
|
||||
.and_then(|response| output_format.write_to_stdout(&response))
|
||||
}
|
||||
|
|
|
@ -3,11 +3,12 @@ use crate::wait;
|
|||
use crate::{OutputFormat, UnitctlError};
|
||||
use unit_client_rs::unit_client::UnitClient;
|
||||
|
||||
pub fn cmd(cli: &UnitCtl, output_format: OutputFormat) -> Result<(), UnitctlError> {
|
||||
let control_socket = wait::wait_for_socket(cli)?;
|
||||
pub async fn cmd(cli: &UnitCtl, output_format: OutputFormat) -> Result<(), UnitctlError> {
|
||||
let control_socket = wait::wait_for_socket(cli).await?;
|
||||
let client = UnitClient::new(control_socket);
|
||||
client
|
||||
.status()
|
||||
.await
|
||||
.map_err(|e| UnitctlError::UnitClientError { source: *e })
|
||||
.and_then(|response| output_format.write_to_stdout(&response))
|
||||
}
|
||||
|
|
|
@ -23,26 +23,27 @@ mod unitctl;
|
|||
mod unitctl_error;
|
||||
mod wait;
|
||||
|
||||
fn main() -> Result<(), UnitctlError> {
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), UnitctlError> {
|
||||
let cli = UnitCtl::parse();
|
||||
|
||||
match cli.command {
|
||||
Commands::Instances { output_format } => instances::cmd(output_format),
|
||||
Commands::Instances { output_format } => instances::cmd(output_format).await,
|
||||
|
||||
Commands::Edit { output_format } => edit::cmd(&cli, output_format),
|
||||
Commands::Edit { output_format } => edit::cmd(&cli, output_format).await,
|
||||
|
||||
Commands::Import { ref directory } => import::cmd(&cli, directory),
|
||||
Commands::Import { ref directory } => import::cmd(&cli, directory).await,
|
||||
|
||||
Commands::Execute {
|
||||
ref output_format,
|
||||
ref input_file,
|
||||
ref method,
|
||||
ref path,
|
||||
} => execute_cmd::cmd(&cli, output_format, input_file, method, path),
|
||||
} => execute_cmd::cmd(&cli, output_format, input_file, method, path).await,
|
||||
|
||||
Commands::Status { output_format } => status::cmd(&cli, output_format),
|
||||
Commands::Status { output_format } => status::cmd(&cli, output_format).await,
|
||||
|
||||
Commands::Listeners { output_format } => listeners::cmd(&cli, output_format),
|
||||
Commands::Listeners { output_format } => listeners::cmd(&cli, output_format).await,
|
||||
}
|
||||
.map_err(|error| {
|
||||
eprint_error(&error);
|
||||
|
|
|
@ -12,7 +12,7 @@ use unit_client_rs::unit_client::UnitClientError;
|
|||
|
||||
/// Send the contents of a file to the unit server
|
||||
/// We assume that the file is valid and can be sent to the server
|
||||
pub fn send_and_validate_config_deserialize_response(
|
||||
pub async fn send_and_validate_config_deserialize_response(
|
||||
client: &UnitClient,
|
||||
method: &str,
|
||||
path: &str,
|
||||
|
@ -35,20 +35,21 @@ pub fn send_and_validate_config_deserialize_response(
|
|||
let reader = KnownSize::String(json.to_string());
|
||||
|
||||
streaming_upload_deserialize_response(client, method, path, mime_type, reader)
|
||||
.await
|
||||
.map_err(|e| UnitctlError::UnitClientError { source: e })
|
||||
}
|
||||
|
||||
/// Send an empty body to the unit server
|
||||
pub fn send_empty_body_deserialize_response(
|
||||
pub async fn send_empty_body_deserialize_response(
|
||||
client: &UnitClient,
|
||||
method: &str,
|
||||
path: &str,
|
||||
) -> Result<UnitSerializableMap, UnitctlError> {
|
||||
send_body_deserialize_response(client, method, path, None)
|
||||
send_body_deserialize_response(client, method, path, None).await
|
||||
}
|
||||
|
||||
/// Send the contents of a PEM file to the unit server
|
||||
pub fn send_and_validate_pem_data_deserialize_response(
|
||||
pub async fn send_and_validate_pem_data_deserialize_response(
|
||||
client: &UnitClient,
|
||||
method: &str,
|
||||
path: &str,
|
||||
|
@ -65,6 +66,7 @@ pub fn send_and_validate_pem_data_deserialize_response(
|
|||
let known_size = KnownSize::Vec((*bytes).to_owned());
|
||||
|
||||
streaming_upload_deserialize_response(client, method, path, Some(input_file.mime_type()), known_size)
|
||||
.await
|
||||
.map_err(|e| UnitctlError::UnitClientError { source: e })
|
||||
}
|
||||
|
||||
|
@ -131,7 +133,7 @@ fn validate_pem_items(pem_items: Vec<Result<Item, UnitctlError>>) -> Result<(),
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn send_body_deserialize_response<RESPONSE: for<'de> serde::Deserialize<'de>>(
|
||||
pub async fn send_body_deserialize_response<RESPONSE: for<'de> serde::Deserialize<'de>>(
|
||||
client: &UnitClient,
|
||||
method: &str,
|
||||
path: &str,
|
||||
|
@ -143,10 +145,11 @@ pub fn send_body_deserialize_response<RESPONSE: for<'de> serde::Deserialize<'de>
|
|||
}
|
||||
None => streaming_upload_deserialize_response(client, method, path, None, KnownSize::Empty),
|
||||
}
|
||||
.await
|
||||
.map_err(|e| UnitctlError::UnitClientError { source: e })
|
||||
}
|
||||
|
||||
fn streaming_upload_deserialize_response<RESPONSE: for<'de> serde::Deserialize<'de>>(
|
||||
async fn streaming_upload_deserialize_response<RESPONSE: for<'de> serde::Deserialize<'de>>(
|
||||
client: &UnitClient,
|
||||
method: &str,
|
||||
path: &str,
|
||||
|
@ -171,5 +174,5 @@ fn streaming_upload_deserialize_response<RESPONSE: for<'de> serde::Deserialize<'
|
|||
.insert("Content-Type", content_type.parse().unwrap());
|
||||
}
|
||||
|
||||
client.send_request_and_deserialize_response(request)
|
||||
client.send_request_and_deserialize_response(request).await
|
||||
}
|
||||
|
|
|
@ -8,10 +8,10 @@ use unit_client_rs::unitd_instance::UnitdInstance;
|
|||
/// Waits for a socket to become available. Availability is tested by attempting to access the
|
||||
/// status endpoint via the control socket. When socket is available, ControlSocket instance
|
||||
/// is returned.
|
||||
pub fn wait_for_socket(cli: &UnitCtl) -> Result<ControlSocket, UnitctlError> {
|
||||
pub async fn wait_for_socket(cli: &UnitCtl) -> Result<ControlSocket, UnitctlError> {
|
||||
// Don't wait, if wait_time is not specified
|
||||
if cli.wait_time_seconds.is_none() {
|
||||
return cli.control_socket_address.instance_value_if_none().and_validate();
|
||||
return cli.control_socket_address.instance_value_if_none().await.and_validate();
|
||||
}
|
||||
|
||||
let wait_time =
|
||||
|
@ -33,7 +33,7 @@ pub fn wait_for_socket(cli: &UnitCtl) -> Result<ControlSocket, UnitctlError> {
|
|||
|
||||
attempt += 1;
|
||||
|
||||
let result = cli.control_socket_address.instance_value_if_none().and_validate();
|
||||
let result = cli.control_socket_address.instance_value_if_none().await.and_validate();
|
||||
|
||||
if let Err(error) = result {
|
||||
if error.retryable() {
|
||||
|
@ -46,7 +46,7 @@ pub fn wait_for_socket(cli: &UnitCtl) -> Result<ControlSocket, UnitctlError> {
|
|||
control_socket = result.unwrap();
|
||||
let client = UnitClient::new(control_socket.clone());
|
||||
|
||||
match client.status() {
|
||||
match client.status().await {
|
||||
Ok(_) => {
|
||||
return Ok(control_socket.to_owned());
|
||||
}
|
||||
|
@ -65,15 +65,15 @@ pub fn wait_for_socket(cli: &UnitCtl) -> Result<ControlSocket, UnitctlError> {
|
|||
}
|
||||
|
||||
trait OptionControlSocket {
|
||||
fn instance_value_if_none(&self) -> Result<ControlSocket, UnitctlError>;
|
||||
async fn instance_value_if_none(&self) -> Result<ControlSocket, UnitctlError>;
|
||||
}
|
||||
|
||||
impl OptionControlSocket for Option<ControlSocket> {
|
||||
fn instance_value_if_none(&self) -> Result<ControlSocket, UnitctlError> {
|
||||
async fn instance_value_if_none(&self) -> Result<ControlSocket, UnitctlError> {
|
||||
if let Some(control_socket) = self {
|
||||
Ok(control_socket.to_owned())
|
||||
} else {
|
||||
find_socket_address_from_instance()
|
||||
find_socket_address_from_instance().await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,8 +109,8 @@ impl ResultControlSocket<ControlSocket, UnitctlError> for Result<ControlSocket,
|
|||
}
|
||||
}
|
||||
|
||||
fn find_socket_address_from_instance() -> Result<ControlSocket, UnitctlError> {
|
||||
let instances = UnitdInstance::running_unitd_instances();
|
||||
async fn find_socket_address_from_instance() -> Result<ControlSocket, UnitctlError> {
|
||||
let instances = UnitdInstance::running_unitd_instances().await;
|
||||
if instances.is_empty() {
|
||||
return Err(UnitctlError::NoUnitInstancesError);
|
||||
} else if instances.len() > 1 {
|
||||
|
@ -127,8 +127,8 @@ fn find_socket_address_from_instance() -> Result<ControlSocket, UnitctlError> {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wait_for_unavailable_unix_socket() {
|
||||
#[tokio::test]
|
||||
async fn wait_for_unavailable_unix_socket() {
|
||||
let control_socket = ControlSocket::try_from("unix:/tmp/this_socket_does_not_exist.sock");
|
||||
let cli = UnitCtl {
|
||||
control_socket_address: Some(control_socket.unwrap()),
|
||||
|
@ -138,15 +138,17 @@ fn wait_for_unavailable_unix_socket() {
|
|||
output_format: crate::output_format::OutputFormat::JsonPretty,
|
||||
},
|
||||
};
|
||||
let error = wait_for_socket(&cli).expect_err("Expected error, but no error received");
|
||||
let error = wait_for_socket(&cli)
|
||||
.await
|
||||
.expect_err("Expected error, but no error received");
|
||||
match error {
|
||||
UnitctlError::WaitTimeoutError => {}
|
||||
_ => panic!("Expected WaitTimeoutError: {}", error),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wait_for_unavailable_tcp_socket() {
|
||||
#[tokio::test]
|
||||
async fn wait_for_unavailable_tcp_socket() {
|
||||
let control_socket = ControlSocket::try_from("http://127.0.0.1:9783456");
|
||||
let cli = UnitCtl {
|
||||
control_socket_address: Some(control_socket.unwrap()),
|
||||
|
@ -157,7 +159,9 @@ fn wait_for_unavailable_tcp_socket() {
|
|||
},
|
||||
};
|
||||
|
||||
let error = wait_for_socket(&cli).expect_err("Expected error, but no error received");
|
||||
let error = wait_for_socket(&cli)
|
||||
.await
|
||||
.expect_err("Expected error, but no error received");
|
||||
match error {
|
||||
UnitctlError::WaitTimeoutError => {}
|
||||
_ => panic!("Expected WaitTimeoutError"),
|
||||
|
|
Loading…
Reference in a new issue