mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-06-29 00:37:24 +00:00
runtime-rs: support rootfs volume for resource
Fixes: #3785 Signed-off-by: Quanwei Zhou <quanweiZhou@linux.alibaba.com>
This commit is contained in:
parent
234d7bca04
commit
3ff0db05a7
75
src/runtime-rs/Cargo.lock
generated
75
src/runtime-rs/Cargo.lock
generated
@ -293,7 +293,7 @@ checksum = "ec3245a0ca564e7f3c797d20d833a6870f57a728ac967d5225b3ffdef4465011"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"rand",
|
"rand 0.8.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -321,6 +321,12 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-cprng"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.1.31"
|
version = "0.1.31"
|
||||||
@ -1066,6 +1072,29 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.3.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand 0.4.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
|
||||||
|
dependencies = [
|
||||||
|
"fuchsia-cprng",
|
||||||
|
"libc",
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
"rdrand",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.8.5"
|
version = "0.8.5"
|
||||||
@ -1074,7 +1103,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha",
|
"rand_chacha",
|
||||||
"rand_core",
|
"rand_core 0.6.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1084,9 +1113,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"ppv-lite86",
|
||||||
"rand_core",
|
"rand_core 0.6.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.6.3"
|
version = "0.6.3"
|
||||||
@ -1096,6 +1140,15 @@ dependencies = [
|
|||||||
"getrandom",
|
"getrandom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rdrand"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
@ -1135,6 +1188,7 @@ dependencies = [
|
|||||||
name = "resource"
|
name = "resource"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"agent",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"cgroups-rs",
|
"cgroups-rs",
|
||||||
@ -1148,7 +1202,9 @@ dependencies = [
|
|||||||
"nix 0.16.1",
|
"nix 0.16.1",
|
||||||
"oci",
|
"oci",
|
||||||
"slog",
|
"slog",
|
||||||
|
"slog-scope",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1306,7 +1362,7 @@ dependencies = [
|
|||||||
"nix 0.23.1",
|
"nix 0.23.1",
|
||||||
"oci",
|
"oci",
|
||||||
"protobuf",
|
"protobuf",
|
||||||
"rand",
|
"rand 0.8.5",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
"service",
|
"service",
|
||||||
"sha2",
|
"sha2",
|
||||||
@ -1472,7 +1528,7 @@ dependencies = [
|
|||||||
name = "tests_utils"
|
name = "tests_utils"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand",
|
"rand 0.8.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1692,6 +1748,15 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7cfec50b0842181ba6e713151b72f4ec84a6a7e2c9c8a8a3ffc37bb1cd16b231"
|
||||||
|
dependencies = [
|
||||||
|
"rand 0.3.23",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcpkg"
|
name = "vcpkg"
|
||||||
version = "0.2.15"
|
version = "0.2.15"
|
||||||
|
@ -13,8 +13,11 @@ libc = ">=0.2.39"
|
|||||||
log = "^0.4.0"
|
log = "^0.4.0"
|
||||||
nix = "0.16.0"
|
nix = "0.16.0"
|
||||||
slog = "2.5.2"
|
slog = "2.5.2"
|
||||||
tokio = { version = "1.8.0", features = ["sync"] }
|
slog-scope = "4.4.0"
|
||||||
|
tokio = { version = "1.8.0", features = ["process"] }
|
||||||
|
uuid = { version = "0.4", features = ["v4"] }
|
||||||
|
|
||||||
|
agent = { path = "../agent" }
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
kata-types = { path = "../../../libs/kata-types" }
|
kata-types = { path = "../../../libs/kata-types" }
|
||||||
kata-sys-util = { path = "../../../libs/kata-sys-util" }
|
kata-sys-util = { path = "../../../libs/kata-sys-util" }
|
||||||
|
@ -4,4 +4,25 @@
|
|||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate slog;
|
||||||
|
|
||||||
|
logging::logger_with_subsystem!(sl, "resource");
|
||||||
|
|
||||||
pub mod cgroups;
|
pub mod cgroups;
|
||||||
|
pub mod manager;
|
||||||
|
mod manager_inner;
|
||||||
|
pub mod rootfs;
|
||||||
|
pub mod share_fs;
|
||||||
|
pub mod volume;
|
||||||
|
pub use manager::ResourceManager;
|
||||||
|
|
||||||
|
use kata_types::config::hypervisor::SharedFsInfo;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ResourceConfig {
|
||||||
|
ShareFs(SharedFsInfo),
|
||||||
|
}
|
||||||
|
92
src/runtime-rs/crates/resource/src/manager.rs
Normal file
92
src/runtime-rs/crates/resource/src/manager.rs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
// Copyright (c) 2019-2022 Alibaba Cloud
|
||||||
|
// Copyright (c) 2019-2022 Ant Group
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use agent::{Agent, Storage};
|
||||||
|
use anyhow::Result;
|
||||||
|
use hypervisor::Hypervisor;
|
||||||
|
use kata_types::config::TomlConfig;
|
||||||
|
use kata_types::mount::Mount;
|
||||||
|
use oci::LinuxResources;
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
use crate::{manager_inner::ResourceManagerInner, rootfs::Rootfs, volume::Volume, ResourceConfig};
|
||||||
|
|
||||||
|
pub struct ResourceManager {
|
||||||
|
inner: Arc<RwLock<ResourceManagerInner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResourceManager {
|
||||||
|
pub fn new(
|
||||||
|
sid: &str,
|
||||||
|
agent: Arc<dyn Agent>,
|
||||||
|
hypervisor: Arc<dyn Hypervisor>,
|
||||||
|
toml_config: &TomlConfig,
|
||||||
|
) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
inner: Arc::new(RwLock::new(ResourceManagerInner::new(
|
||||||
|
sid,
|
||||||
|
agent,
|
||||||
|
hypervisor,
|
||||||
|
toml_config,
|
||||||
|
)?)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn prepare_before_start_vm(&self, device_configs: Vec<ResourceConfig>) -> Result<()> {
|
||||||
|
let mut inner = self.inner.write().await;
|
||||||
|
inner.prepare_before_start_vm(device_configs).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn setup_after_start_vm(&self) -> Result<()> {
|
||||||
|
let mut inner = self.inner.write().await;
|
||||||
|
inner.setup_after_start_vm().await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_storage_for_sandbox(&self) -> Result<Vec<Storage>> {
|
||||||
|
let inner = self.inner.read().await;
|
||||||
|
inner.get_storage_for_sandbox().await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handler_rootfs(
|
||||||
|
&self,
|
||||||
|
cid: &str,
|
||||||
|
bundle_path: &str,
|
||||||
|
rootfs_mounts: &[Mount],
|
||||||
|
) -> Result<Arc<dyn Rootfs>> {
|
||||||
|
let inner = self.inner.read().await;
|
||||||
|
inner.handler_rootfs(cid, bundle_path, rootfs_mounts).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handler_volumes(
|
||||||
|
&self,
|
||||||
|
cid: &str,
|
||||||
|
oci_mounts: &[oci::Mount],
|
||||||
|
) -> Result<Vec<Arc<dyn Volume>>> {
|
||||||
|
let inner = self.inner.read().await;
|
||||||
|
inner.handler_volumes(cid, oci_mounts).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn dump(&self) {
|
||||||
|
let inner = self.inner.read().await;
|
||||||
|
inner.dump().await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_cgroups(
|
||||||
|
&self,
|
||||||
|
cid: &str,
|
||||||
|
linux_resources: Option<&LinuxResources>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let inner = self.inner.read().await;
|
||||||
|
inner.update_cgroups(cid, linux_resources).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_cgroups(&self) -> Result<()> {
|
||||||
|
let inner = self.inner.read().await;
|
||||||
|
inner.delete_cgroups().await
|
||||||
|
}
|
||||||
|
}
|
134
src/runtime-rs/crates/resource/src/manager_inner.rs
Normal file
134
src/runtime-rs/crates/resource/src/manager_inner.rs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
// Copyright (c) 2019-2022 Alibaba Cloud
|
||||||
|
// Copyright (c) 2019-2022 Ant Group
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use agent::{Agent, Storage};
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use hypervisor::Hypervisor;
|
||||||
|
use kata_types::config::TomlConfig;
|
||||||
|
use kata_types::mount::Mount;
|
||||||
|
use oci::LinuxResources;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
cgroups::CgroupsResource,
|
||||||
|
rootfs::{RootFsResource, Rootfs},
|
||||||
|
share_fs::{self, ShareFs},
|
||||||
|
volume::{Volume, VolumeResource},
|
||||||
|
ResourceConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) struct ResourceManagerInner {
|
||||||
|
sid: String,
|
||||||
|
// TODO: remove
|
||||||
|
#[allow(dead_code)]
|
||||||
|
agent: Arc<dyn Agent>,
|
||||||
|
hypervisor: Arc<dyn Hypervisor>,
|
||||||
|
share_fs: Option<Arc<dyn ShareFs>>,
|
||||||
|
|
||||||
|
pub rootfs_resource: RootFsResource,
|
||||||
|
pub volume_resource: VolumeResource,
|
||||||
|
pub cgroups_resource: CgroupsResource,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ResourceManagerInner {
|
||||||
|
pub(crate) fn new(
|
||||||
|
sid: &str,
|
||||||
|
agent: Arc<dyn Agent>,
|
||||||
|
hypervisor: Arc<dyn Hypervisor>,
|
||||||
|
toml_config: &TomlConfig,
|
||||||
|
) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
sid: sid.to_string(),
|
||||||
|
agent,
|
||||||
|
hypervisor,
|
||||||
|
share_fs: None,
|
||||||
|
rootfs_resource: RootFsResource::new(),
|
||||||
|
volume_resource: VolumeResource::new(),
|
||||||
|
cgroups_resource: CgroupsResource::new(sid, toml_config)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn prepare_before_start_vm(
|
||||||
|
&mut self,
|
||||||
|
device_configs: Vec<ResourceConfig>,
|
||||||
|
) -> Result<()> {
|
||||||
|
for dc in device_configs {
|
||||||
|
match dc {
|
||||||
|
ResourceConfig::ShareFs(c) => {
|
||||||
|
let share_fs = share_fs::new(&self.sid, &c).context("new share fs")?;
|
||||||
|
share_fs
|
||||||
|
.setup_device_before_start_vm(self.hypervisor.as_ref())
|
||||||
|
.await
|
||||||
|
.context("setup share fs device before start vm")?;
|
||||||
|
self.share_fs = Some(share_fs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn setup_after_start_vm(&mut self) -> Result<()> {
|
||||||
|
if let Some(share_fs) = self.share_fs.as_ref() {
|
||||||
|
share_fs
|
||||||
|
.setup_device_after_start_vm(self.hypervisor.as_ref())
|
||||||
|
.await
|
||||||
|
.context("setup share fs device after start vm")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_storage_for_sandbox(&self) -> Result<Vec<Storage>> {
|
||||||
|
let mut storages = vec![];
|
||||||
|
if let Some(d) = self.share_fs.as_ref() {
|
||||||
|
let mut s = d.get_storages().await.context("get storage")?;
|
||||||
|
storages.append(&mut s);
|
||||||
|
}
|
||||||
|
Ok(storages)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handler_rootfs(
|
||||||
|
&self,
|
||||||
|
cid: &str,
|
||||||
|
bundle_path: &str,
|
||||||
|
rootfs_mounts: &[Mount],
|
||||||
|
) -> Result<Arc<dyn Rootfs>> {
|
||||||
|
self.rootfs_resource
|
||||||
|
.handler_rootfs(&self.share_fs, cid, bundle_path, rootfs_mounts)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handler_volumes(
|
||||||
|
&self,
|
||||||
|
cid: &str,
|
||||||
|
oci_mounts: &[oci::Mount],
|
||||||
|
) -> Result<Vec<Arc<dyn Volume>>> {
|
||||||
|
self.volume_resource
|
||||||
|
.handler_volumes(&self.share_fs, cid, oci_mounts)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_cgroups(
|
||||||
|
&self,
|
||||||
|
cid: &str,
|
||||||
|
linux_resources: Option<&LinuxResources>,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.cgroups_resource
|
||||||
|
.update_cgroups(cid, linux_resources, self.hypervisor.as_ref())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_cgroups(&self) -> Result<()> {
|
||||||
|
self.cgroups_resource.delete().await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn dump(&self) {
|
||||||
|
self.rootfs_resource.dump().await;
|
||||||
|
self.volume_resource.dump().await;
|
||||||
|
}
|
||||||
|
}
|
123
src/runtime-rs/crates/resource/src/rootfs/mod.rs
Normal file
123
src/runtime-rs/crates/resource/src/rootfs/mod.rs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
// Copyright (c) 2019-2022 Alibaba Cloud
|
||||||
|
// Copyright (c) 2019-2022 Ant Group
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
mod share_fs_rootfs;
|
||||||
|
|
||||||
|
use std::{sync::Arc, vec::Vec};
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use kata_types::mount::Mount;
|
||||||
|
use log::{error, info};
|
||||||
|
use nix::sys::stat::{self, SFlag};
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
use crate::share_fs::ShareFs;
|
||||||
|
|
||||||
|
const ROOTFS: &str = "rootfs";
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait Rootfs: Send + Sync {
|
||||||
|
async fn get_guest_rootfs_path(&self) -> Result<String>;
|
||||||
|
async fn get_rootfs_mount(&self) -> Result<Vec<oci::Mount>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct RootFsResourceInner {
|
||||||
|
rootfs: Vec<Arc<dyn Rootfs>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RootFsResource {
|
||||||
|
inner: Arc<RwLock<RootFsResourceInner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for RootFsResource {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RootFsResource {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Arc::new(RwLock::new(RootFsResourceInner::default())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handler_rootfs(
|
||||||
|
&self,
|
||||||
|
share_fs: &Option<Arc<dyn ShareFs>>,
|
||||||
|
cid: &str,
|
||||||
|
bundle_path: &str,
|
||||||
|
rootfs_mounts: &[Mount],
|
||||||
|
) -> Result<Arc<dyn Rootfs>> {
|
||||||
|
match rootfs_mounts {
|
||||||
|
mounts_vec if is_single_layer_rootfs(mounts_vec) => {
|
||||||
|
// Safe as single_layer_rootfs must have one layer
|
||||||
|
let layer = &mounts_vec[0];
|
||||||
|
|
||||||
|
let rootfs = if let Some(_dev_id) = get_block_device(&layer.source) {
|
||||||
|
// block rootfs
|
||||||
|
unimplemented!()
|
||||||
|
} else if let Some(share_fs) = share_fs {
|
||||||
|
// share fs rootfs
|
||||||
|
let share_fs_mount = share_fs.get_share_fs_mount();
|
||||||
|
share_fs_rootfs::ShareFsRootfs::new(&share_fs_mount, cid, bundle_path, layer)
|
||||||
|
.await
|
||||||
|
.context("new share fs rootfs")?
|
||||||
|
} else {
|
||||||
|
return Err(anyhow!("unsupported rootfs {:?}", &layer));
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut inner = self.inner.write().await;
|
||||||
|
let r = Arc::new(rootfs);
|
||||||
|
inner.rootfs.push(r.clone());
|
||||||
|
Ok(r)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"unsupported rootfs mounts count {}",
|
||||||
|
rootfs_mounts.len()
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn dump(&self) {
|
||||||
|
let inner = self.inner.read().await;
|
||||||
|
for r in &inner.rootfs {
|
||||||
|
info!(
|
||||||
|
"rootfs {:?}: count {}",
|
||||||
|
r.get_guest_rootfs_path().await,
|
||||||
|
Arc::strong_count(r)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_single_layer_rootfs(rootfs_mounts: &[Mount]) -> bool {
|
||||||
|
rootfs_mounts.len() == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_block_device(file_path: &str) -> Option<u64> {
|
||||||
|
if file_path.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match stat::stat(file_path) {
|
||||||
|
Ok(fstat) => {
|
||||||
|
if SFlag::from_bits_truncate(fstat.st_mode) == SFlag::S_IFBLK {
|
||||||
|
return Some(fstat.st_rdev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!("failed to stat for {} {:?}", file_path, err);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
59
src/runtime-rs/crates/resource/src/rootfs/share_fs_rootfs.rs
Normal file
59
src/runtime-rs/crates/resource/src/rootfs/share_fs_rootfs.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
// Copyright (c) 2019-2022 Alibaba Cloud
|
||||||
|
// Copyright (c) 2019-2022 Ant Group
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use kata_sys_util::mount::Mounter;
|
||||||
|
use kata_types::mount::Mount;
|
||||||
|
|
||||||
|
use super::{Rootfs, ROOTFS};
|
||||||
|
use crate::share_fs::{ShareFsMount, ShareFsRootfsConfig};
|
||||||
|
|
||||||
|
pub(crate) struct ShareFsRootfs {
|
||||||
|
guest_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShareFsRootfs {
|
||||||
|
pub async fn new(
|
||||||
|
share_fs_mount: &Arc<dyn ShareFsMount>,
|
||||||
|
cid: &str,
|
||||||
|
bundle_path: &str,
|
||||||
|
rootfs: &Mount,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let bundle_rootfs = format!("{}/{}", bundle_path, ROOTFS);
|
||||||
|
rootfs.mount(&bundle_rootfs).context(format!(
|
||||||
|
"mount rootfs from {:?} to {}",
|
||||||
|
&rootfs, &bundle_rootfs
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let mount_result = share_fs_mount
|
||||||
|
.share_rootfs(ShareFsRootfsConfig {
|
||||||
|
cid: cid.to_string(),
|
||||||
|
source: bundle_rootfs.to_string(),
|
||||||
|
target: ROOTFS.to_string(),
|
||||||
|
readonly: false,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.context("share rootfs")?;
|
||||||
|
|
||||||
|
Ok(ShareFsRootfs {
|
||||||
|
guest_path: mount_result.guest_path,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Rootfs for ShareFsRootfs {
|
||||||
|
async fn get_guest_rootfs_path(&self) -> Result<String> {
|
||||||
|
Ok(self.guest_path.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_rootfs_mount(&self) -> Result<Vec<oci::Mount>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
78
src/runtime-rs/crates/resource/src/share_fs/mod.rs
Normal file
78
src/runtime-rs/crates/resource/src/share_fs/mod.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// Copyright (c) 2019-2022 Alibaba Cloud
|
||||||
|
// Copyright (c) 2019-2022 Ant Group
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
mod share_virtio_fs;
|
||||||
|
mod share_virtio_fs_inline;
|
||||||
|
use share_virtio_fs_inline::ShareVirtioFsInline;
|
||||||
|
mod share_virtio_fs_standalone;
|
||||||
|
use share_virtio_fs_standalone::ShareVirtioFsStandalone;
|
||||||
|
mod utils;
|
||||||
|
mod virtio_fs_share_mount;
|
||||||
|
use virtio_fs_share_mount::VirtiofsShareMount;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use agent::Storage;
|
||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use hypervisor::Hypervisor;
|
||||||
|
use kata_types::config::hypervisor::SharedFsInfo;
|
||||||
|
|
||||||
|
const VIRTIO_FS: &str = "virtio-fs";
|
||||||
|
const INLINE_VIRTIO_FS: &str = "inline-virtio-fs";
|
||||||
|
|
||||||
|
const KATA_HOST_SHARED_DIR: &str = "/run/kata-containers/shared/sandboxes/";
|
||||||
|
const KATA_GUEST_SHARE_DIR: &str = "/run/kata-containers/shared/containers/";
|
||||||
|
pub(crate) const DEFAULT_KATA_GUEST_SANDBOX_DIR: &str = "/run/kata-containers/sandbox/";
|
||||||
|
|
||||||
|
const PASSTHROUGH_FS_DIR: &str = "passthrough";
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait ShareFs: Send + Sync {
|
||||||
|
fn get_share_fs_mount(&self) -> Arc<dyn ShareFsMount>;
|
||||||
|
async fn setup_device_before_start_vm(&self, h: &dyn Hypervisor) -> Result<()>;
|
||||||
|
async fn setup_device_after_start_vm(&self, h: &dyn Hypervisor) -> Result<()>;
|
||||||
|
async fn get_storages(&self) -> Result<Vec<Storage>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ShareFsRootfsConfig {
|
||||||
|
// TODO: for nydus v5/v6 need to update ShareFsMount
|
||||||
|
pub cid: String,
|
||||||
|
pub source: String,
|
||||||
|
pub target: String,
|
||||||
|
pub readonly: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ShareFsVolumeConfig {
|
||||||
|
pub cid: String,
|
||||||
|
pub source: String,
|
||||||
|
pub target: String,
|
||||||
|
pub readonly: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ShareFsMountResult {
|
||||||
|
pub guest_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait ShareFsMount: Send + Sync {
|
||||||
|
async fn share_rootfs(&self, config: ShareFsRootfsConfig) -> Result<ShareFsMountResult>;
|
||||||
|
async fn share_volume(&self, config: ShareFsVolumeConfig) -> Result<ShareFsMountResult>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(id: &str, config: &SharedFsInfo) -> Result<Arc<dyn ShareFs>> {
|
||||||
|
let shared_fs = config.shared_fs.clone();
|
||||||
|
let shared_fs = shared_fs.unwrap_or_default();
|
||||||
|
match shared_fs.as_str() {
|
||||||
|
INLINE_VIRTIO_FS => Ok(Arc::new(
|
||||||
|
ShareVirtioFsInline::new(id, config).context("new inline virtio fs")?,
|
||||||
|
)),
|
||||||
|
VIRTIO_FS => Ok(Arc::new(
|
||||||
|
ShareVirtioFsStandalone::new(id, config).context("new standalone virtio fs")?,
|
||||||
|
)),
|
||||||
|
_ => Err(anyhow!("unsupported shred fs {:?}", &shared_fs)),
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) 2019-2022 Alibaba Cloud
|
||||||
|
// Copyright (c) 2019-2022 Ant Group
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use hypervisor::{device, Hypervisor};
|
||||||
|
use kata_sys_util::mount;
|
||||||
|
|
||||||
|
use super::utils;
|
||||||
|
|
||||||
|
pub(crate) const MOUNT_GUEST_TAG: &str = "kataShared";
|
||||||
|
pub(crate) const PASSTHROUGH_FS_DIR: &str = "passthrough";
|
||||||
|
|
||||||
|
pub(crate) const FS_TYPE_VIRTIO_FS: &str = "virtio_fs";
|
||||||
|
pub(crate) const KATA_VIRTIO_FS_DEV_TYPE: &str = "virtio-fs";
|
||||||
|
|
||||||
|
const VIRTIO_FS_SOCKET: &str = "virtiofsd.sock";
|
||||||
|
|
||||||
|
pub(crate) fn generate_sock_path(root: &str) -> String {
|
||||||
|
let socket_path = Path::new(root).join(VIRTIO_FS_SOCKET);
|
||||||
|
socket_path.to_str().unwrap().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn prepare_virtiofs(
|
||||||
|
h: &dyn Hypervisor,
|
||||||
|
fs_type: &str,
|
||||||
|
id: &str,
|
||||||
|
root: &str,
|
||||||
|
) -> Result<()> {
|
||||||
|
let host_ro_dest = utils::get_host_ro_shared_path(id);
|
||||||
|
utils::ensure_dir_exist(&host_ro_dest)?;
|
||||||
|
|
||||||
|
let host_rw_dest = utils::get_host_rw_shared_path(id);
|
||||||
|
utils::ensure_dir_exist(&host_rw_dest)?;
|
||||||
|
|
||||||
|
mount::bind_mount_unchecked(&host_rw_dest, &host_ro_dest, true)
|
||||||
|
.context("bind mount shared_fs directory")?;
|
||||||
|
|
||||||
|
let share_fs_device = device::Device::ShareFsDevice(device::ShareFsDeviceConfig {
|
||||||
|
sock_path: generate_sock_path(root),
|
||||||
|
mount_tag: String::from(MOUNT_GUEST_TAG),
|
||||||
|
host_path: String::from(host_ro_dest.to_str().unwrap()),
|
||||||
|
fs_type: fs_type.to_string(),
|
||||||
|
queue_size: 0,
|
||||||
|
queue_num: 0,
|
||||||
|
});
|
||||||
|
h.add_device(share_fs_device).await.context("add device")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -0,0 +1,114 @@
|
|||||||
|
// Copyright (c) 2019-2022 Alibaba Cloud
|
||||||
|
// Copyright (c) 2019-2022 Ant Group
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use agent::Storage;
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use hypervisor::{
|
||||||
|
device::{Device as HypervisorDevice, ShareFsMountConfig, ShareFsMountType, ShareFsOperation},
|
||||||
|
Hypervisor,
|
||||||
|
};
|
||||||
|
use kata_types::config::hypervisor::SharedFsInfo;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
share_virtio_fs::{
|
||||||
|
prepare_virtiofs, FS_TYPE_VIRTIO_FS, KATA_VIRTIO_FS_DEV_TYPE, MOUNT_GUEST_TAG,
|
||||||
|
PASSTHROUGH_FS_DIR,
|
||||||
|
},
|
||||||
|
utils, ShareFs, *,
|
||||||
|
};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
pub(crate) static ref SHARED_DIR_VIRTIO_FS_OPTIONS: Vec::<String> = vec![
|
||||||
|
String::from("default_permissions,allow_other,rootmode=040000,user_id=0,group_id=0"),
|
||||||
|
String::from("nodev"),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ShareVirtioFsInlineConfig {
|
||||||
|
pub id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ShareVirtioFsInline {
|
||||||
|
config: ShareVirtioFsInlineConfig,
|
||||||
|
share_fs_mount: Arc<dyn ShareFsMount>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShareVirtioFsInline {
|
||||||
|
pub(crate) fn new(id: &str, _config: &SharedFsInfo) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
config: ShareVirtioFsInlineConfig { id: id.to_string() },
|
||||||
|
share_fs_mount: Arc::new(VirtiofsShareMount::new(id)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl ShareFs for ShareVirtioFsInline {
|
||||||
|
fn get_share_fs_mount(&self) -> Arc<dyn ShareFsMount> {
|
||||||
|
self.share_fs_mount.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn setup_device_before_start_vm(&self, h: &dyn Hypervisor) -> Result<()> {
|
||||||
|
prepare_virtiofs(h, INLINE_VIRTIO_FS, &self.config.id, "")
|
||||||
|
.await
|
||||||
|
.context("prepare virtiofs")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn setup_device_after_start_vm(&self, h: &dyn Hypervisor) -> Result<()> {
|
||||||
|
setup_inline_virtiofs(&self.config.id, h)
|
||||||
|
.await
|
||||||
|
.context("setup inline virtiofs")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
async fn get_storages(&self) -> Result<Vec<Storage>> {
|
||||||
|
// setup storage
|
||||||
|
let mut storages: Vec<Storage> = Vec::new();
|
||||||
|
|
||||||
|
let mut shared_options = SHARED_DIR_VIRTIO_FS_OPTIONS.clone();
|
||||||
|
shared_options.push(format!("tag={}", MOUNT_GUEST_TAG));
|
||||||
|
|
||||||
|
let shared_volume: Storage = Storage {
|
||||||
|
driver: String::from(KATA_VIRTIO_FS_DEV_TYPE),
|
||||||
|
driver_options: Vec::new(),
|
||||||
|
source: String::from(MOUNT_GUEST_TAG),
|
||||||
|
fs_type: String::from(FS_TYPE_VIRTIO_FS),
|
||||||
|
options: shared_options,
|
||||||
|
mount_point: String::from(KATA_GUEST_SHARE_DIR),
|
||||||
|
};
|
||||||
|
|
||||||
|
storages.push(shared_volume);
|
||||||
|
Ok(storages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn setup_inline_virtiofs(id: &str, h: &dyn Hypervisor) -> Result<()> {
|
||||||
|
// - source is the absolute path of PASSTHROUGH_FS_DIR on host, e.g.
|
||||||
|
// /run/kata-containers/shared/sandboxes/<sid>/passthrough
|
||||||
|
// - mount point is the path relative to KATA_GUEST_SHARE_DIR in guest
|
||||||
|
let mnt = format!("/{}", PASSTHROUGH_FS_DIR);
|
||||||
|
|
||||||
|
let rw_source = utils::get_host_rw_shared_path(id).join(PASSTHROUGH_FS_DIR);
|
||||||
|
utils::ensure_dir_exist(&rw_source)?;
|
||||||
|
|
||||||
|
let ro_source = utils::get_host_ro_shared_path(id).join(PASSTHROUGH_FS_DIR);
|
||||||
|
let source = String::from(ro_source.to_str().unwrap());
|
||||||
|
|
||||||
|
let virtio_fs = HypervisorDevice::ShareFsMount(ShareFsMountConfig {
|
||||||
|
source: source.clone(),
|
||||||
|
fstype: ShareFsMountType::PASSTHROUGH,
|
||||||
|
mount_point: mnt,
|
||||||
|
config: None,
|
||||||
|
tag: String::from(MOUNT_GUEST_TAG),
|
||||||
|
op: ShareFsOperation::Mount,
|
||||||
|
prefetch_list_path: None,
|
||||||
|
});
|
||||||
|
h.add_device(virtio_fs)
|
||||||
|
.await
|
||||||
|
.context(format!("fail to attach passthrough fs {:?}", source))
|
||||||
|
}
|
@ -0,0 +1,178 @@
|
|||||||
|
// Copyright (c) 2019-2022 Alibaba Cloud
|
||||||
|
// Copyright (c) 2019-2022 Ant Group
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use std::{process::Stdio, sync::Arc};
|
||||||
|
|
||||||
|
use agent::Storage;
|
||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use hypervisor::Hypervisor;
|
||||||
|
use kata_types::config::hypervisor::SharedFsInfo;
|
||||||
|
use tokio::{
|
||||||
|
io::{AsyncBufReadExt, BufReader},
|
||||||
|
process::{Child, Command},
|
||||||
|
sync::{
|
||||||
|
mpsc::{channel, Receiver, Sender},
|
||||||
|
RwLock,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
share_virtio_fs::generate_sock_path, utils::get_host_ro_shared_path,
|
||||||
|
virtio_fs_share_mount::VirtiofsShareMount, ShareFs, ShareFsMount,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ShareVirtioFsStandaloneConfig {
|
||||||
|
id: String,
|
||||||
|
jail_root: String,
|
||||||
|
|
||||||
|
// virtio_fs_daemon is the virtio-fs vhost-user daemon path
|
||||||
|
pub virtio_fs_daemon: String,
|
||||||
|
// virtio_fs_cache cache mode for fs version cache or "none"
|
||||||
|
pub virtio_fs_cache: String,
|
||||||
|
// virtio_fs_extra_args passes options to virtiofsd daemon
|
||||||
|
pub virtio_fs_extra_args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ShareVirtioFsStandaloneInner {
|
||||||
|
pid: Option<u32>,
|
||||||
|
}
|
||||||
|
pub(crate) struct ShareVirtioFsStandalone {
|
||||||
|
inner: Arc<RwLock<ShareVirtioFsStandaloneInner>>,
|
||||||
|
config: ShareVirtioFsStandaloneConfig,
|
||||||
|
share_fs_mount: Arc<dyn ShareFsMount>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShareVirtioFsStandalone {
|
||||||
|
pub(crate) fn new(id: &str, _config: &SharedFsInfo) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
inner: Arc::new(RwLock::new(ShareVirtioFsStandaloneInner::default())),
|
||||||
|
// TODO: update with config
|
||||||
|
config: ShareVirtioFsStandaloneConfig {
|
||||||
|
id: id.to_string(),
|
||||||
|
jail_root: "".to_string(),
|
||||||
|
virtio_fs_daemon: "".to_string(),
|
||||||
|
virtio_fs_cache: "".to_string(),
|
||||||
|
virtio_fs_extra_args: vec![],
|
||||||
|
},
|
||||||
|
share_fs_mount: Arc::new(VirtiofsShareMount::new(id)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn virtiofsd_args(&self, sock_path: &str) -> Result<Vec<String>> {
|
||||||
|
let source_path = get_host_ro_shared_path(&self.config.id);
|
||||||
|
if !source_path.exists() {
|
||||||
|
return Err(anyhow!("The virtiofs shared path didn't exist"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut args: Vec<String> = vec![
|
||||||
|
String::from("-f"),
|
||||||
|
String::from("-o"),
|
||||||
|
format!("vhost_user_socket={}", sock_path),
|
||||||
|
String::from("-o"),
|
||||||
|
format!("source={}", source_path.to_str().unwrap()),
|
||||||
|
String::from("-o"),
|
||||||
|
format!("cache={}", self.config.virtio_fs_cache),
|
||||||
|
];
|
||||||
|
|
||||||
|
if !self.config.virtio_fs_extra_args.is_empty() {
|
||||||
|
let mut extra_args: Vec<String> = self.config.virtio_fs_extra_args.clone();
|
||||||
|
args.append(&mut extra_args);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn setup_virtiofsd(&self) -> Result<()> {
|
||||||
|
let sock_path = generate_sock_path(&self.config.jail_root);
|
||||||
|
let args = self.virtiofsd_args(&sock_path).context("virtiofsd args")?;
|
||||||
|
|
||||||
|
let mut cmd = Command::new(&self.config.virtio_fs_daemon);
|
||||||
|
let child_cmd = cmd.args(&args).stderr(Stdio::piped());
|
||||||
|
let child = child_cmd.spawn().context("spawn virtiofsd")?;
|
||||||
|
|
||||||
|
// update virtiofsd pid{
|
||||||
|
{
|
||||||
|
let mut inner = self.inner.write().await;
|
||||||
|
inner.pid = child.id();
|
||||||
|
}
|
||||||
|
|
||||||
|
let (tx, mut rx): (Sender<Result<()>>, Receiver<Result<()>>) = channel(100);
|
||||||
|
tokio::spawn(run_virtiofsd(child, tx));
|
||||||
|
|
||||||
|
// TODO: support timeout
|
||||||
|
match rx.recv().await.unwrap() {
|
||||||
|
Ok(_) => {
|
||||||
|
info!(sl!(), "start virtiofsd successfully");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
error!(sl!(), "failed to start virtiofsd {}", e);
|
||||||
|
self.shutdown_virtiofsd()
|
||||||
|
.await
|
||||||
|
.context("shutdown_virtiofsd")?;
|
||||||
|
Err(anyhow!("failed to start virtiofsd"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn shutdown_virtiofsd(&self) -> Result<()> {
|
||||||
|
let mut inner = self.inner.write().await;
|
||||||
|
|
||||||
|
if let Some(pid) = inner.pid.take() {
|
||||||
|
info!(sl!(), "shutdown virtiofsd pid {}", pid);
|
||||||
|
let pid = ::nix::unistd::Pid::from_raw(pid as i32);
|
||||||
|
if let Err(err) = ::nix::sys::signal::kill(pid, nix::sys::signal::SIGKILL) {
|
||||||
|
if err != ::nix::Error::Sys(nix::errno::Errno::ESRCH) {
|
||||||
|
return Err(anyhow!("failed to kill virtiofsd pid {} {}", pid, err));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run_virtiofsd(mut child: Child, tx: Sender<Result<()>>) -> Result<()> {
|
||||||
|
let stderr = child.stderr.as_mut().unwrap();
|
||||||
|
let stderr_reader = BufReader::new(stderr);
|
||||||
|
let mut lines = stderr_reader.lines();
|
||||||
|
|
||||||
|
while let Some(buffer) = lines.next_line().await.context("read next line")? {
|
||||||
|
let trim_buffer = buffer.trim_end();
|
||||||
|
if !trim_buffer.is_empty() {
|
||||||
|
info!(sl!(), "source: virtiofsd {}", trim_buffer);
|
||||||
|
}
|
||||||
|
if buffer.contains("Waiting for vhost-user socket connection") {
|
||||||
|
tx.send(Ok(())).await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!(sl!(), "wait virtiofsd {:?}", child.wait().await);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl ShareFs for ShareVirtioFsStandalone {
|
||||||
|
fn get_share_fs_mount(&self) -> Arc<dyn ShareFsMount> {
|
||||||
|
self.share_fs_mount.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn setup_device_before_start_vm(&self, _h: &dyn Hypervisor) -> Result<()> {
|
||||||
|
self.setup_virtiofsd().await.context("setup virtiofsd")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn setup_device_after_start_vm(&self, _h: &dyn Hypervisor) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_storages(&self) -> Result<Vec<Storage>> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
}
|
94
src/runtime-rs/crates/resource/src/share_fs/utils.rs
Normal file
94
src/runtime-rs/crates/resource/src/share_fs/utils.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
// Copyright (c) 2019-2022 Alibaba Cloud
|
||||||
|
// Copyright (c) 2019-2022 Ant Group
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use kata_sys_util::mount;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub(crate) fn ensure_dir_exist(path: &Path) -> Result<()> {
|
||||||
|
if !path.exists() {
|
||||||
|
std::fs::create_dir_all(path).context(format!("failed to create directory {:?}", path))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn share_to_guest(
|
||||||
|
// absolute path for source
|
||||||
|
source: &str,
|
||||||
|
// relative path for target
|
||||||
|
target: &str,
|
||||||
|
sid: &str,
|
||||||
|
cid: &str,
|
||||||
|
readonly: bool,
|
||||||
|
is_volume: bool,
|
||||||
|
) -> Result<String> {
|
||||||
|
let host_dest = do_get_host_path(target, sid, cid, is_volume, false);
|
||||||
|
mount::bind_mount_unchecked(source, &host_dest, readonly)
|
||||||
|
.context(format!("failed to bind mount {} to {}", source, &host_dest))?;
|
||||||
|
|
||||||
|
// bind mount remount event is not propagated to mount subtrees, so we have
|
||||||
|
// to remount the read only dir mount point directly.
|
||||||
|
if readonly {
|
||||||
|
let dst = do_get_host_path(target, sid, cid, is_volume, true);
|
||||||
|
mount::bind_remount_read_only(&dst).context("bind remount readonly")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(do_get_guest_path(target, cid, is_volume))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_host_ro_shared_path(id: &str) -> PathBuf {
|
||||||
|
Path::new(KATA_HOST_SHARED_DIR).join(id).join("ro")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_host_rw_shared_path(id: &str) -> PathBuf {
|
||||||
|
Path::new(KATA_HOST_SHARED_DIR).join(id).join("rw")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_get_guest_any_path(target: &str, cid: &str, is_volume: bool, is_virtiofs: bool) -> String {
|
||||||
|
let dir = PASSTHROUGH_FS_DIR;
|
||||||
|
let guest_share_dir = if is_virtiofs {
|
||||||
|
Path::new("/")
|
||||||
|
} else {
|
||||||
|
Path::new(KATA_GUEST_SHARE_DIR)
|
||||||
|
};
|
||||||
|
|
||||||
|
let path = if is_volume && !is_virtiofs {
|
||||||
|
guest_share_dir.join(dir).join(target)
|
||||||
|
} else {
|
||||||
|
guest_share_dir.join(dir).join(cid).join(target)
|
||||||
|
};
|
||||||
|
path.to_str().unwrap().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_get_guest_path(target: &str, cid: &str, is_volume: bool) -> String {
|
||||||
|
do_get_guest_any_path(target, cid, is_volume, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_get_host_path(
|
||||||
|
target: &str,
|
||||||
|
sid: &str,
|
||||||
|
cid: &str,
|
||||||
|
is_volume: bool,
|
||||||
|
read_only: bool,
|
||||||
|
) -> String {
|
||||||
|
let dir = PASSTHROUGH_FS_DIR;
|
||||||
|
|
||||||
|
let get_host_path = if read_only {
|
||||||
|
get_host_ro_shared_path
|
||||||
|
} else {
|
||||||
|
get_host_rw_shared_path
|
||||||
|
};
|
||||||
|
|
||||||
|
let path = if is_volume {
|
||||||
|
get_host_path(sid).join(dir).join(target)
|
||||||
|
} else {
|
||||||
|
get_host_path(sid).join(dir).join(cid).join(target)
|
||||||
|
};
|
||||||
|
path.to_str().unwrap().to_string()
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
// 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 super::{utils, ShareFsMount, ShareFsMountResult, ShareFsRootfsConfig, ShareFsVolumeConfig};
|
||||||
|
|
||||||
|
pub struct VirtiofsShareMount {
|
||||||
|
id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtiofsShareMount {
|
||||||
|
pub fn new(id: &str) -> Self {
|
||||||
|
Self { id: id.to_string() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl ShareFsMount for VirtiofsShareMount {
|
||||||
|
async fn share_rootfs(&self, config: ShareFsRootfsConfig) -> Result<ShareFsMountResult> {
|
||||||
|
// TODO: select virtiofs or support nydus
|
||||||
|
let guest_path = utils::share_to_guest(
|
||||||
|
&config.source,
|
||||||
|
&config.target,
|
||||||
|
&self.id,
|
||||||
|
&config.cid,
|
||||||
|
config.readonly,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.context("share to guest")?;
|
||||||
|
Ok(ShareFsMountResult { guest_path })
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn share_volume(&self, config: ShareFsVolumeConfig) -> Result<ShareFsMountResult> {
|
||||||
|
let guest_path = utils::share_to_guest(
|
||||||
|
&config.source,
|
||||||
|
&config.target,
|
||||||
|
&self.id,
|
||||||
|
&config.cid,
|
||||||
|
config.readonly,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.context("share to guest")?;
|
||||||
|
Ok(ShareFsMountResult { guest_path })
|
||||||
|
}
|
||||||
|
}
|
37
src/runtime-rs/crates/resource/src/volume/block_volume.rs
Normal file
37
src/runtime-rs/crates/resource/src/volume/block_volume.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright (c) 2019-2022 Alibaba Cloud
|
||||||
|
// Copyright (c) 2019-2022 Ant Group
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use super::Volume;
|
||||||
|
|
||||||
|
pub(crate) struct BlockVolume {}
|
||||||
|
|
||||||
|
/// BlockVolume: block device volume
|
||||||
|
impl BlockVolume {
|
||||||
|
pub(crate) fn new(_m: &oci::Mount) -> Result<Self> {
|
||||||
|
Ok(Self {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Volume for BlockVolume {
|
||||||
|
fn get_volume_mount(&self) -> anyhow::Result<Vec<oci::Mount>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_storage(&self) -> Result<Vec<agent::Storage>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup(&self) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_block_volume(_m: &oci::Mount) -> bool {
|
||||||
|
// attach block device
|
||||||
|
false
|
||||||
|
}
|
36
src/runtime-rs/crates/resource/src/volume/default_volume.rs
Normal file
36
src/runtime-rs/crates/resource/src/volume/default_volume.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (c) 2019-2022 Alibaba Cloud
|
||||||
|
// Copyright (c) 2019-2022 Ant Group
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use super::Volume;
|
||||||
|
|
||||||
|
pub(crate) struct DefaultVolume {
|
||||||
|
mount: oci::Mount,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// DefaultVolume: passthrough the mount to guest
|
||||||
|
impl DefaultVolume {
|
||||||
|
pub fn new(mount: &oci::Mount) -> Result<Self> {
|
||||||
|
Ok(Self {
|
||||||
|
mount: mount.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Volume for DefaultVolume {
|
||||||
|
fn get_volume_mount(&self) -> anyhow::Result<Vec<oci::Mount>> {
|
||||||
|
Ok(vec![self.mount.clone()])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_storage(&self) -> Result<Vec<agent::Storage>> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup(&self) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
99
src/runtime-rs/crates/resource/src/volume/mod.rs
Normal file
99
src/runtime-rs/crates/resource/src/volume/mod.rs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
// Copyright (c) 2019-2022 Alibaba Cloud
|
||||||
|
// Copyright (c) 2019-2022 Ant Group
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
mod block_volume;
|
||||||
|
mod default_volume;
|
||||||
|
mod share_fs_volume;
|
||||||
|
mod shm_volume;
|
||||||
|
|
||||||
|
use std::{sync::Arc, vec::Vec};
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
use tokio::sync::RwLock;
|
||||||
|
|
||||||
|
use crate::share_fs::ShareFs;
|
||||||
|
|
||||||
|
pub trait Volume: Send + Sync {
|
||||||
|
fn get_volume_mount(&self) -> Result<Vec<oci::Mount>>;
|
||||||
|
fn get_storage(&self) -> Result<Vec<agent::Storage>>;
|
||||||
|
fn cleanup(&self) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct VolumeResourceInner {
|
||||||
|
volumes: Vec<Arc<dyn Volume>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct VolumeResource {
|
||||||
|
inner: Arc<RwLock<VolumeResourceInner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VolumeResource {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn handler_volumes(
|
||||||
|
&self,
|
||||||
|
share_fs: &Option<Arc<dyn ShareFs>>,
|
||||||
|
cid: &str,
|
||||||
|
oci_mounts: &[oci::Mount],
|
||||||
|
) -> Result<Vec<Arc<dyn Volume>>> {
|
||||||
|
let mut volumes: Vec<Arc<dyn Volume>> = vec![];
|
||||||
|
for m in oci_mounts {
|
||||||
|
let volume: Arc<dyn Volume> = if shm_volume::is_shim_volume(m) {
|
||||||
|
let shm_size = shm_volume::DEFAULT_SHM_SIZE;
|
||||||
|
Arc::new(
|
||||||
|
shm_volume::ShmVolume::new(m, shm_size)
|
||||||
|
.context(format!("new shm volume {:?}", m))?,
|
||||||
|
)
|
||||||
|
} else if share_fs_volume::is_share_fs_volume(m) {
|
||||||
|
Arc::new(
|
||||||
|
share_fs_volume::ShareFsVolume::new(share_fs, m, cid)
|
||||||
|
.await
|
||||||
|
.context(format!("new share fs volume {:?}", m))?,
|
||||||
|
)
|
||||||
|
} else if block_volume::is_block_volume(m) {
|
||||||
|
Arc::new(
|
||||||
|
block_volume::BlockVolume::new(m)
|
||||||
|
.context(format!("new block volume {:?}", m))?,
|
||||||
|
)
|
||||||
|
} else if is_skip_volume(m) {
|
||||||
|
info!(sl!(), "skip volume {:?}", m);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
Arc::new(
|
||||||
|
default_volume::DefaultVolume::new(m)
|
||||||
|
.context(format!("new default volume {:?}", m))?,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
volumes.push(volume.clone());
|
||||||
|
let mut inner = self.inner.write().await;
|
||||||
|
inner.volumes.push(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(volumes)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn dump(&self) {
|
||||||
|
let inner = self.inner.read().await;
|
||||||
|
for v in &inner.volumes {
|
||||||
|
info!(
|
||||||
|
sl!(),
|
||||||
|
"volume mount {:?}: count {}",
|
||||||
|
v.get_volume_mount(),
|
||||||
|
Arc::strong_count(v)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_skip_volume(_m: &oci::Mount) -> bool {
|
||||||
|
// TODO: support volume check
|
||||||
|
false
|
||||||
|
}
|
153
src/runtime-rs/crates/resource/src/volume/share_fs_volume.rs
Normal file
153
src/runtime-rs/crates/resource/src/volume/share_fs_volume.rs
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// Copyright (c) 2019-2022 Alibaba Cloud
|
||||||
|
// Copyright (c) 2019-2022 Ant Group
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use std::{path::Path, sync::Arc};
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
use log::debug;
|
||||||
|
use nix::sys::stat::{stat, SFlag};
|
||||||
|
|
||||||
|
use super::Volume;
|
||||||
|
use crate::share_fs::{ShareFs, ShareFsVolumeConfig};
|
||||||
|
|
||||||
|
// copy file to container's rootfs if filesystem sharing is not supported, otherwise
|
||||||
|
// bind mount it in the shared directory.
|
||||||
|
// Ignore /dev, directories and all other device files. We handle
|
||||||
|
// only regular files in /dev. It does not make sense to pass the host
|
||||||
|
// device nodes to the guest.
|
||||||
|
// skip the volumes whose source had already set to guest share dir.
|
||||||
|
pub(crate) struct ShareFsVolume {
|
||||||
|
mounts: Vec<oci::Mount>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShareFsVolume {
|
||||||
|
pub(crate) async fn new(
|
||||||
|
share_fs: &Option<Arc<dyn ShareFs>>,
|
||||||
|
m: &oci::Mount,
|
||||||
|
cid: &str,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let file_name = Path::new(&m.source).file_name().unwrap().to_str().unwrap();
|
||||||
|
let file_name = generate_mount_path(cid, file_name);
|
||||||
|
|
||||||
|
let mut volume = Self { mounts: vec![] };
|
||||||
|
match share_fs {
|
||||||
|
None => {
|
||||||
|
let mut need_copy = false;
|
||||||
|
match stat(Path::new(&m.source)) {
|
||||||
|
Ok(stat) => {
|
||||||
|
// Ignore the mount if this is not a regular file (excludes
|
||||||
|
// directory, socket, device, ...) as it cannot be handled by
|
||||||
|
// a simple copy. But this should not be treated as an error,
|
||||||
|
// only as a limitation.
|
||||||
|
// golang implement:
|
||||||
|
// ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket |
|
||||||
|
// ModeDevice | ModeCharDevice | ModeIrregular
|
||||||
|
let file_type = SFlag::S_IFDIR
|
||||||
|
| SFlag::S_IFLNK
|
||||||
|
| SFlag::S_IFIFO
|
||||||
|
| SFlag::S_IFSOCK
|
||||||
|
| SFlag::S_IFCHR
|
||||||
|
| SFlag::S_IFREG;
|
||||||
|
if !file_type.contains(SFlag::from_bits_truncate(stat.st_mode)) {
|
||||||
|
debug!(
|
||||||
|
"Ignoring non-regular file as FS sharing not supported. mount: {:?}",
|
||||||
|
m
|
||||||
|
);
|
||||||
|
return Ok(volume);
|
||||||
|
}
|
||||||
|
if SFlag::from_bits_truncate(stat.st_mode) != SFlag::S_IFDIR {
|
||||||
|
need_copy = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return Err(anyhow!(format!(
|
||||||
|
"failed to stat file {} {:?}",
|
||||||
|
&m.source, err
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if need_copy {
|
||||||
|
// TODO: copy file
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(share_fs) => {
|
||||||
|
let share_fs_mount = share_fs.get_share_fs_mount();
|
||||||
|
let mount_result = share_fs_mount
|
||||||
|
.share_volume(ShareFsVolumeConfig {
|
||||||
|
cid: cid.to_string(),
|
||||||
|
source: m.source.clone(),
|
||||||
|
target: file_name,
|
||||||
|
readonly: false,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.context("share fs volume")?;
|
||||||
|
|
||||||
|
volume.mounts.push(oci::Mount {
|
||||||
|
destination: m.destination.clone(),
|
||||||
|
r#type: "bind".to_string(),
|
||||||
|
source: mount_result.guest_path,
|
||||||
|
options: m.options.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(volume)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Volume for ShareFsVolume {
|
||||||
|
fn get_volume_mount(&self) -> anyhow::Result<Vec<oci::Mount>> {
|
||||||
|
Ok(self.mounts.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_storage(&self) -> Result<Vec<agent::Storage>> {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup(&self) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_share_fs_volume(m: &oci::Mount) -> bool {
|
||||||
|
m.r#type == "bind" && !is_host_device(&m.destination)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_host_device(dest: &str) -> bool {
|
||||||
|
if dest == "/dev" {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if dest.starts_with("/dev") {
|
||||||
|
let src = match std::fs::canonicalize(dest) {
|
||||||
|
Err(_) => return false,
|
||||||
|
Ok(src) => src,
|
||||||
|
};
|
||||||
|
|
||||||
|
if src.is_file() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note, don't generate random name, attaching rafs depends on the predictable name.
|
||||||
|
// If template_mnt is passed, just use existed name in it
|
||||||
|
pub fn generate_mount_path(id: &str, file_name: &str) -> String {
|
||||||
|
let mut nid = String::from(id);
|
||||||
|
if nid.len() > 10 {
|
||||||
|
nid = nid.chars().take(10).collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut uid = uuid::Uuid::new_v4().to_string();
|
||||||
|
let uid_vec: Vec<&str> = uid.splitn(2, '-').collect();
|
||||||
|
uid = String::from(uid_vec[0]);
|
||||||
|
|
||||||
|
format!("{}-{}-{}", nid, uid, file_name)
|
||||||
|
}
|
105
src/runtime-rs/crates/resource/src/volume/shm_volume.rs
Normal file
105
src/runtime-rs/crates/resource/src/volume/shm_volume.rs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// Copyright (c) 2019-2022 Alibaba Cloud
|
||||||
|
// Copyright (c) 2019-2022 Ant Group
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use super::Volume;
|
||||||
|
use crate::share_fs::DEFAULT_KATA_GUEST_SANDBOX_DIR;
|
||||||
|
|
||||||
|
pub const SHM_DIR: &str = "shm";
|
||||||
|
// DEFAULT_SHM_SIZE is the default shm size to be used in case host
|
||||||
|
// IPC is used.
|
||||||
|
pub const DEFAULT_SHM_SIZE: u64 = 65536 * 1024;
|
||||||
|
|
||||||
|
// KATA_EPHEMERAL_DEV_TYPE creates a tmpfs backed volume for sharing files between containers.
|
||||||
|
pub const KATA_EPHEMERAL_DEV_TYPE: &str = "ephemeral";
|
||||||
|
|
||||||
|
pub(crate) struct ShmVolume {
|
||||||
|
mount: oci::Mount,
|
||||||
|
storage: Option<agent::Storage>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShmVolume {
|
||||||
|
pub(crate) fn new(m: &oci::Mount, shm_size: u64) -> Result<Self> {
|
||||||
|
let (storage, mount) = if shm_size > 0 {
|
||||||
|
// storage
|
||||||
|
let mount_path = Path::new(DEFAULT_KATA_GUEST_SANDBOX_DIR).join(SHM_DIR);
|
||||||
|
let mount_path = mount_path.to_str().unwrap();
|
||||||
|
let option = format!("size={}", shm_size);
|
||||||
|
|
||||||
|
let options = vec![
|
||||||
|
String::from("noexec"),
|
||||||
|
String::from("nosuid"),
|
||||||
|
String::from("nodev"),
|
||||||
|
String::from("mode=1777"),
|
||||||
|
option,
|
||||||
|
];
|
||||||
|
|
||||||
|
let storage = agent::Storage {
|
||||||
|
driver: String::from(KATA_EPHEMERAL_DEV_TYPE),
|
||||||
|
driver_options: Vec::new(),
|
||||||
|
source: String::from("shm"),
|
||||||
|
fs_type: String::from("tmpfs"),
|
||||||
|
options,
|
||||||
|
mount_point: mount_path.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// mount
|
||||||
|
let mount = oci::Mount {
|
||||||
|
r#type: "bind".to_string(),
|
||||||
|
destination: m.destination.clone(),
|
||||||
|
source: mount_path.to_string(),
|
||||||
|
options: vec!["rbind".to_string()],
|
||||||
|
};
|
||||||
|
|
||||||
|
(Some(storage), mount)
|
||||||
|
} else {
|
||||||
|
let mount = oci::Mount {
|
||||||
|
r#type: "tmpfs".to_string(),
|
||||||
|
destination: m.destination.clone(),
|
||||||
|
source: "shm".to_string(),
|
||||||
|
options: vec![
|
||||||
|
"noexec",
|
||||||
|
"nosuid",
|
||||||
|
"nodev",
|
||||||
|
"mode=1777",
|
||||||
|
&format!("size={}", DEFAULT_SHM_SIZE),
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
(None, mount)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Self { storage, mount })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Volume for ShmVolume {
|
||||||
|
fn get_volume_mount(&self) -> anyhow::Result<Vec<oci::Mount>> {
|
||||||
|
Ok(vec![self.mount.clone()])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_storage(&self) -> Result<Vec<agent::Storage>> {
|
||||||
|
let s = if let Some(s) = self.storage.as_ref() {
|
||||||
|
vec![s.clone()]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cleanup(&self) -> Result<()> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_shim_volume(m: &oci::Mount) -> bool {
|
||||||
|
m.destination == "/dev/shm" && m.r#type != KATA_EPHEMERAL_DEV_TYPE
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user