Merge pull request #4892 from openanolis/shuoyu/runtime-rs

runtime-rs: support loading kernel modules in guest vm
This commit is contained in:
Bin Liu
2022-08-25 15:01:23 +08:00
committed by GitHub
12 changed files with 186 additions and 32 deletions

View File

@@ -8,6 +8,8 @@ use anyhow::{Context, Result};
use async_trait::async_trait;
use ttrpc::context as ttrpc_ctx;
use kata_types::config::Agent as AgentConfig;
use crate::{kata::KataAgent, Agent, AgentManager, HealthService};
/// millisecond to nanosecond
@@ -37,6 +39,10 @@ impl AgentManager for KataAgent {
async fn stop(&self) {
self.stop_log_forwarder().await;
}
async fn agent_config(&self) -> AgentConfig {
self.agent_config().await
}
}
// implement for health service

View File

@@ -126,4 +126,9 @@ impl KataAgent {
let mut inner = self.inner.lock().await;
inner.log_forwarder.stop();
}
pub(crate) async fn agent_config(&self) -> AgentConfig {
let inner = self.inner.lock().await;
inner.config.clone()
}
}

View File

@@ -29,10 +29,16 @@ pub use types::{
use anyhow::Result;
use async_trait::async_trait;
use kata_types::config::Agent as AgentConfig;
pub const AGENT_KATA: &str = "kata";
#[async_trait]
pub trait AgentManager: Send + Sync {
async fn start(&self, address: &str) -> Result<()>;
async fn stop(&self);
async fn agent_config(&self) -> AgentConfig;
}
#[async_trait]

View File

@@ -4,6 +4,9 @@
// SPDX-License-Identifier: Apache-2.0
//
use anyhow::{anyhow, Result};
use std::convert::TryFrom;
use serde::Deserialize;
#[derive(PartialEq, Clone, Default)]
@@ -390,6 +393,56 @@ pub struct KernelModule {
pub parameters: Vec<String>,
}
impl KernelModule {
pub fn set_kernel_modules(modules: Vec<String>) -> Result<Vec<Self>> {
let mut kernel_modules = Vec::new();
for module_string in modules {
if module_string.is_empty() {
continue;
}
let kernel_module = Self::try_from(module_string)?;
kernel_modules.push(kernel_module);
}
Ok(kernel_modules)
}
}
impl TryFrom<String> for KernelModule {
type Error = anyhow::Error;
// input string: " ModuleName Param1 Param2 ... "
// NOTICE: " ModuleName Param1="spaces in here" " => KernelModule { name: ModuleName, parameters: Param1="spaces in here" }
fn try_from(str: String) -> Result<Self> {
let split: Vec<&str> = str.split(' ').collect();
let mut name = String::new();
let mut parameters = Vec::new();
let mut flag = false;
for (index, info) in split.iter().enumerate() {
if index == 0 {
name = info.to_string();
} else if flag {
// a former param's string contains \"
if let Some(former_param) = parameters.pop() {
let cur_param = format!("{} {}", former_param, info);
parameters.push(cur_param);
}
} else {
parameters.push(info.to_string());
}
if info.contains('\"') {
flag = !flag;
}
}
if flag {
return Err(anyhow!("\" not match"));
}
Ok(KernelModule { name, parameters })
}
}
#[derive(PartialEq, Clone, Default)]
pub struct CreateSandboxRequest {
pub hostname: String,
@@ -486,3 +539,44 @@ pub struct VersionCheckResponse {
pub struct OomEventResponse {
pub container_id: String,
}
#[cfg(test)]
mod test {
use std::convert::TryFrom;
use super::KernelModule;
#[test]
fn test_new_kernel_module() {
let kernel_module_str1 = "ModuleName Param1 Param2";
let kernel_module1 = KernelModule::try_from(kernel_module_str1.to_string()).unwrap();
assert!(kernel_module1.name == "ModuleName");
assert!(kernel_module1.parameters[0] == "Param1");
assert!(kernel_module1.parameters[1] == "Param2");
let kernel_module_str2 = "ModuleName Param1=\"spaces in here\"";
let kernel_module2 = KernelModule::try_from(kernel_module_str2.to_string()).unwrap();
assert!(kernel_module2.name == "ModuleName");
assert!(kernel_module2.parameters[0] == "Param1=\"spaces in here\"");
// exception case
let kernel_module_str3 = "ModuleName \"Param1";
let kernel_module3 = KernelModule::try_from(kernel_module_str3.to_string());
assert!(kernel_module3.is_err());
}
#[test]
fn test_kernel_modules() {
let kernel_module_str1 = "ModuleName1 Param1 Param2".to_string();
let kernel_module_str2 = "".to_string();
let kernel_module_str3 = "ModuleName2".to_string();
let kernel_modules_str = vec![kernel_module_str1, kernel_module_str2, kernel_module_str3];
let kernel_modules = KernelModule::set_kernel_modules(kernel_modules_str).unwrap();
assert!(kernel_modules.len() == 2);
assert!(kernel_modules[0].name == "ModuleName1");
assert!(kernel_modules[0].parameters.len() == 2);
assert!(kernel_modules[1].name == "ModuleName2");
assert!(kernel_modules[1].parameters.is_empty());
}
}

View File

@@ -16,7 +16,7 @@ pub mod sandbox_persist;
use std::sync::Arc;
use agent::kata::KataAgent;
use agent::{kata::KataAgent, AGENT_KATA};
use anyhow::{anyhow, Context, Result};
use async_trait::async_trait;
use common::{message::Message, RuntimeHandler, RuntimeInstance};
@@ -55,20 +55,7 @@ impl RuntimeHandler for VirtContainer {
let hypervisor = new_hypervisor(&config).await.context("new hypervisor")?;
// get uds from hypervisor and get config from toml_config
let agent = Arc::new(KataAgent::new(kata_types::config::Agent {
debug: true,
enable_tracing: false,
server_port: 1024,
log_port: 1025,
dial_timeout_ms: 10,
reconnect_timeout_ms: 3_000,
request_timeout_ms: 30_000,
health_check_request_timeout_ms: 90_000,
kernel_modules: Default::default(),
container_pipe_size: 0,
debug_console_enabled: false,
}));
let agent = new_agent(&config).context("new agent")?;
let resource_manager = Arc::new(ResourceManager::new(
sid,
agent.clone(),
@@ -121,3 +108,44 @@ async fn new_hypervisor(toml_config: &TomlConfig) -> Result<Arc<dyn Hypervisor>>
_ => Err(anyhow!("Unsupported hypervisor {}", &hypervisor_name)),
}
}
fn new_agent(toml_config: &TomlConfig) -> Result<Arc<KataAgent>> {
let agent_name = &toml_config.runtime.agent_name;
let agent_config = toml_config
.agent
.get(agent_name)
.ok_or_else(|| anyhow!("failed to get agent for {}", &agent_name))
.context("get agent")?;
match agent_name.as_str() {
AGENT_KATA => {
let agent = KataAgent::new(agent_config.clone());
Ok(Arc::new(agent))
}
_ => Err(anyhow!("Unsupported agent {}", &agent_name)),
}
}
#[cfg(test)]
mod test {
use super::*;
fn default_toml_config_agent() -> Result<TomlConfig> {
let config_content = r#"
[agent.kata]
container_pipe_size=1
[runtime]
agent_name="kata"
"#;
TomlConfig::load(config_content).map_err(|e| anyhow!("can not load config toml: {}", e))
}
#[test]
fn test_new_agent() {
let toml_config = default_toml_config_agent().unwrap();
let res = new_agent(&toml_config);
assert!(res.is_ok());
}
}

View File

@@ -6,7 +6,7 @@
use std::sync::Arc;
use agent::{self, kata::KataAgent, Agent};
use agent::{self, kata::KataAgent, types::KernelModule, Agent};
use anyhow::{anyhow, Context, Result};
use async_trait::async_trait;
use common::{
@@ -159,6 +159,8 @@ impl Sandbox for VirtSandbox {
.context("setup device after start vm")?;
// create sandbox in vm
let agent_config = self.agent.agent_config().await;
let kernel_modules = KernelModule::set_kernel_modules(agent_config.kernel_modules)?;
let req = agent::CreateSandboxRequest {
hostname: "".to_string(),
dns: vec![],
@@ -175,7 +177,7 @@ impl Sandbox for VirtSandbox {
.await
.security_info
.guest_hook_path,
kernel_modules: vec![],
kernel_modules,
};
self.agent