feat: finish monitor in executor, daemonize executor

Signed-off-by: Catty Steve <4795515+Catty2014@user.noreply.gitee.com>
This commit is contained in:
Catty Steve
2026-01-15 11:53:30 +08:00
parent ab7cbfd730
commit d7ba65fbf4
8 changed files with 1211 additions and 37 deletions
+621 -14
View File
@@ -70,12 +70,138 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "anyhow"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "arraydeque"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236"
[[package]]
name = "async-broadcast"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
dependencies = [
"event-listener",
"event-listener-strategy",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-channel"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
dependencies = [
"concurrent-queue",
"event-listener-strategy",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-executor"
version = "1.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8"
dependencies = [
"async-task",
"concurrent-queue",
"fastrand",
"futures-lite",
"pin-project-lite",
"slab",
]
[[package]]
name = "async-io"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc"
dependencies = [
"autocfg",
"cfg-if",
"concurrent-queue",
"futures-io",
"futures-lite",
"parking",
"polling",
"rustix",
"slab",
"windows-sys 0.61.2",
]
[[package]]
name = "async-lock"
version = "3.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311"
dependencies = [
"event-listener",
"event-listener-strategy",
"pin-project-lite",
]
[[package]]
name = "async-process"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75"
dependencies = [
"async-channel",
"async-io",
"async-lock",
"async-signal",
"async-task",
"blocking",
"cfg-if",
"event-listener",
"futures-lite",
"rustix",
]
[[package]]
name = "async-recursion"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "async-signal"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c"
dependencies = [
"async-io",
"async-lock",
"atomic-waker",
"cfg-if",
"futures-core",
"futures-io",
"rustix",
"signal-hook-registry",
"slab",
"windows-sys 0.61.2",
]
[[package]]
name = "async-task"
version = "4.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
[[package]]
name = "async-trait"
version = "0.1.89"
@@ -87,12 +213,30 @@ dependencies = [
"syn",
]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "bit-vec"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.10.0"
@@ -111,6 +255,19 @@ dependencies = [
"generic-array",
]
[[package]]
name = "blocking"
version = "1.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21"
dependencies = [
"async-channel",
"async-task",
"futures-io",
"futures-lite",
"piper",
]
[[package]]
name = "bumpalo"
version = "3.19.1"
@@ -139,6 +296,26 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "cgroups-rs"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efc46cf39fc5922b840030e0e5b378ce5caa9a824a675a95c6dec2c2c9ce9468"
dependencies = [
"bit-vec",
"libc",
"log",
"nix 0.25.1",
"thiserror 1.0.69",
"zbus",
]
[[package]]
name = "chrono"
version = "0.4.42"
@@ -199,6 +376,15 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "concurrent-queue"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "config"
version = "0.15.19"
@@ -263,6 +449,12 @@ dependencies = [
"libc",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "crunchy"
version = "0.2.4"
@@ -307,6 +499,33 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "endi"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099"
[[package]]
name = "enumflags2"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef"
dependencies = [
"enumflags2_derive",
"serde",
]
[[package]]
name = "enumflags2_derive"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "env_filter"
version = "0.1.4"
@@ -330,6 +549,12 @@ dependencies = [
"log",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "erased-serde"
version = "0.4.9"
@@ -351,6 +576,27 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "event-listener"
version = "5.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab"
dependencies = [
"concurrent-queue",
"parking",
"pin-project-lite",
]
[[package]]
name = "event-listener-strategy"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93"
dependencies = [
"event-listener",
"pin-project-lite",
]
[[package]]
name = "fastrand"
version = "2.3.0"
@@ -369,6 +615,31 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-io"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-lite"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"parking",
"pin-project-lite",
]
[[package]]
name = "generic-array"
version = "0.14.7"
@@ -417,6 +688,12 @@ dependencies = [
"foldhash",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "hashlink"
version = "0.10.0"
@@ -432,6 +709,18 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "iana-time-zone"
version = "0.1.64"
@@ -456,6 +745,16 @@ dependencies = [
"cc",
]
[[package]]
name = "indexmap"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
@@ -552,6 +851,15 @@ version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "memoffset"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
dependencies = [
"autocfg",
]
[[package]]
name = "minijinja"
version = "2.14.0"
@@ -572,6 +880,31 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "nix"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
dependencies = [
"autocfg",
"bitflags 1.3.2",
"cfg-if",
"libc",
]
[[package]]
name = "nix"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
dependencies = [
"bitflags 2.10.0",
"cfg-if",
"cfg_aliases",
"libc",
"memoffset",
]
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -603,6 +936,22 @@ dependencies = [
"hashbrown 0.14.5",
]
[[package]]
name = "ordered-stream"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
dependencies = [
"futures-core",
"pin-project-lite",
]
[[package]]
name = "parking"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]]
name = "parking_lot"
version = "0.12.5"
@@ -681,6 +1030,31 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "piper"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
dependencies = [
"atomic-waker",
"fastrand",
"futures-io",
]
[[package]]
name = "polling"
version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
dependencies = [
"cfg-if",
"concurrent-queue",
"hermit-abi",
"pin-project-lite",
"rustix",
"windows-sys 0.61.2",
]
[[package]]
name = "portable-atomic"
version = "1.11.1"
@@ -696,6 +1070,15 @@ dependencies = [
"portable-atomic",
]
[[package]]
name = "proc-macro-crate"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
dependencies = [
"toml_edit",
]
[[package]]
name = "proc-macro2"
version = "1.0.103"
@@ -726,7 +1109,7 @@ version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags",
"bitflags 2.10.0",
]
[[package]]
@@ -764,7 +1147,7 @@ version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd490c5b18261893f14449cbd28cb9c0b637aebf161cd77900bfdedaff21ec32"
dependencies = [
"bitflags",
"bitflags 2.10.0",
"once_cell",
"serde",
"serde_derive",
@@ -788,7 +1171,7 @@ version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
dependencies = [
"bitflags",
"bitflags 2.10.0",
"errno",
"libc",
"linux-raw-sys",
@@ -801,12 +1184,6 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "scopeguard"
version = "1.2.0"
@@ -857,15 +1234,26 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.145"
version = "1.0.148"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c"
checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "serde_repr"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
@@ -904,6 +1292,12 @@ dependencies = [
"libc",
]
[[package]]
name = "slab"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]]
name = "smallvec"
version = "1.15.1"
@@ -920,6 +1314,12 @@ dependencies = [
"windows-sys 0.60.2",
]
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.11.1"
@@ -950,13 +1350,33 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl 1.0.69",
]
[[package]]
name = "thiserror"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
dependencies = [
"thiserror-impl",
"thiserror-impl 2.0.17",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
@@ -1029,6 +1449,18 @@ dependencies = [
"serde_core",
]
[[package]]
name = "toml_edit"
version = "0.23.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832"
dependencies = [
"indexmap",
"toml_datetime",
"toml_parser",
"winnow",
]
[[package]]
name = "toml_parser"
version = "1.0.4"
@@ -1044,6 +1476,37 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
[[package]]
name = "tracing"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
]
[[package]]
name = "typeid"
version = "1.0.3"
@@ -1062,6 +1525,17 @@ version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
[[package]]
name = "uds_windows"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9"
dependencies = [
"memoffset",
"tempfile",
"winapi",
]
[[package]]
name = "unicode-ident"
version = "1.0.22"
@@ -1088,6 +1562,7 @@ checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
dependencies = [
"getrandom 0.3.4",
"js-sys",
"serde_core",
"wasm-bindgen",
]
@@ -1157,6 +1632,28 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-core"
version = "0.62.2"
@@ -1318,6 +1815,8 @@ checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
name = "workshop-executor"
version = "0.1.0"
dependencies = [
"anyhow",
"cgroups-rs",
"chrono",
"clap",
"config",
@@ -1327,9 +1826,10 @@ dependencies = [
"minijinja",
"regex",
"serde",
"serde_json",
"shlex",
"tempfile",
"thiserror",
"thiserror 2.0.17",
"tokio",
"topological-sort",
"uuid",
@@ -1345,3 +1845,110 @@ dependencies = [
"encoding_rs",
"hashlink",
]
[[package]]
name = "zbus"
version = "5.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b622b18155f7a93d1cd2dc8c01d2d6a44e08fb9ebb7b3f9e6ed101488bad6c91"
dependencies = [
"async-broadcast",
"async-executor",
"async-io",
"async-lock",
"async-process",
"async-recursion",
"async-task",
"async-trait",
"blocking",
"enumflags2",
"event-listener",
"futures-core",
"futures-lite",
"hex",
"nix 0.30.1",
"ordered-stream",
"serde",
"serde_repr",
"tracing",
"uds_windows",
"uuid",
"windows-sys 0.61.2",
"winnow",
"zbus_macros",
"zbus_names",
"zvariant",
]
[[package]]
name = "zbus_macros"
version = "5.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cdb94821ca8a87ca9c298b5d1cbd80e2a8b67115d99f6e4551ac49e42b6a314"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
"zbus_names",
"zvariant",
"zvariant_utils",
]
[[package]]
name = "zbus_names"
version = "4.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97"
dependencies = [
"serde",
"static_assertions",
"winnow",
"zvariant",
]
[[package]]
name = "zmij"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9747e91771f56fd7893e1164abd78febd14a670ceec257caad15e051de35f06"
[[package]]
name = "zvariant"
version = "5.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2be61892e4f2b1772727be11630a62664a1826b62efa43a6fe7449521cb8744c"
dependencies = [
"endi",
"enumflags2",
"serde",
"winnow",
"zvariant_derive",
"zvariant_utils",
]
[[package]]
name = "zvariant_derive"
version = "5.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da58575a1b2b20766513b1ec59d8e2e68db2745379f961f86650655e862d2006"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
"zvariant_utils",
]
[[package]]
name = "zvariant_utils"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599"
dependencies = [
"proc-macro2",
"quote",
"serde",
"syn",
"winnow",
]
+3
View File
@@ -4,6 +4,8 @@ version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1.0.100"
cgroups-rs = "0.5.0"
chrono = { version = "0.4.42", features = ["serde"] }
clap = { version = "4.5.53", features = ["derive"] }
config = "0.15.19"
@@ -13,6 +15,7 @@ log = "0.4.28"
minijinja = "2.14.0"
regex = "1.12.2"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.148"
shlex = "1.3.0"
tempfile = "3.24.0"
thiserror = "2.0.17"
+187
View File
@@ -0,0 +1,187 @@
use crate::Cli;
use serde::Serialize;
use serde_json::Value;
use std::sync::Arc;
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::net::{UnixListener, UnixStream};
use tokio::process::{Child, Command};
use tokio::signal;
use tokio::sync::RwLock;
use tokio::time::{Duration, interval};
#[derive(Debug, Clone, Serialize)]
struct Heartbeat {
pipeline_id: String,
build_id: String,
stage: String,
timestamp: u64,
status: String,
subtasks: Vec<String>,
}
pub async fn daemon(cli: &Cli) {
let _ = std::fs::remove_file("/tmp/wsexecutor.sock");
let mut heartbeat = Arc::new(RwLock::new(Heartbeat {
pipeline_id: cli.pipeline.clone(),
build_id: cli.build_id.clone(),
stage: String::new(),
timestamp: 0,
status: String::new(),
subtasks: Vec::new(),
}));
// let heartbeat_clone = heartbeat.clone();
let heartbeat_task = {
let hb = heartbeat.clone();
tokio::spawn(async move {
let mut tick = interval(Duration::from_secs(1));
loop {
tick.tick().await;
let mut hb = hb.write().await;
hb.timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis() as u64;
// 只输出到stdout(模拟未来发送给Agent
println!("[HEARTBEAT] {}", serde_json::to_string(&*hb).unwrap());
}
})
};
let socket_task = tokio::spawn(async move {
log::info!("Creating Unix socket at /tmp/wsexecutor.sock");
let listener =
UnixListener::bind("/tmp/wsexecutor.sock").expect("Failed to bind Unix socket");
loop {
match listener.accept().await {
Ok((stream, _)) => {
tokio::spawn(handle_connection(stream));
}
Err(e) => {
log::error!("Accept error: {}", e);
break;
}
}
}
});
let execution_task = {
let hb = heartbeat.clone();
tokio::spawn(async move {
let mut hb = hb.write().await;
let mut children: Vec<(String, Child)> = vec![];
for _i in 1..=3 {
let child = Command::new("bash")
.arg("-c")
.arg("'echo running!'")
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.expect("Failed to spawn child");
let pid = child.id().unwrap_or(0).to_string();
children.push((pid.clone(), child));
hb.subtasks.push(pid.clone());
}
for (pid, mut child) in children {
let status = child.wait().await.expect("Failed to wait child");
log::info!("Subtask {} exited with: {}", pid, status);
hb.subtasks.retain(|p| p != &pid);
}
hb.stage = "Success".to_string();
})
};
tokio::select! {
_ = heartbeat_task => {
log::error!("Heartbeat task died");
}
_ = execution_task => {
log::info!("All subtasks completed");
}
_ = socket_task => {
log::error!("Socket task died");
}
_ = signal::ctrl_c() => {
log::info!("Shutting down...");
// 清理子进程
}
}
// 清理子进程(防止僵尸)
for pid in heartbeat.read().await.subtasks.clone() {
// 发送SIGTERM
let _ = Command::new("kill").arg("-TERM").arg(&pid).status().await;
}
// 清理socket
let _ = std::fs::remove_file("/tmp/wsexecutor.sock");
}
async fn handle_connection(stream: UnixStream) {
let peer_addr = match stream.peer_addr() {
Ok(addr) => format!("{:?}", addr),
Err(_) => "unknown".to_string(),
};
log::debug!("New connection from: {}", peer_addr);
let mut reader = BufReader::new(stream);
let mut line = String::new();
loop {
line.clear();
match reader.read_line(&mut line).await {
Ok(0) => {
log::debug!("Connection closed by peer: {}", peer_addr);
break;
}
Ok(_) => {
// 简单打印原始JSON(预留字段解析空间)
match serde_json::from_str::<Value>(&line) {
Ok(json) => {
// 只提取关键字段打印(字段可能变化)
let msgtype = json
.get("msgtype")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
let name = json
.get("name")
.and_then(|v| v.as_str())
.unwrap_or("unnamed");
log::info!("[{}] Message from '{}': {}", msgtype, name, line.trim());
// TODO: 字段稳定后,可扩展为结构化打印
// match msgtype {
// "ResourceUsage" => print_resource(&json),
// "Log" => print_log(&json),
// _ => log::debug!("Unknown type: {}", msgtype),
// }
}
Err(e) => {
log::warn!(
"Invalid JSON from {}: {} - Error: {}",
peer_addr,
line.trim(),
e
);
}
}
}
Err(e) => {
log::error!("Failed to read from {}: {}", peer_addr, e);
break;
}
}
}
log::debug!("Connection handler for {} exiting", peer_addr);
}
+96 -8
View File
@@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use std::time::Duration;
use tokio::process::Command;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceUsage {
@@ -12,7 +13,7 @@ pub struct ResourceUsage {
pub io_write_kb: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct RetryPolicy {
pub max_attempts: usize,
pub backoff_ms: u64,
@@ -92,7 +93,7 @@ pub async fn execute(
env_vars: HashMap<String, String>,
event_tx: Option<tokio::sync::mpsc::Sender<ExecutionEvent>>,
) -> Result<ExecutionResult, Box<dyn std::error::Error + Send + Sync>> {
let retry_policy = RetryPolicy {
let mut retry_policy = RetryPolicy {
max_attempts: 1,
backoff_ms: 0,
jitter: false,
@@ -117,14 +118,14 @@ pub async fn execute(
script_path,
pipeline_name,
username,
task_id: function.name,
task_id: function.name.clone(),
working_dir,
env_vars,
timeout,
retry_policy,
};
let mut attempt = 1;
let start_time = std::time::Instant::now();
// let start_time = std::time::Instant::now();
loop {
if let Some(tx) = &event_tx {
@@ -133,9 +134,96 @@ pub async fn execute(
timestamp: chrono::Utc::now(),
});
}
match execute_single(context, attempt, event_tx.clone()).await {
match execute_single(&context, attempt, event_tx.clone()).await {
Ok(result) => {
log::info!("Task {} succeeded on attempt {}", context.task_id, attempt);
return Ok(result);
}
Err((error, retryable)) => {
if let Some(tx) = &event_tx {
let _ = tx.send(ExecutionEvent::TaskFailed { task_id: context.task_id.clone(), error:error.clone(), retryable, attempt }).await;
}
if retryable && attempt < retry_policy.max_attempts {
attempt += 1;
log::info!("Task {} failed on attempt {}, retrying...", context.task_id, attempt);
tokio::time::sleep(Duration::from_millis(retry_policy.backoff_ms)).await;
continue;
} else {
return Err(format!("Task {} failed after {} attempts", context.task_id, attempt).into());
}
}
}
}
}
async fn execute_single(
context: &ExecutionContext,
attempt: usize,
event_tx: Option<tokio::sync::mpsc::Sender<ExecutionEvent>>,
) -> Result<ExecutionResult, (String, bool)> {
let start_time = std::time::Instant::now();
let mut command = Command::new("bash"); // TODO: 换成可变的解释器
command.current_dir(&context.working_dir);
command.envs(&context.env_vars);
command.env("WS_TASKID", &context.task_id);
command.env("WS_PIPELINE", &context.pipeline_name);
command.env("WS_ATTEMPT", attempt.to_string());
command.arg(&context.script_path); // TODO: 适配可变的解释器
let child = match command.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
{
Ok(child) => child,
Err(err) => {
return Err((format!("Failed to spawn command: {}", err), false));
}
};
let pid = child.id();
log::debug!("Task {} started with PID {}", context.task_id, pid.unwrap_or(0));
todo!();
/*
let output_handler = OutputHandler::new(); // TODO: 完成OutputHandler
let result = if let Some(timeout) = context.timeout && timeout.as_secs() > 0{
tokio::time::timeout(timeout, output_handler.wait()).await
} else {
Ok(output_handler.wait().await)
};
match result.flatten() {
Ok((exit_code, stdout_len, stderr_len, new_env)) => {
let duration = start_time.elapsed();
let result = ExecutionResult {
task_id: context.task_id.clone(),
exit_code,
success: exit_code == 0,
duration,
stdout_bytes: stdout_len,
stderr_bytes: stderr_len,
new_env_vars: new_env,
resource_usage: None,
};
if let Some(tx) = &event_tx {
let _ = tx.send(ExecutionEvent::TaskCompleted {
task_id: context.task_id.clone(),
result: result.clone(),
}).await;
}
return Ok(result)
}
Err(e) => {
return Err((format!("Execution error: {}", e), true))
}
}
*/
}
}
todo!();
}
+70 -17
View File
@@ -1,24 +1,23 @@
mod parser;
mod decorator;
mod schedule;
mod error;
mod variable;
mod builder;
mod decorator;
mod engine;
use clap::{Parser};
mod error;
mod monitor;
mod parser;
mod schedule;
mod variable;
mod daemon;
use clap::{Parser, Subcommand};
use config::Config;
use env_logger;
use crate::monitor::monitor;
use crate::daemon::daemon;
/// Honey Biscuit Workshop script executor
#[derive(Parser, Debug)]
#[command(version, about, long_about=None)]
struct Args {
/// Path to the script to execute
script: String,
/// Path to bake_base.sh
#[arg(short, long, default_value="./bake_base.sh")]
bake_base: String,
struct Cli {
/// Username of the pipeline executor
#[arg(short, long)]
username: String,
@@ -27,17 +26,71 @@ struct Args {
#[arg(short, long)]
pipeline: String,
/// Build ID of this build
#[arg(short, long)]
build_id: String,
/// Parse the script without executing
#[arg(long, default_value = "false")]
dry_run: bool,
#[command(subcommand)]
command: Option<Commands>,
}
fn main(){
#[derive(Subcommand, Debug)]
enum Commands {
/// Monitor a running task
Monitor { group: String },
/// Prepare a task
Prebake {
config: String,
},
/// Run a task
Bake {
/// Path to the script to execute
script: String,
/// Path to bake_base.sh
#[arg(short, long, default_value = "./bake_base.sh")]
bake_base: String,
},
/// Finish a task
Finalize {
config: String,
}
}
#[tokio::main]
async fn main() {
env_logger::init();
let mut settings = Config::builder()
// .add_source(config::File::with_name("_config.toml"))
.add_source(config::Environment::with_prefix("HBW_EXEC_"))
.build()
.unwrap();
let args = Args::parse();
let cli = Cli::parse();
match &cli.command {
Some(Commands::Monitor { group }) => {
log::info!("Starting monitor for group: {}", group);
let result = monitor(group, Some(1000)).await;
result.unwrap();
}
Some(Commands::Prebake { config }) => {
// builder::prebake(config);
}
Some(Commands::Bake {script, bake_base}) => {
// builder::bake(script, bake_base, username, pipeline, *dry_run);
}
Some(Commands::Finalize {config}) => {
// builder::finalize(config);
}
None => {
// Running in daemon mode
log::info!("Executor will be running in daemon mode.");
let result = daemon(&cli).await;
}
}
}
+120
View File
@@ -0,0 +1,120 @@
mod cgroups;
mod jobobject;
use serde::{Deserialize, Serialize};
use std::time::Instant;
use tokio::io::AsyncWriteExt;
#[derive(Debug, Serialize, Deserialize)]
pub struct ResourceUsageInfo {
pub msgtype: String,
pub name: String,
pub os_type: OsType,
pub statistics: UsageStats,
pub timestamp: u64,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum OsType {
Linux,
Windows,
MacOS,
}
enum AgentSocket {
Unix(String),
Tcp(String),
DryRun
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UsageStats {
pub cpu_usage_percent: f64,
pub memory_usage_bytes: u64,
pub memory_limit_bytes: i64,
// pub memory_usage_percent: f64,
pub process_count: u64,
pub cpu_usage_us: u64,
/// Internal timestamp, using std::Instant
#[serde(skip, default="default_internal_timestamp")]
_timestamp: Instant,
}
fn default_internal_timestamp() -> Instant{
Instant::now()
}
pub async fn monitor(name: &String, interval_ms: Option<u64>) -> anyhow::Result<()> {
let socket_addr = get_wsagent_socket_addr()?;
let interval = tokio::time::Duration::from_millis(interval_ms.unwrap_or(1000));
let mut socket = establish_connection(&socket_addr).await?;
let mut prev_usage = UsageStats{
cpu_usage_percent: 0.0,
memory_usage_bytes: 0,
memory_limit_bytes: 0,
process_count: 0,
cpu_usage_us: 0,
_timestamp: default_internal_timestamp(),
};
loop {
let usage:UsageStats = cgroups::get_usage(&name, prev_usage)?; // TODO: 多系统支持?
prev_usage = usage.clone();
// dbg!(&usage);
let usage_info = ResourceUsageInfo {
msgtype: "ResourceUsage".to_string(),
name: name.clone(),
os_type: OsType::Linux, // TODO: 多系统支持?
statistics: usage,
timestamp: std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?.as_millis() as u64,
};
let usage_json = serde_json::to_string(&usage_info)?;
send_usage_info(&mut socket, &usage_json).await?;
// TODO: 增加消息接收,提供(伪)双向通信功能
tokio::time::sleep(interval).await;
}
}
fn get_wsagent_socket_addr() -> anyhow::Result<AgentSocket> {
let socket_addr = std::env::var("WSAGENT_SOCK")
.map_err(|_| anyhow::anyhow!("WSAGENT_SOCK is not set!"))?;
let socket = if socket_addr.starts_with("unix://") {
AgentSocket::Unix(socket_addr.strip_prefix("unix://").unwrap().to_string())
} else if socket_addr.starts_with("/") {
AgentSocket::Unix(socket_addr)
} else if socket_addr.starts_with("tcp://") {
AgentSocket::Tcp(socket_addr.strip_prefix("tcp://").unwrap().to_string())
} else if socket_addr.starts_with("dryrun") {
AgentSocket::DryRun
} else { // TCP by default
AgentSocket::Tcp(socket_addr)
};
Ok(socket)
}
async fn send_usage_info<S>(stream: &mut S, usage_json: &str) -> anyhow::Result<()>
where S: tokio::io::AsyncWrite + Unpin
{
stream.write(usage_json.as_bytes()).await?;
stream.write_all(b"\n").await?;
Ok(())
}
async fn establish_connection(socket_addr: &AgentSocket) -> anyhow::Result<Box<dyn tokio::io::AsyncWrite + Unpin>> {
match &socket_addr {
AgentSocket::Unix(path) => {
let socket = tokio::net::UnixStream::connect(path).await?;
Ok(Box::new(socket) as Box<dyn tokio::io::AsyncWrite + Unpin>)
}
AgentSocket::Tcp(path) => {
let socket = tokio::net::TcpStream::connect(path).await?;
Ok(Box::new(socket) as Box<dyn tokio::io::AsyncWrite + Unpin>)
}
AgentSocket::DryRun => {
Ok(Box::new(tokio::io::sink()) as Box<dyn tokio::io::AsyncWrite + Unpin>)
}
}
}
+112
View File
@@ -0,0 +1,112 @@
use cgroups_rs;
use cgroups_rs::{fs::{cpu::CpuController, memory::MemController, pid::PidController}, stats::CpuAcctStats};
use log;
use cgroups_rs::fs::Cgroup;
use std::time::{Instant, Duration};
use crate::monitor::UsageStats;
#[cfg(target_os = "linux")]
pub fn get_usage(name: &String, usage_prev: crate::monitor::UsageStats) -> anyhow::Result<crate::monitor::UsageStats> {
let hierarchy = cgroups_rs::fs::hierarchies::auto();
if !hierarchy.v2() {
return Err(anyhow::anyhow!("Unsupported cgroup hierarchy, cgroups v2 required."));
}
let cgroup = Cgroup::load(hierarchy, name);
let mut cpu_usage_us: u64 = 0;
let mut cpu_usage_percent: f64 = 0.0;
let mut memory_usage_bytes: u64 = 0;
let mut memory_limit_bytes: i64 = 0;
let mut process_count: u64 = 0;
if let Some(cpu) = cgroup.controller_of::<CpuController>() {
let stats = parse_cpustat(&cpu.cpu().stat.as_str())?;
cpu_usage_us = stats.usage_usec;
}
else {
log::warn!("Failed to fetch CPU usage info!");
}
let current = Instant::now();
let delta_usage = cpu_usage_us - usage_prev.cpu_usage_us;
let delta_wall = current - usage_prev._timestamp;
if usage_prev._timestamp > current {
log::warn!("Usage stats are from the future!");
}
else if usage_prev.cpu_usage_us == 0 {
log::info!("Previous CPU usage is zero, probably a fresh start.");
}
else if delta_wall < Duration::from_millis(1) {
log::warn!("Usage stats are too close together!");
}
else {
cpu_usage_percent = delta_usage as f64 / delta_wall.as_micros() as f64 * 100.0;
// dbg!(&delta_usage, &delta_wall);
}
if let Some(mem) = cgroup.controller_of::<MemController>() {
let stats = mem.memory_stat();
memory_usage_bytes = stats.usage_in_bytes;
memory_limit_bytes = stats.limit_in_bytes;
}
if let Some(proc) = cgroup.controller_of::<PidController>() {
process_count = proc.get_pid_current()?;
}
Ok(UsageStats{
cpu_usage_us,
cpu_usage_percent,
memory_usage_bytes,
// memory_usage_percent: 0.0,
memory_limit_bytes,
process_count,
_timestamp: Instant::now(),
})
// todo!();
}
#[derive(Debug, Default, Clone)]
pub struct CgroupCpuStat {
pub usage_usec: u64,
pub user_usec: u64,
pub system_usec: u64,
pub nice_usec: u64,
pub core_sched_force_idle_usec: u64,
pub nr_periods: u64,
pub nr_throttled: u64,
pub throttled_usec: u64,
pub nr_bursts: u64,
pub burst_usec: u64,
}
fn parse_cpustat(stat: &str) -> anyhow::Result<CgroupCpuStat> {
let mut cpu_stat = CgroupCpuStat::default();
for line in stat.lines() {
let line = line.trim();
if line.is_empty() {
continue;
}
let (key, value_str) = line
.split_once(' ')
.ok_or_else(|| anyhow::anyhow!("Invalid format:{}", line))?;
let value = value_str
.parse::<u64>()
.map_err(|e| anyhow::anyhow!("Cannot convert value {}({}) into integer: {}", key, value_str, e))?;
match key {
"usage_usec" => cpu_stat.usage_usec = value,
"user_usec" => cpu_stat.user_usec = value,
"system_usec" => cpu_stat.system_usec = value,
"nice_usec" => cpu_stat.nice_usec = value,
"core_sched.force_idle_usec" => cpu_stat.core_sched_force_idle_usec = value,
"nr_periods" => cpu_stat.nr_periods = value,
"nr_throttled" => cpu_stat.nr_throttled = value,
"throttled_usec" => cpu_stat.throttled_usec = value,
"nr_bursts" => cpu_stat.nr_bursts = value,
"burst_usec" => cpu_stat.burst_usec = value,
_unknown_key => {}, // Ignore unknown keys
}
}
Ok(cpu_stat)
}
@@ -0,0 +1,4 @@
#[cfg(target_os = "windows")]
pub fn get_usage() -> anyhow::Result<f64> {
unimplemented!("Not supporting windows JobObject at this time!");
}