diff --git a/src/libs/kata-types/src/config/agent.rs b/src/libs/kata-types/src/config/agent.rs index ef9546ca9d..b5bcf5b3b5 100644 --- a/src/libs/kata-types/src/config/agent.rs +++ b/src/libs/kata-types/src/config/agent.rs @@ -33,9 +33,29 @@ pub struct Agent { #[serde(default)] pub debug_console_enabled: bool, - /// Agent connection dialing timeout value in seconds + /// Agent server port #[serde(default)] - pub dial_timeout: u32, + pub server_port: u32, + + /// Agent log port + #[serde(default)] + pub log_port: u32, + + /// Agent connection dialing timeout value in millisecond + #[serde(default = "default_dial_timeout")] + pub dial_timeout_ms: u32, + + /// Agent reconnect timeout value in millisecond + #[serde(default = "default_reconnect_timeout")] + pub reconnect_timeout_ms: u32, + + /// Agent request timeout value in millisecond + #[serde(default = "default_request_timeout")] + pub request_timeout_ms: u32, + + /// Agent health check request timeout value in millisecond + #[serde(default = "default_health_check_timeout")] + pub health_check_request_timeout_ms: u32, /// Comma separated list of kernel modules and their parameters. /// @@ -55,6 +75,26 @@ pub struct Agent { pub container_pipe_size: u32, } +fn default_dial_timeout() -> u32 { + // 10ms + 10 +} + +fn default_reconnect_timeout() -> u32 { + // 3s + 3_000 +} + +fn default_request_timeout() -> u32 { + // 30s + 30_000 +} + +fn default_health_check_timeout() -> u32 { + // 90s + 90_000 +} + impl ConfigOps for Agent { fn adjust_config(conf: &mut TomlConfig) -> Result<()> { AgentVendor::adjust_config(conf)?; diff --git a/src/libs/kata-types/src/config/mod.rs b/src/libs/kata-types/src/config/mod.rs index 435b8d1f06..52c9a0e3c0 100644 --- a/src/libs/kata-types/src/config/mod.rs +++ b/src/libs/kata-types/src/config/mod.rs @@ -21,7 +21,7 @@ pub mod default; mod agent; pub mod hypervisor; -use self::agent::Agent; +pub use self::agent::Agent; pub use self::hypervisor::{ BootInfo, DragonballConfig, Hypervisor, QemuConfig, HYPERVISOR_NAME_DRAGONBALL, HYPERVISOR_NAME_QEMU, diff --git a/src/runtime-rs/Cargo.lock b/src/runtime-rs/Cargo.lock index 352d21cd6e..7f3db194da 100644 --- a/src/runtime-rs/Cargo.lock +++ b/src/runtime-rs/Cargo.lock @@ -17,6 +17,27 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "agent" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "futures 0.1.31", + "kata-types", + "log", + "oci", + "protobuf", + "protocols", + "serde", + "serde_json", + "slog", + "slog-scope", + "tokio", + "ttrpc", + "url", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -28,9 +49,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.55" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "159bb86af3a200e19a068f4224eae4c8bb2d0fa054c7e5d1cacd5cef95e684cd" +checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" [[package]] name = "arc-swap" @@ -178,9 +199,9 @@ checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" [[package]] name = "crossbeam-channel" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" +checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -188,14 +209,25 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" dependencies = [ "cfg-if 1.0.0", "lazy_static", ] +[[package]] +name = "derive-new" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "digest" version = "0.9.0" @@ -205,6 +237,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "enum-iterator" version = "0.7.0" @@ -245,6 +283,12 @@ dependencies = [ "instant", ] +[[package]] +name = "fixedbitset" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" + [[package]] name = "form_urlencoded" version = "1.0.1" @@ -255,6 +299,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + [[package]] name = "futures" version = "0.3.21" @@ -356,13 +406,13 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" +checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.10.2+wasi-snapshot-preview1", ] [[package]] @@ -385,9 +435,9 @@ checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" [[package]] name = "git2" -version = "0.13.25" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29229cc1b24c0e6062f6e742aa3e256492a5323365e5ed3413599f8a5eff7d6" +checksum = "3826a6e0e2215d7a41c2bfc7c9244123969273f3476b939a226aac0ab56e9e3c" dependencies = [ "bitflags", "libc", @@ -411,6 +461,21 @@ dependencies = [ "cfg-if 0.1.10", ] +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -431,6 +496,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indexmap" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" +dependencies = [ + "autocfg", + "hashbrown", +] + [[package]] name = "instant" version = "0.1.12" @@ -449,6 +524,15 @@ dependencies = [ "libc", ] +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.1" @@ -510,15 +594,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.119" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" +checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" [[package]] name = "libgit2-sys" -version = "0.12.26+1.3.0" +version = "0.13.2+1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e1c899248e606fbfe68dcb31d8b0176ebab833b103824af31bddf4b7457494" +checksum = "3a42de9a51a5c12e00fc0e4ca6bc2ea43582fc6418488e8f615e905d886f258b" dependencies = [ "cc", "libc", @@ -528,9 +612,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66" +checksum = "6f35facd4a5673cb5a48822be2be1d4236c1c99cb4113cab7061ac720d5bf859" dependencies = [ "cc", "libc", @@ -600,14 +684,15 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2" +checksum = "52da4364ffb0e4fe33a9841a98a3f3014fb964045ce4f7a45a398243c8d6b0c9" dependencies = [ "libc", "log", "miow", "ntapi", + "wasi 0.11.0+wasi-snapshot-preview1", "winapi", ] @@ -620,6 +705,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "multimap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" + [[package]] name = "nix" version = "0.16.1" @@ -686,9 +777,9 @@ dependencies = [ [[package]] name = "num_threads" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ba99ba6393e2c3734791401b66902d981cb03bf190af674ca69949b6d5fb15" +checksum = "aba1801fb138d8e85e11d0fc70baf4fe1cdfffda7c6cd34a854905df588e5ed0" dependencies = [ "libc", ] @@ -755,6 +846,16 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +[[package]] +name = "petgraph" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "pin-project-lite" version = "0.2.8" @@ -812,11 +913,66 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "prost" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de5e2533f59d08fcf364fd374ebda0692a70bd6d7e66ef97f306f45c6c5d8020" +dependencies = [ + "bytes 1.1.0", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603" +dependencies = [ + "bytes 1.1.0", + "heck", + "itertools", + "log", + "multimap", + "petgraph", + "prost", + "prost-types", + "tempfile", + "which", +] + +[[package]] +name = "prost-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "600d2f334aa05acb02a755e217ef1ab6dea4d51b58b7846588b747edec04efba" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "603bbd6394701d13f3f25aada59c7de9d35a6a5887cfc156181234a44002771b" +dependencies = [ + "bytes 1.1.0", + "prost", +] + [[package]] name = "protobuf" version = "2.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf7e6d18738ecd0902d30d1ad232c9125985a3422929b16c65517b38adc14f96" +dependencies = [ + "serde", + "serde_derive", +] [[package]] name = "protobuf-codegen" @@ -837,11 +993,22 @@ dependencies = [ "protobuf-codegen", ] +[[package]] +name = "protocols" +version = "0.1.0" +dependencies = [ + "async-trait", + "oci", + "protobuf", + "ttrpc", + "ttrpc-codegen", +] + [[package]] name = "quote" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" +checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57" dependencies = [ "proc-macro2", ] @@ -878,18 +1045,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.5.4" +version = "1.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" dependencies = [ "aho-corasick", "memchr", @@ -1132,9 +1299,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.86" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" +checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54" dependencies = [ "proc-macro2", "quote", @@ -1268,7 +1435,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e0723fc001950a3b018947b05eeb45014fd2b7c6e8f292502193ab74486bdb6" dependencies = [ "bytes 0.4.12", - "futures", + "futures 0.3.21", "libc", "tokio", "vsock", @@ -1291,7 +1458,7 @@ checksum = "0c7d6c992964a013c17814c08d31708d577b0aae44ebadb58755659dd824c2d1" dependencies = [ "async-trait", "byteorder", - "futures", + "futures 0.3.21", "libc", "log", "nix 0.23.1", @@ -1302,6 +1469,33 @@ dependencies = [ "tokio-vsock", ] +[[package]] +name = "ttrpc-codegen" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809eda4e459820237104e4b61d6b41bbe6c9e1ce6adf4057955e6e6722a90408" +dependencies = [ + "protobuf", + "protobuf-codegen", + "protobuf-codegen-pure", + "ttrpc-compiler", +] + +[[package]] +name = "ttrpc-compiler" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2978ed3fa047d8fd55cbeb4d4a61d461fb3021a90c9618519c73ce7e5bb66c15" +dependencies = [ + "derive-new", + "prost", + "prost-build", + "prost-types", + "protobuf", + "protobuf-codegen", + "tempfile", +] + [[package]] name = "typenum" version = "1.15.0" @@ -1323,6 +1517,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" + [[package]] name = "unicode-xid" version = "0.2.2" @@ -1401,6 +1601,23 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "which" +version = "4.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +dependencies = [ + "either", + "lazy_static", + "libc", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/src/runtime-rs/Cargo.toml b/src/runtime-rs/Cargo.toml index 414b707258..b359f9d1fb 100644 --- a/src/runtime-rs/Cargo.toml +++ b/src/runtime-rs/Cargo.toml @@ -1,4 +1,6 @@ [workspace] members = [ "crates/shim", + # TODO: current only for check, delete after use the agent crate + "crates/agent", ] diff --git a/src/runtime-rs/crates/agent/Cargo.toml b/src/runtime-rs/crates/agent/Cargo.toml new file mode 100644 index 0000000000..bd17f82d00 --- /dev/null +++ b/src/runtime-rs/crates/agent/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "agent" +version = "0.1.0" +authors = ["The Kata Containers community "] +edition = "2018" + +[dev-dependencies] +futures = "0.1.27" + +[dependencies] +anyhow = "1.0.26" +async-trait = "0.1.48" +log = "0.4.14" +protobuf = "2.23.0" +serde = { version = "^1.0", features = ["derive"] } +serde_json = ">=1.0.9" +slog = "2.5.2" +slog-scope = "4.4.0" +ttrpc = { version = "0.6.0" } +tokio = { version = "1.8.0", features = ["fs", "rt"] } +url = "2.2.2" + +kata-types = { path = "../../../libs/kata-types"} +oci = { path = "../../../libs/oci" } +protocols = { path = "../../../libs/protocols", features=["async"] } + +[features] +default = [] diff --git a/src/runtime-rs/crates/agent/src/kata/agent.rs b/src/runtime-rs/crates/agent/src/kata/agent.rs new file mode 100644 index 0000000000..9f8b4304d3 --- /dev/null +++ b/src/runtime-rs/crates/agent/src/kata/agent.rs @@ -0,0 +1,110 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use anyhow::{Context, Result}; +use async_trait::async_trait; +use ttrpc::context as ttrpc_ctx; + +use crate::{kata::KataAgent, Agent, AgentManager, HealthService}; + +/// millisecond to nanosecond +const MILLISECOND_TO_NANOSECOND: i64 = 1_000_000; + +/// new ttrpc context with timeout +fn new_ttrpc_ctx(timeout: i64) -> ttrpc_ctx::Context { + ttrpc_ctx::with_timeout(timeout) +} + +#[async_trait] +impl AgentManager for KataAgent { + async fn set_socket_address(&self, address: &str) -> Result<()> { + let mut inner = self.inner.lock().await; + inner.socket_address = address.to_string(); + Ok(()) + } + + async fn start(&self) -> Result<()> { + info!(sl!(), "begin to connect agent"); + self.connect_agent_server() + .await + .context("connect agent server")?; + self.start_log_forwarder() + .await + .context("connect log forwarder")?; + Ok(()) + } + + async fn stop(&self) { + self.stop_log_forwarder().await; + } +} + +// implement for health service +macro_rules! impl_health_service { + ($($name: tt | $req: ty | $resp: ty),*) => { + #[async_trait] + impl HealthService for KataAgent { + $(async fn $name(&self, req: $req) -> Result<$resp> { + let r = req.into(); + let (mut client, timeout, _) = self.get_health_client().await.context("get health client")?; + let resp = client.$name(new_ttrpc_ctx(timeout * MILLISECOND_TO_NANOSECOND), &r).await?; + Ok(resp.into()) + })* + } + }; +} + +impl_health_service!( + check | crate::CheckRequest | crate::HealthCheckResponse, + version | crate::CheckRequest | crate::VersionCheckResponse +); + +macro_rules! impl_agent { + ($($name: tt | $req: ty | $resp: ty | $new_timeout: expr),*) => { + #[async_trait] + impl Agent for KataAgent { + $(async fn $name(&self, req: $req) -> Result<$resp> { + let r = req.into(); + let (mut client, mut timeout, _) = self.get_agent_client().await.context("get client")?; + + // update new timeout + if let Some(v) = $new_timeout { + timeout = v; + } + + let resp = client.$name(new_ttrpc_ctx(timeout * MILLISECOND_TO_NANOSECOND), &r).await?; + Ok(resp.into()) + })* + } + }; +} + +impl_agent!( + create_container | crate::CreateContainerRequest | crate::Empty | None, + start_container | crate::ContainerID | crate::Empty | None, + remove_container | crate::RemoveContainerRequest | crate::Empty | None, + exec_process | crate::ExecProcessRequest | crate::Empty | None, + signal_process | crate::SignalProcessRequest | crate::Empty | None, + wait_process | crate::WaitProcessRequest | crate::WaitProcessResponse | Some(0), + update_container | crate::UpdateContainerRequest | crate::Empty | None, + stats_container | crate::ContainerID | crate::StatsContainerResponse | None, + pause_container | crate::ContainerID | crate::Empty | None, + resume_container | crate::ContainerID | crate::Empty | None, + write_stdin | crate::WriteStreamRequest | crate::WriteStreamResponse | None, + read_stdout | crate::ReadStreamRequest | crate::ReadStreamResponse | None, + read_stderr | crate::ReadStreamRequest | crate::ReadStreamResponse | None, + close_stdin | crate::CloseStdinRequest | crate::Empty | None, + tty_win_resize | crate::TtyWinResizeRequest | crate::Empty | None, + update_interface | crate::UpdateInterfaceRequest | crate::Interface | None, + update_routes | crate::UpdateRoutesRequest | crate::Routes | None, + add_arp_neighbors | crate::AddArpNeighborRequest | crate::Empty | None, + list_interfaces | crate::Empty | crate::Interfaces | None, + list_routes | crate::Empty | crate::Routes | None, + create_sandbox | crate::CreateSandboxRequest | crate::Empty | None, + destroy_sandbox | crate::Empty | crate::Empty | None, + copy_file | crate::CopyFileRequest | crate::Empty | None, + get_oom_event | crate::Empty | crate::OomEventResponse | Some(0) +); diff --git a/src/runtime-rs/crates/agent/src/kata/mod.rs b/src/runtime-rs/crates/agent/src/kata/mod.rs new file mode 100644 index 0000000000..043b9aa14e --- /dev/null +++ b/src/runtime-rs/crates/agent/src/kata/mod.rs @@ -0,0 +1,123 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +mod agent; +mod trans; + +use std::os::unix::io::{IntoRawFd, RawFd}; + +use anyhow::{Context, Result}; +use kata_types::config::Agent as AgentConfig; +use protocols::{agent_ttrpc_async as agent_ttrpc, health_ttrpc_async as health_ttrpc}; +use tokio::sync::Mutex; +use ttrpc::asynchronous::Client; + +use crate::{log_forwarder::LogForwarder, sock}; + +// https://github.com/firecracker-microvm/firecracker/blob/master/docs/vsock.md +#[derive(Debug, Default)] +pub struct Vsock { + pub context_id: u64, + pub port: u32, +} + +pub(crate) struct KataAgentInner { + /// TTRPC client + pub client: Option, + + /// Client fd + pub client_fd: RawFd, + + /// Unix domain socket address + pub socket_address: String, + + /// Agent config + config: AgentConfig, + + /// Log forwarder + log_forwarder: LogForwarder, +} + +unsafe impl Send for KataAgent {} +unsafe impl Sync for KataAgent {} +pub struct KataAgent { + pub(crate) inner: Mutex, +} + +impl KataAgent { + pub fn new(config: AgentConfig) -> Self { + KataAgent { + inner: Mutex::new(KataAgentInner { + client: None, + client_fd: -1, + socket_address: "".to_string(), + config, + log_forwarder: LogForwarder::new(), + }), + } + } + + pub async fn get_health_client(&self) -> Option<(health_ttrpc::HealthClient, i64, RawFd)> { + let inner = self.inner.lock().await; + inner.client.as_ref().map(|c| { + ( + health_ttrpc::HealthClient::new(c.clone()), + inner.config.health_check_request_timeout_ms as i64, + inner.client_fd, + ) + }) + } + + pub async fn get_agent_client(&self) -> Option<(agent_ttrpc::AgentServiceClient, i64, RawFd)> { + let inner = self.inner.lock().await; + inner.client.as_ref().map(|c| { + ( + agent_ttrpc::AgentServiceClient::new(c.clone()), + inner.config.request_timeout_ms as i64, + inner.client_fd, + ) + }) + } + + pub(crate) async fn connect_agent_server(&self) -> Result<()> { + let mut inner = self.inner.lock().await; + + let config = sock::ConnectConfig::new( + inner.config.dial_timeout_ms as u64, + inner.config.reconnect_timeout_ms as u64, + ); + let sock = + sock::new(&inner.socket_address, inner.config.server_port).context("new sock")?; + let stream = sock.connect(&config).await.context("connect")?; + let fd = stream.into_raw_fd(); + info!(sl!(), "get stream raw fd {:?}", fd); + let c = Client::new(fd); + inner.client = Some(c); + inner.client_fd = fd; + Ok(()) + } + + pub(crate) async fn start_log_forwarder(&self) -> Result<()> { + let mut inner = self.inner.lock().await; + let config = sock::ConnectConfig::new( + inner.config.dial_timeout_ms as u64, + inner.config.reconnect_timeout_ms as u64, + ); + let address = inner.socket_address.clone(); + let port = inner.config.log_port; + inner + .log_forwarder + .start(&address, port, config) + .await + .context("start log forwarder")?; + Ok(()) + } + + pub(crate) async fn stop_log_forwarder(&self) { + let mut inner = self.inner.lock().await; + inner.log_forwarder.stop(); + } +} diff --git a/src/runtime-rs/crates/agent/src/kata/trans.rs b/src/runtime-rs/crates/agent/src/kata/trans.rs new file mode 100644 index 0000000000..c8e4dbca7c --- /dev/null +++ b/src/runtime-rs/crates/agent/src/kata/trans.rs @@ -0,0 +1,794 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use std::convert::Into; + +use protocols::{ + agent::{self, OOMEvent}, + empty, health, types, +}; + +use crate::{ + types::{ + ARPNeighbor, ARPNeighbors, AddArpNeighborRequest, AgentDetails, BlkioStats, + BlkioStatsEntry, CgroupStats, CheckRequest, CloseStdinRequest, ContainerID, + CopyFileRequest, CpuStats, CpuUsage, CreateContainerRequest, CreateSandboxRequest, Device, + Empty, ExecProcessRequest, GuestDetailsResponse, HealthCheckResponse, HugetlbStats, + IPAddress, IPFamily, Interface, Interfaces, KernelModule, MemHotplugByProbeRequest, + MemoryData, MemoryStats, NetworkStats, OnlineCPUMemRequest, PidsStats, ReadStreamRequest, + ReadStreamResponse, RemoveContainerRequest, ReseedRandomDevRequest, Route, Routes, + SetGuestDateTimeRequest, SignalProcessRequest, StatsContainerResponse, Storage, StringUser, + ThrottlingData, TtyWinResizeRequest, UpdateContainerRequest, UpdateInterfaceRequest, + UpdateRoutesRequest, VersionCheckResponse, WaitProcessRequest, WriteStreamRequest, + }, + OomEventResponse, WaitProcessResponse, WriteStreamResponse, +}; + +fn from_vec, T: Sized>(from: Vec) -> ::protobuf::RepeatedField { + let mut to: Vec = vec![]; + for data in from { + to.push(data.into()); + } + ::protobuf::RepeatedField::from_vec(to) +} + +fn into_vec>(from: ::protobuf::RepeatedField) -> Vec { + let mut to: Vec = vec![]; + for data in from.to_vec() { + to.push(data.into()); + } + to +} + +fn from_option>(from: Option) -> ::protobuf::SingularPtrField { + match from { + Some(f) => ::protobuf::SingularPtrField::from_option(Some(T::from(f))), + None => ::protobuf::SingularPtrField::none(), + } +} + +fn into_option, T: Sized>(from: ::protobuf::SingularPtrField) -> Option { + from.into_option().map(|f| f.into()) +} + +fn into_hash_map, T>( + from: std::collections::HashMap, +) -> std::collections::HashMap { + let mut to: std::collections::HashMap = Default::default(); + + for (key, value) in from { + to.insert(key, value.into()); + } + + to +} + +impl From for Empty { + fn from(_: empty::Empty) -> Self { + Self {} + } +} + +impl From for agent::StringUser { + fn from(from: StringUser) -> Self { + Self { + uid: from.uid, + gid: from.gid, + additionalGids: ::protobuf::RepeatedField::from_vec(from.additional_gids), + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::Device { + fn from(from: Device) -> Self { + Self { + id: from.id, + field_type: from.field_type, + vm_path: from.vm_path, + container_path: from.container_path, + options: from_vec(from.options), + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::Storage { + fn from(from: Storage) -> Self { + Self { + driver: from.driver, + driver_options: from_vec(from.driver_options), + source: from.source, + fstype: from.fs_type, + options: from_vec(from.options), + mount_point: from.mount_point, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::KernelModule { + fn from(from: KernelModule) -> Self { + Self { + name: from.name, + parameters: from_vec(from.parameters), + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for types::IPFamily { + fn from(from: IPFamily) -> Self { + if from == IPFamily::V4 { + types::IPFamily::v4 + } else { + types::IPFamily::v6 + } + } +} + +impl From for IPFamily { + fn from(src: types::IPFamily) -> Self { + match src { + types::IPFamily::v4 => IPFamily::V4, + types::IPFamily::v6 => IPFamily::V6, + } + } +} + +impl From for types::IPAddress { + fn from(from: IPAddress) -> Self { + Self { + family: from.family.into(), + address: from.address, + mask: from.mask, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for IPAddress { + fn from(src: types::IPAddress) -> Self { + Self { + family: src.family.into(), + address: "".to_string(), + mask: "".to_string(), + } + } +} + +impl From for types::Interface { + fn from(from: Interface) -> Self { + Self { + device: from.device, + name: from.name, + IPAddresses: from_vec(from.ip_addresses), + mtu: from.mtu, + hwAddr: from.hw_addr, + pciPath: from.pci_addr, + field_type: from.field_type, + raw_flags: from.raw_flags, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for Interface { + fn from(src: types::Interface) -> Self { + Self { + device: src.device, + name: src.name, + ip_addresses: into_vec(src.IPAddresses), + mtu: src.mtu, + hw_addr: src.hwAddr, + pci_addr: src.pciPath, + field_type: src.field_type, + raw_flags: src.raw_flags, + } + } +} + +impl From for Interfaces { + fn from(src: agent::Interfaces) -> Self { + Self { + interfaces: into_vec(src.Interfaces), + } + } +} + +impl From for types::Route { + fn from(from: Route) -> Self { + Self { + dest: from.dest, + gateway: from.gateway, + device: from.device, + source: from.source, + scope: from.scope, + family: from.family.into(), + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for Route { + fn from(src: types::Route) -> Self { + Self { + dest: src.dest, + gateway: src.gateway, + device: src.device, + source: src.source, + scope: src.scope, + family: src.family.into(), + } + } +} + +impl From for agent::Routes { + fn from(from: Routes) -> Self { + Self { + Routes: from_vec(from.routes), + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for Routes { + fn from(src: agent::Routes) -> Self { + Self { + routes: into_vec(src.Routes), + } + } +} + +impl From for agent::CreateContainerRequest { + fn from(from: CreateContainerRequest) -> Self { + Self { + container_id: from.container_id, + exec_id: from.exec_id, + string_user: from_option(from.string_user), + devices: from_vec(from.devices), + storages: from_vec(from.storages), + OCI: from_option(from.oci), + sandbox_pidns: from.sandbox_pidns, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::RemoveContainerRequest { + fn from(from: RemoveContainerRequest) -> Self { + Self { + container_id: from.container_id, + timeout: from.timeout, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::StartContainerRequest { + fn from(from: ContainerID) -> Self { + Self { + container_id: from.container_id, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::StatsContainerRequest { + fn from(from: ContainerID) -> Self { + Self { + container_id: from.container_id, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::PauseContainerRequest { + fn from(from: ContainerID) -> Self { + Self { + container_id: from.container_id, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::ResumeContainerRequest { + fn from(from: ContainerID) -> Self { + Self { + container_id: from.container_id, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::SignalProcessRequest { + fn from(from: SignalProcessRequest) -> Self { + Self { + container_id: from.container_id, + exec_id: from.exec_id, + signal: from.signal, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::WaitProcessRequest { + fn from(from: WaitProcessRequest) -> Self { + Self { + container_id: from.container_id, + exec_id: from.exec_id, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::UpdateContainerRequest { + fn from(from: UpdateContainerRequest) -> Self { + Self { + container_id: from.container_id, + resources: from_option(Some(from.resources)), + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::WriteStreamRequest { + fn from(from: WriteStreamRequest) -> Self { + Self { + container_id: from.container_id, + exec_id: from.exec_id, + data: from.data, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for WriteStreamResponse { + fn from(from: agent::WriteStreamResponse) -> Self { + Self { length: from.len } + } +} + +impl From for agent::ExecProcessRequest { + fn from(from: ExecProcessRequest) -> Self { + Self { + container_id: from.container_id, + exec_id: from.exec_id, + string_user: from_option(from.string_user), + process: from_option(from.process), + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for CpuUsage { + fn from(src: agent::CpuUsage) -> Self { + Self { + total_usage: src.total_usage, + percpu_usage: src.percpu_usage, + usage_in_kernelmode: src.usage_in_kernelmode, + usage_in_usermode: src.usage_in_usermode, + } + } +} + +impl From for ThrottlingData { + fn from(src: agent::ThrottlingData) -> Self { + Self { + periods: src.periods, + throttled_periods: src.throttled_periods, + throttled_time: src.throttled_time, + } + } +} + +impl From for CpuStats { + fn from(src: agent::CpuStats) -> Self { + Self { + cpu_usage: into_option(src.cpu_usage), + throttling_data: into_option(src.throttling_data), + } + } +} + +impl From for MemoryData { + fn from(src: agent::MemoryData) -> Self { + Self { + usage: src.usage, + max_usage: src.max_usage, + failcnt: src.failcnt, + limit: src.limit, + } + } +} + +impl From for MemoryStats { + fn from(src: agent::MemoryStats) -> Self { + Self { + cache: src.cache, + usage: into_option(src.usage), + swap_usage: into_option(src.swap_usage), + kernel_usage: into_option(src.kernel_usage), + use_hierarchy: src.use_hierarchy, + stats: into_hash_map(src.stats), + } + } +} + +impl From for PidsStats { + fn from(src: agent::PidsStats) -> Self { + Self { + current: src.current, + limit: src.limit, + } + } +} + +impl From for BlkioStatsEntry { + fn from(src: agent::BlkioStatsEntry) -> Self { + Self { + major: src.major, + minor: src.minor, + op: src.op, + value: src.value, + } + } +} + +impl From for BlkioStats { + fn from(src: agent::BlkioStats) -> Self { + Self { + io_service_bytes_recursive: into_vec(src.io_service_bytes_recursive), + io_serviced_recursive: into_vec(src.io_serviced_recursive), + io_queued_recursive: into_vec(src.io_queued_recursive), + io_service_time_recursive: into_vec(src.io_service_time_recursive), + io_wait_time_recursive: into_vec(src.io_wait_time_recursive), + io_merged_recursive: into_vec(src.io_merged_recursive), + io_time_recursive: into_vec(src.io_time_recursive), + sectors_recursive: into_vec(src.sectors_recursive), + } + } +} + +impl From for HugetlbStats { + fn from(src: agent::HugetlbStats) -> Self { + Self { + usage: src.usage, + max_usage: src.max_usage, + failcnt: src.failcnt, + } + } +} + +impl From for CgroupStats { + fn from(src: agent::CgroupStats) -> Self { + Self { + cpu_stats: into_option(src.cpu_stats), + memory_stats: into_option(src.memory_stats), + pids_stats: into_option(src.pids_stats), + blkio_stats: into_option(src.blkio_stats), + hugetlb_stats: into_hash_map(src.hugetlb_stats), + } + } +} + +impl From for NetworkStats { + fn from(src: agent::NetworkStats) -> Self { + Self { + name: src.name, + rx_bytes: src.rx_bytes, + rx_packets: src.rx_packets, + rx_errors: src.rx_errors, + rx_dropped: src.rx_dropped, + tx_bytes: src.tx_bytes, + tx_packets: src.tx_packets, + tx_errors: src.tx_errors, + tx_dropped: src.tx_dropped, + } + } +} + +// translate ttrpc::agent response to interface::agent response +impl From for StatsContainerResponse { + fn from(src: agent::StatsContainerResponse) -> Self { + Self { + cgroup_stats: into_option(src.cgroup_stats), + network_stats: into_vec(src.network_stats), + } + } +} + +impl From for agent::ReadStreamRequest { + fn from(from: ReadStreamRequest) -> Self { + Self { + container_id: from.container_id, + exec_id: from.exec_id, + len: from.len, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for ReadStreamResponse { + fn from(from: agent::ReadStreamResponse) -> Self { + Self { data: from.data } + } +} + +impl From for agent::CloseStdinRequest { + fn from(from: CloseStdinRequest) -> Self { + Self { + container_id: from.container_id, + exec_id: from.exec_id, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::TtyWinResizeRequest { + fn from(from: TtyWinResizeRequest) -> Self { + Self { + container_id: from.container_id, + exec_id: from.exec_id, + row: from.row, + column: from.column, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::UpdateInterfaceRequest { + fn from(from: UpdateInterfaceRequest) -> Self { + Self { + interface: from_option(from.interface), + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::ListInterfacesRequest { + fn from(_: Empty) -> Self { + Self { + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::UpdateRoutesRequest { + fn from(from: UpdateRoutesRequest) -> Self { + Self { + routes: from_option(from.route), + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::ListRoutesRequest { + fn from(_: Empty) -> Self { + Self { + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for types::ARPNeighbor { + fn from(from: ARPNeighbor) -> Self { + Self { + toIPAddress: from_option(from.to_ip_address), + device: from.device, + lladdr: from.ll_addr, + state: from.state, + flags: from.flags, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::ARPNeighbors { + fn from(from: ARPNeighbors) -> Self { + Self { + ARPNeighbors: from_vec(from.neighbors), + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::AddARPNeighborsRequest { + fn from(from: AddArpNeighborRequest) -> Self { + Self { + neighbors: from_option(from.neighbors), + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::CreateSandboxRequest { + fn from(from: CreateSandboxRequest) -> Self { + Self { + hostname: from.hostname, + dns: from_vec(from.dns), + storages: from_vec(from.storages), + sandbox_pidns: from.sandbox_pidns, + sandbox_id: from.sandbox_id, + guest_hook_path: from.guest_hook_path, + kernel_modules: from_vec(from.kernel_modules), + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::DestroySandboxRequest { + fn from(_: Empty) -> Self { + Self { + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::OnlineCPUMemRequest { + fn from(from: OnlineCPUMemRequest) -> Self { + Self { + wait: from.wait, + nb_cpus: from.nb_cpus, + cpu_only: from.cpu_only, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::ReseedRandomDevRequest { + fn from(from: ReseedRandomDevRequest) -> Self { + Self { + data: from.data, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::MemHotplugByProbeRequest { + fn from(from: MemHotplugByProbeRequest) -> Self { + Self { + memHotplugProbeAddr: from.mem_hotplug_probe_addr, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for agent::SetGuestDateTimeRequest { + fn from(from: SetGuestDateTimeRequest) -> Self { + Self { + Sec: from.sec, + Usec: from.usec, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for AgentDetails { + fn from(src: agent::AgentDetails) -> Self { + Self { + version: src.version, + init_daemon: src.init_daemon, + device_handlers: into_vec(src.device_handlers), + storage_handlers: into_vec(src.storage_handlers), + supports_seccomp: src.supports_seccomp, + } + } +} + +impl From for GuestDetailsResponse { + fn from(src: agent::GuestDetailsResponse) -> Self { + Self { + mem_block_size_bytes: src.mem_block_size_bytes, + agent_details: into_option(src.agent_details), + support_mem_hotplug_probe: src.support_mem_hotplug_probe, + } + } +} + +impl From for agent::CopyFileRequest { + fn from(from: CopyFileRequest) -> Self { + Self { + path: from.path, + file_size: from.file_size, + file_mode: from.file_mode, + dir_mode: from.dir_mode, + uid: from.uid, + gid: from.gid, + offset: from.offset, + data: from.data, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for WaitProcessResponse { + fn from(from: agent::WaitProcessResponse) -> Self { + Self { + status: from.status, + } + } +} + +impl From for agent::GetOOMEventRequest { + fn from(_: Empty) -> Self { + Self { + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for health::CheckRequest { + fn from(from: CheckRequest) -> Self { + Self { + service: from.service, + unknown_fields: Default::default(), + cached_size: Default::default(), + } + } +} + +impl From for HealthCheckResponse { + fn from(from: health::HealthCheckResponse) -> Self { + Self { + status: from.status as u32, + } + } +} + +impl From for VersionCheckResponse { + fn from(from: health::VersionCheckResponse) -> Self { + Self { + grpc_version: from.grpc_version, + agent_version: from.agent_version, + } + } +} + +impl From for OomEventResponse { + fn from(from: OOMEvent) -> Self { + Self { + container_id: from.container_id, + } + } +} diff --git a/src/runtime-rs/crates/agent/src/lib.rs b/src/runtime-rs/crates/agent/src/lib.rs new file mode 100644 index 0000000000..a9d8a15f92 --- /dev/null +++ b/src/runtime-rs/crates/agent/src/lib.rs @@ -0,0 +1,84 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +#[macro_use] +extern crate slog; + +macro_rules! sl { + () => { + slog_scope::logger().new(slog::o!("subsystem" => "agent")) + }; +} + +pub mod kata; +mod log_forwarder; +mod sock; +mod types; +pub use types::{ + ARPNeighbor, ARPNeighbors, AddArpNeighborRequest, BlkioStatsEntry, CheckRequest, + CloseStdinRequest, ContainerID, CopyFileRequest, CreateContainerRequest, CreateSandboxRequest, + Empty, ExecProcessRequest, GetGuestDetailsRequest, GuestDetailsResponse, HealthCheckResponse, + IPAddress, IPFamily, Interface, Interfaces, ListProcessesRequest, MemHotplugByProbeRequest, + OnlineCPUMemRequest, OomEventResponse, ReadStreamRequest, ReadStreamResponse, + RemoveContainerRequest, ReseedRandomDevRequest, Route, Routes, SetGuestDateTimeRequest, + SignalProcessRequest, StatsContainerResponse, Storage, TtyWinResizeRequest, + UpdateContainerRequest, UpdateInterfaceRequest, UpdateRoutesRequest, VersionCheckResponse, + WaitProcessRequest, WaitProcessResponse, WriteStreamRequest, WriteStreamResponse, +}; + +use anyhow::Result; +use async_trait::async_trait; + +#[async_trait] +pub trait AgentManager: Send + Sync { + async fn set_socket_address(&self, address: &str) -> Result<()>; + async fn start(&self) -> Result<()>; + async fn stop(&self); +} + +#[async_trait] +pub trait HealthService: Send + Sync { + async fn check(&self, req: CheckRequest) -> Result; + async fn version(&self, req: CheckRequest) -> Result; +} + +#[async_trait] +pub trait Agent: AgentManager + HealthService + Send + Sync { + // sandbox + async fn create_sandbox(&self, req: CreateSandboxRequest) -> Result; + async fn destroy_sandbox(&self, req: Empty) -> Result; + + // network + async fn add_arp_neighbors(&self, req: AddArpNeighborRequest) -> Result; + async fn list_interfaces(&self, req: Empty) -> Result; + async fn list_routes(&self, req: Empty) -> Result; + async fn update_interface(&self, req: UpdateInterfaceRequest) -> Result; + async fn update_routes(&self, req: UpdateRoutesRequest) -> Result; + // container + async fn create_container(&self, req: CreateContainerRequest) -> Result; + async fn pause_container(&self, req: ContainerID) -> Result; + async fn remove_container(&self, req: RemoveContainerRequest) -> Result; + async fn resume_container(&self, req: ContainerID) -> Result; + async fn start_container(&self, req: ContainerID) -> Result; + async fn stats_container(&self, req: ContainerID) -> Result; + async fn update_container(&self, req: UpdateContainerRequest) -> Result; + + // process + async fn exec_process(&self, req: ExecProcessRequest) -> Result; + async fn signal_process(&self, req: SignalProcessRequest) -> Result; + async fn wait_process(&self, req: WaitProcessRequest) -> Result; + + // io and tty + async fn close_stdin(&self, req: CloseStdinRequest) -> Result; + async fn read_stderr(&self, req: ReadStreamRequest) -> Result; + async fn read_stdout(&self, req: ReadStreamRequest) -> Result; + async fn tty_win_resize(&self, req: TtyWinResizeRequest) -> Result; + async fn write_stdin(&self, req: WriteStreamRequest) -> Result; + + // utils + async fn copy_file(&self, req: CopyFileRequest) -> Result; + async fn get_oom_event(&self, req: Empty) -> Result; +} diff --git a/src/runtime-rs/crates/agent/src/log_forwarder.rs b/src/runtime-rs/crates/agent/src/log_forwarder.rs new file mode 100644 index 0000000000..73c668f2be --- /dev/null +++ b/src/runtime-rs/crates/agent/src/log_forwarder.rs @@ -0,0 +1,159 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use anyhow::Result; +use tokio::io::{AsyncBufReadExt, BufReader}; + +use crate::sock; + +// https://github.com/slog-rs/slog/blob/master/src/lib.rs#L2082 +const LOG_LEVEL_TRACE: &str = "TRCE"; +const LOG_LEVEL_DEBUG: &str = "DEBG"; +const LOG_LEVEL_INFO: &str = "INFO"; +const LOG_LEVEL_WARNING: &str = "WARN"; +const LOG_LEVEL_ERROR: &str = "ERRO"; +const LOG_LEVEL_CRITICAL: &str = "CRIT"; + +pub(crate) struct LogForwarder { + task_handler: Option>, +} + +impl LogForwarder { + pub(crate) fn new() -> Self { + Self { task_handler: None } + } + + pub(crate) fn stop(&mut self) { + let task_handler = self.task_handler.take(); + if let Some(handler) = task_handler { + handler.abort(); + info!(sl!(), "abort log forwarder thread"); + } + } + + // start connect kata-agent log vsock and copy data to hypervisor's log stream + pub(crate) async fn start( + &mut self, + address: &str, + port: u32, + config: sock::ConnectConfig, + ) -> Result<()> { + let logger = sl!().clone(); + let address = address.to_string(); + let task_handler = tokio::spawn(async move { + loop { + info!(logger, "try to connect to get agent log"); + let sock = match sock::new(&address, port) { + Ok(sock) => sock, + Err(err) => { + error!( + sl!(), + "failed to new sock for address {:?} port {} error {:?}", + address, + port, + err + ); + return; + } + }; + + match sock.connect(&config).await { + Ok(stream) => { + let stream = BufReader::new(stream); + let mut lines = stream.lines(); + while let Ok(line) = lines.next_line().await { + if let Some(l) = line { + match parse_agent_log_level(&l) { + LOG_LEVEL_TRACE => trace!(sl!(), "{}", l), + LOG_LEVEL_DEBUG => debug!(sl!(), "{}", l), + LOG_LEVEL_WARNING => warn!(sl!(), "{}", l), + LOG_LEVEL_ERROR => error!(sl!(), "{}", l), + LOG_LEVEL_CRITICAL => crit!(sl!(), "{}", l), + _ => info!(sl!(), "{}", l), + } + } + } + } + Err(err) => { + warn!(logger, "connect agent vsock failed: {:?}", err); + } + } + } + }); + self.task_handler = Some(task_handler); + Ok(()) + } +} + +pub fn parse_agent_log_level(s: &str) -> &str { + let v: serde_json::Result = serde_json::from_str(s); + match v { + Err(_err) => LOG_LEVEL_INFO, + Ok(val) => { + match &val["level"] { + serde_json::Value::String(s) => match s.as_str() { + LOG_LEVEL_TRACE => LOG_LEVEL_TRACE, + LOG_LEVEL_DEBUG => LOG_LEVEL_DEBUG, + LOG_LEVEL_WARNING => LOG_LEVEL_WARNING, + LOG_LEVEL_ERROR => LOG_LEVEL_ERROR, + LOG_LEVEL_CRITICAL => LOG_LEVEL_CRITICAL, + _ => LOG_LEVEL_INFO, // info or other values will return info, + }, + _ => LOG_LEVEL_INFO, // info or other values will return info, + } + } + } +} + +#[cfg(test)] +mod tests { + use super::parse_agent_log_level; + + #[test] + fn test_parse_agent_log_level() { + let cases = vec![ + // normal cases + ( + r#"{"msg":"child exited unexpectedly","level":"TRCE"}"#, + super::LOG_LEVEL_TRACE, + ), + ( + r#"{"msg":"child exited unexpectedly","level":"DEBG"}"#, + super::LOG_LEVEL_DEBUG, + ), + ( + r#"{"msg":"child exited unexpectedly","level":"INFO"}"#, + super::LOG_LEVEL_INFO, + ), + ( + r#"{"msg":"child exited unexpectedly","level":"WARN"}"#, + super::LOG_LEVEL_WARNING, + ), + ( + r#"{"msg":"child exited unexpectedly","level":"ERRO"}"#, + super::LOG_LEVEL_ERROR, + ), + ( + r#"{"msg":"child exited unexpectedly","level":"CRIT"}"#, + super::LOG_LEVEL_CRITICAL, + ), + ( + r#"{"msg":"child exited unexpectedly","level":"abc"}"#, + super::LOG_LEVEL_INFO, + ), + // exception cases + (r#"{"not a valid json struct"}"#, super::LOG_LEVEL_INFO), + ("not a valid json struct", super::LOG_LEVEL_INFO), + ]; + + for case in cases.iter() { + let s = case.0; + let result = parse_agent_log_level(s); + let excepted = case.1; + assert_eq!(result, excepted); + } + } +} diff --git a/src/runtime-rs/crates/agent/src/sock/hybrid_vsock.rs b/src/runtime-rs/crates/agent/src/sock/hybrid_vsock.rs new file mode 100644 index 0000000000..7079ea392e --- /dev/null +++ b/src/runtime-rs/crates/agent/src/sock/hybrid_vsock.rs @@ -0,0 +1,81 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use std::os::unix::prelude::AsRawFd; + +use anyhow::{anyhow, Context, Result}; +use async_trait::async_trait; +use tokio::{ + io::{AsyncBufReadExt, AsyncWriteExt, BufReader}, + net::UnixStream, +}; + +use super::{ConnectConfig, Sock, Stream}; + +unsafe impl Send for HybridVsock {} +unsafe impl Sync for HybridVsock {} + +#[derive(Debug, PartialEq)] +pub struct HybridVsock { + uds: String, + port: u32, +} + +impl HybridVsock { + pub fn new(uds: &str, port: u32) -> Self { + Self { + uds: uds.to_string(), + port, + } + } +} + +#[async_trait] +impl Sock for HybridVsock { + async fn connect(&self, config: &ConnectConfig) -> Result { + let retry_times = config.reconnect_timeout_ms / config.dial_timeout_ms; + for i in 0..retry_times { + match connect_helper(&self.uds, self.port).await { + Ok(stream) => { + info!( + sl!(), + "connect success on {} current client fd {}", + i, + stream.as_raw_fd() + ); + return Ok(Stream::Unix(stream)); + } + Err(err) => { + debug!(sl!(), "connect on {} err : {:?}", i, err); + tokio::time::sleep(std::time::Duration::from_millis(config.dial_timeout_ms)) + .await; + continue; + } + } + } + Err(anyhow!("cannot connect to agent ttrpc server")) + } +} + +async fn connect_helper(uds: &str, port: u32) -> Result { + info!(sl!(), "connect uds {:?} port {}", &uds, port); + let mut stream = UnixStream::connect(&uds).await.context("connect")?; + stream + .write_all(format!("connect {}\n", port).as_bytes()) + .await + .context("write all")?; + let mut reads = BufReader::new(&mut stream); + let mut response = String::new(); + reads.read_line(&mut response).await.context("read line")?; + //info!(sl!(), "get socket resp: {}", response); + if !response.contains("OK") { + return Err(anyhow!( + "handshake error: malformed response code: {:?}", + response + )); + } + Ok(stream) +} diff --git a/src/runtime-rs/crates/agent/src/sock/mod.rs b/src/runtime-rs/crates/agent/src/sock/mod.rs new file mode 100644 index 0000000000..52ea993b50 --- /dev/null +++ b/src/runtime-rs/crates/agent/src/sock/mod.rs @@ -0,0 +1,159 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +mod hybrid_vsock; +pub use hybrid_vsock::HybridVsock; +mod vsock; +pub use vsock::Vsock; + +use std::{ + pin::Pin, + task::{Context as TaskContext, Poll}, + { + os::unix::{io::IntoRawFd, prelude::RawFd}, + sync::Arc, + }, +}; + +use anyhow::{anyhow, Context, Result}; +use async_trait::async_trait; +use tokio::{ + io::{AsyncRead, ReadBuf}, + net::UnixStream, +}; +use url::Url; + +const VSOCK_SCHEME: &str = "vsock"; +const HYBRID_VSOCK_SCHEME: &str = "hvsock"; + +/// Socket stream +pub enum Stream { + // hvsock://:. Firecracker/Dragonball implements the virtio-vsock device + // model, and mediates communication between AF_UNIX sockets (on the host end) + // and AF_VSOCK sockets (on the guest end). + Unix(UnixStream), + // TODO: support vsock + // vsock://: +} + +impl Stream { + fn poll_read_priv( + &mut self, + cx: &mut TaskContext<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + // Safety: `UnixStream::read` correctly handles reads into uninitialized memory + match self { + Stream::Unix(stream) => Pin::new(stream).poll_read(cx, buf), + } + } +} + +impl IntoRawFd for Stream { + fn into_raw_fd(self) -> RawFd { + match self { + Stream::Unix(stream) => match stream.into_std() { + Ok(stream) => stream.into_raw_fd(), + Err(err) => { + error!(sl!(), "failed to into std unix stream {:?}", err); + -1 + } + }, + } + } +} + +impl AsyncRead for Stream { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut TaskContext<'_>, + buf: &mut ReadBuf<'_>, + ) -> Poll> { + // we know this is safe because doesn't moved + let me = unsafe { self.get_unchecked_mut() }; + me.poll_read_priv(cx, buf) + } +} + +/// Connect config +pub struct ConnectConfig { + dial_timeout_ms: u64, + reconnect_timeout_ms: u64, +} + +impl ConnectConfig { + pub fn new(dial_timeout_ms: u64, reconnect_timeout_ms: u64) -> Self { + Self { + dial_timeout_ms, + reconnect_timeout_ms, + } + } +} + +#[derive(Debug, PartialEq)] +enum SockType { + Vsock(Vsock), + HybridVsock(HybridVsock), +} + +#[async_trait] +pub trait Sock: Send + Sync { + async fn connect(&self, config: &ConnectConfig) -> Result; +} + +// Supported sock address formats are: +// - vsock://: +// - hvsock://:. Firecracker implements the virtio-vsock device +// model, and mediates communication between AF_UNIX sockets (on the host end) +// and AF_VSOCK sockets (on the guest end). +pub fn new(address: &str, port: u32) -> Result> { + match parse(address, port).context("parse url")? { + SockType::Vsock(sock) => Ok(Arc::new(sock)), + SockType::HybridVsock(sock) => Ok(Arc::new(sock)), + } +} + +fn parse(address: &str, port: u32) -> Result { + let url = Url::parse(address).context("parse url")?; + match url.scheme() { + VSOCK_SCHEME => { + let cid = url + .host_str() + .unwrap_or_default() + .parse::() + .context("parse cid")?; + Ok(SockType::Vsock(Vsock::new(cid, port))) + } + HYBRID_VSOCK_SCHEME => { + let path: Vec<&str> = url.path().split(':').collect(); + if path.len() != 1 { + return Err(anyhow!("invalid path {:?}", path)); + } + let uds = path[0]; + Ok(SockType::HybridVsock(HybridVsock::new(uds, port))) + } + _ => Err(anyhow!("Unsupported scheme")), + } +} + +#[cfg(test)] +mod test { + use super::{hybrid_vsock::HybridVsock, parse, vsock::Vsock, SockType}; + + #[test] + fn test_parse_url() { + // check vsock + let vsock = parse("vsock://123", 456).unwrap(); + assert_eq!(vsock, SockType::Vsock(Vsock::new(123, 456))); + + // check hybrid vsock + let hvsock = parse("hvsock:///tmp/test.hvsock", 456).unwrap(); + assert_eq!( + hvsock, + SockType::HybridVsock(HybridVsock::new("/tmp/test.hvsock", 456)) + ); + } +} diff --git a/src/runtime-rs/crates/agent/src/sock/vsock.rs b/src/runtime-rs/crates/agent/src/sock/vsock.rs new file mode 100644 index 0000000000..7f6a59d89a --- /dev/null +++ b/src/runtime-rs/crates/agent/src/sock/vsock.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use anyhow::Result; +use async_trait::async_trait; + +use super::{ConnectConfig, Sock, Stream}; + +unsafe impl Send for Vsock {} +unsafe impl Sync for Vsock {} + +#[derive(Debug, PartialEq)] +pub struct Vsock { + cid: u32, + port: u32, +} + +impl Vsock { + pub fn new(cid: u32, port: u32) -> Self { + Self { cid, port } + } +} + +#[async_trait] +impl Sock for Vsock { + async fn connect(&self, _config: &ConnectConfig) -> Result { + todo!() + } +} diff --git a/src/runtime-rs/crates/agent/src/types.rs b/src/runtime-rs/crates/agent/src/types.rs new file mode 100644 index 0000000000..41e0e3ee36 --- /dev/null +++ b/src/runtime-rs/crates/agent/src/types.rs @@ -0,0 +1,454 @@ +// Copyright (c) 2019-2022 Alibaba Cloud +// Copyright (c) 2019-2022 Ant Group +// +// SPDX-License-Identifier: Apache-2.0 +// + +use serde::Deserialize; + +#[derive(PartialEq, Clone, Default)] +pub struct Empty {} + +impl Empty { + pub fn new() -> Self { + Self::default() + } +} + +#[derive(PartialEq, Clone, Default)] +pub struct StringUser { + pub uid: String, + pub gid: String, + pub additional_gids: Vec, +} + +#[derive(PartialEq, Clone, Default)] +pub struct Device { + pub id: String, + pub field_type: String, + pub vm_path: String, + pub container_path: String, + pub options: Vec, +} + +#[derive(Debug, PartialEq, Clone, Default)] +pub struct Storage { + pub driver: String, + pub driver_options: Vec, + pub source: String, + pub fs_type: String, + pub options: Vec, + pub mount_point: String, +} + +#[derive(Deserialize, Clone, PartialEq, Eq, Debug, Hash)] +pub enum IPFamily { + V4 = 0, + V6 = 1, +} + +impl ::std::default::Default for IPFamily { + fn default() -> Self { + IPFamily::V4 + } +} + +#[derive(Deserialize, Debug, PartialEq, Clone, Default)] +pub struct IPAddress { + pub family: IPFamily, + pub address: String, + pub mask: String, +} + +#[derive(Deserialize, Debug, PartialEq, Clone, Default)] +pub struct Interface { + pub device: String, + pub name: String, + pub ip_addresses: Vec, + pub mtu: u64, + pub hw_addr: String, + #[serde(default)] + pub pci_addr: String, + #[serde(default)] + pub field_type: String, + #[serde(default)] + pub raw_flags: u32, +} + +#[derive(PartialEq, Clone, Default)] +pub struct Interfaces { + pub interfaces: Vec, +} + +#[derive(Deserialize, Debug, PartialEq, Clone, Default)] +pub struct Route { + pub dest: String, + pub gateway: String, + pub device: String, + pub source: String, + pub scope: u32, + pub family: IPFamily, +} + +#[derive(Deserialize, Debug, PartialEq, Clone, Default)] +pub struct Routes { + pub routes: Vec, +} + +#[derive(PartialEq, Clone, Default)] +pub struct CreateContainerRequest { + pub container_id: String, + pub exec_id: String, + pub string_user: Option, + pub devices: Vec, + pub storages: Vec, + pub oci: Option, + pub guest_hooks: Option, + pub sandbox_pidns: bool, + pub rootfs_mounts: Vec, +} + +#[derive(PartialEq, Clone, Default)] +pub struct ContainerID { + pub container_id: String, +} + +impl ContainerID { + pub fn new(id: &str) -> Self { + Self { + container_id: id.to_string(), + } + } +} + +#[derive(PartialEq, Clone, Debug, Default)] +pub struct RemoveContainerRequest { + pub container_id: String, + pub timeout: u32, +} + +impl RemoveContainerRequest { + pub fn new(id: &str, timeout: u32) -> Self { + Self { + container_id: id.to_string(), + timeout, + } + } +} + +#[derive(PartialEq, Clone, Default)] +pub struct SignalProcessRequest { + pub container_id: String, + pub exec_id: String, + pub signal: u32, +} + +#[derive(PartialEq, Clone, Default)] +pub struct WaitProcessRequest { + pub container_id: String, + pub exec_id: String, +} + +#[derive(PartialEq, Clone, Default)] +pub struct ListProcessesRequest { + pub container_id: String, + pub format: String, + pub args: Vec, +} + +#[derive(PartialEq, Clone, Default)] +pub struct UpdateContainerRequest { + pub container_id: String, + pub resources: oci::LinuxResources, + pub mounts: Vec, +} + +#[derive(PartialEq, Clone, Default)] +pub struct WriteStreamRequest { + pub container_id: String, + pub exec_id: String, + pub data: Vec, +} + +#[derive(PartialEq, Clone, Default)] +pub struct WriteStreamResponse { + pub length: u32, +} + +#[derive(PartialEq, Clone, Default)] +pub struct ExecProcessRequest { + pub container_id: String, + pub exec_id: String, + pub string_user: Option, + pub process: Option, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct CpuUsage { + pub total_usage: u64, + pub percpu_usage: ::std::vec::Vec, + pub usage_in_kernelmode: u64, + pub usage_in_usermode: u64, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct ThrottlingData { + pub periods: u64, + pub throttled_periods: u64, + pub throttled_time: u64, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct LoadData { + pub one: String, + pub five: String, + pub fifteen: String, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct CpuStats { + pub cpu_usage: Option, + pub throttling_data: Option, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct MemoryData { + pub usage: u64, + pub max_usage: u64, + pub failcnt: u64, + pub limit: u64, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct MemoryStats { + pub cache: u64, + pub usage: Option, + pub swap_usage: Option, + pub kernel_usage: Option, + pub use_hierarchy: bool, + pub stats: ::std::collections::HashMap, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct PidsStats { + pub current: u64, + pub limit: u64, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct BlkioStatsEntry { + pub major: u64, + pub minor: u64, + pub op: String, + pub value: u64, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct BlkioStats { + pub io_service_bytes_recursive: Vec, + pub io_serviced_recursive: Vec, + pub io_queued_recursive: Vec, + pub io_service_time_recursive: Vec, + pub io_wait_time_recursive: Vec, + pub io_merged_recursive: Vec, + pub io_time_recursive: Vec, + pub sectors_recursive: Vec, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct HugetlbStats { + pub usage: u64, + pub max_usage: u64, + pub failcnt: u64, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct CgroupStats { + pub cpu_stats: Option, + pub memory_stats: Option, + pub pids_stats: Option, + pub blkio_stats: Option, + pub hugetlb_stats: ::std::collections::HashMap, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct NetworkStats { + pub name: String, + pub rx_bytes: u64, + pub rx_packets: u64, + pub rx_errors: u64, + pub rx_dropped: u64, + pub tx_bytes: u64, + pub tx_packets: u64, + pub tx_errors: u64, + pub tx_dropped: u64, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct StatsContainerResponse { + pub cgroup_stats: Option, + pub network_stats: Vec, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct WaitProcessResponse { + pub status: i32, +} + +#[derive(PartialEq, Clone, Default)] +pub struct ReadStreamRequest { + pub container_id: String, + pub exec_id: String, + pub len: u32, +} + +#[derive(PartialEq, Clone, Default)] +pub struct ReadStreamResponse { + pub data: Vec, +} + +#[derive(PartialEq, Clone, Default)] +pub struct CloseStdinRequest { + pub container_id: String, + pub exec_id: String, +} + +#[derive(PartialEq, Clone, Default)] +pub struct TtyWinResizeRequest { + pub container_id: String, + pub exec_id: String, + pub row: u32, + pub column: u32, +} + +#[derive(Debug, PartialEq, Clone, Default)] +pub struct UpdateInterfaceRequest { + pub interface: Option, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct UpdateRoutesRequest { + pub route: Option, +} + +#[derive(Deserialize, PartialEq, Clone, Default, Debug)] +pub struct ARPNeighbor { + pub to_ip_address: Option, + pub device: String, + pub ll_addr: String, + pub state: i32, + pub flags: i32, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct ARPNeighbors { + pub neighbors: Vec, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct AddArpNeighborRequest { + pub neighbors: Option, +} + +#[derive(PartialEq, Clone, Default)] +pub struct KernelModule { + pub name: String, + pub parameters: Vec, +} + +#[derive(PartialEq, Clone, Default)] +pub struct CreateSandboxRequest { + pub hostname: String, + pub dns: Vec, + pub storages: Vec, + pub sandbox_pidns: bool, + pub sandbox_id: String, + pub guest_hook_path: String, + pub kernel_modules: Vec, +} + +#[derive(PartialEq, Clone, Default)] +pub struct OnlineCPUMemRequest { + pub wait: bool, + pub nb_cpus: u32, + pub cpu_only: bool, +} + +#[derive(PartialEq, Clone, Default)] +pub struct ReseedRandomDevRequest { + pub data: ::std::vec::Vec, +} + +#[derive(PartialEq, Clone, Default)] +pub struct GetGuestDetailsRequest { + pub mem_block_size: bool, + pub mem_hotplug_probe: bool, +} + +#[derive(PartialEq, Clone, Default)] +pub struct MemHotplugByProbeRequest { + pub mem_hotplug_probe_addr: ::std::vec::Vec, +} + +#[derive(PartialEq, Clone, Default)] +pub struct SetGuestDateTimeRequest { + pub sec: i64, + pub usec: i64, +} + +#[derive(PartialEq, Clone, Default)] +pub struct AgentDetails { + pub version: String, + pub init_daemon: bool, + pub device_handlers: Vec, + pub storage_handlers: Vec, + pub supports_seccomp: bool, +} + +#[derive(PartialEq, Clone, Default)] +pub struct GuestDetailsResponse { + pub mem_block_size_bytes: u64, + pub agent_details: Option, + pub support_mem_hotplug_probe: bool, +} + +#[derive(PartialEq, Clone, Default)] +pub struct CopyFileRequest { + pub path: String, + pub file_size: i64, + pub file_mode: u32, + pub dir_mode: u32, + pub uid: i32, + pub gid: i32, + pub offset: i64, + pub data: ::std::vec::Vec, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct CheckRequest { + pub service: String, +} + +impl CheckRequest { + pub fn new(service: &str) -> Self { + Self { + service: service.to_string(), + } + } +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct HealthCheckResponse { + pub status: u32, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct VersionCheckResponse { + pub grpc_version: String, + pub agent_version: String, +} + +#[derive(PartialEq, Clone, Default, Debug)] +pub struct OomEventResponse { + pub container_id: String, +}