Skip to content

Commit

Permalink
Merge pull request #100 from Burning1020/sandbox-dns
Browse files Browse the repository at this point in the history
vmm: setup hosts, hostname and resolv.conf files
  • Loading branch information
abel-von authored Jan 11, 2024
2 parents 4f43907 + 1df144a commit 6a599c0
Show file tree
Hide file tree
Showing 11 changed files with 441 additions and 27 deletions.
8 changes: 8 additions & 0 deletions vmm/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,11 @@ pub const KUASAR_STATE_DIR: &str = "/run/kuasar/state";
pub const IO_FILE_PREFIX: &str = "io";
pub const STORAGE_FILE_PREFIX: &str = "storage";
pub const SHARED_DIR_SUFFIX: &str = "shared";

pub const ETC_HOSTS: &str = "/etc/hosts";
pub const ETC_HOSTNAME: &str = "/etc/hostname";
pub const ETC_RESOLV: &str = "/etc/resolv.conf";
pub const DEV_SHM: &str = "/dev/shm";
pub const HOSTS_FILENAME: &str = "hosts";
pub const HOSTNAME_FILENAME: &str = "hostname";
pub const RESOLV_FILENAME: &str = "resolv.conf";
32 changes: 32 additions & 0 deletions vmm/sandbox/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions vmm/sandbox/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ ttrpc = { version = "0.7", features = ["async"] }
protobuf = "3.2"
cgroups-rs = "0.3.2"
proc-macro2 = "1.0.66"
hostname = "0.3"
path-clean = "1.0.1"

[[bin]]
name = "qemu"
Expand All @@ -55,3 +57,7 @@ path = "src/bin/cloud_hypervisor/main.rs"
[[bin]]
name = "stratovirt"
path = "src/bin/stratovirt/main.rs"

[dev-dependencies]
temp-dir = "0.1.11"

6 changes: 3 additions & 3 deletions vmm/sandbox/src/container/handler/append.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ limitations under the License.
use anyhow::anyhow;
use async_trait::async_trait;
use containerd_sandbox::{error::Result, ContainerOption};
use vmm_common::SHARED_DIR_SUFFIX;

use crate::{
container::{handler::Handler, KuasarContainer},
Expand Down Expand Up @@ -52,8 +51,9 @@ where
processes: vec![],
};
let bundle = format!(
"{}/{}/{}",
sandbox.base_dir, SHARED_DIR_SUFFIX, self.option.container.id
"{}/{}",
sandbox.get_sandbox_shared_path(),
self.option.container.id
);
tokio::fs::create_dir_all(&bundle)
.await
Expand Down
188 changes: 184 additions & 4 deletions vmm/sandbox/src/container/handler/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

use std::path::Path;

use anyhow::anyhow;
use async_trait::async_trait;
use containerd_sandbox::error::Error;
use vmm_common::SHARED_DIR_SUFFIX;
use containerd_sandbox::{
error::Error,
spec::{JsonSpec, Mount},
};
use path_clean::clean;
use vmm_common::{
ETC_HOSTNAME, ETC_HOSTS, ETC_RESOLV, HOSTNAME_FILENAME, HOSTS_FILENAME, KUASAR_STATE_DIR,
RESOLV_FILENAME,
};

use crate::{
container::handler::Handler, sandbox::KuasarSandbox, utils::write_file_atomic, vm::VM,
Expand Down Expand Up @@ -46,6 +55,7 @@ where
&self,
sandbox: &mut KuasarSandbox<T>,
) -> containerd_sandbox::error::Result<()> {
let shared_path = sandbox.get_sandbox_shared_path();
let container = sandbox.container_mut(&self.container_id)?;
let spec = container
.data
Expand All @@ -59,6 +69,8 @@ where
if let Some(p) = spec.process.as_mut() {
p.apparmor_profile = "".to_string();
}
// Update sandbox files mounts for container
container_mounts(&shared_path, spec);
let spec_str = serde_json::to_string(spec)
.map_err(|e| anyhow!("failed to parse spec in sandbox, {}", e))?;
let config_path = format!("{}/{}", container.data.bundle, CONFIG_FILE_NAME);
Expand All @@ -71,12 +83,180 @@ where
sandbox: &mut KuasarSandbox<T>,
) -> containerd_sandbox::error::Result<()> {
let bundle = format!(
"{}/{}/{}",
sandbox.base_dir, SHARED_DIR_SUFFIX, self.container_id
"{}/{}",
sandbox.get_sandbox_shared_path(),
self.container_id
);
tokio::fs::remove_dir_all(&*bundle)
.await
.map_err(|e| anyhow!("failed to remove container bundle, {}", e))?;
Ok(())
}
}

// container_mounts sets up necessary container system file mounts
// including /etc/hostname, /etc/hosts and /etc/resolv.conf.
fn container_mounts(shared_path: &str, spec: &mut JsonSpec) {
let rw_option = if spec.root.as_ref().map(|r| r.readonly).unwrap_or_default() {
"ro"
} else {
"rw"
};

let mut extra_mounts: Vec<Mount> = vec![];
let cri_mount_handler = |dst, filename, extra_mounts: &mut Vec<Mount>| {
if !is_in_cri_mounts(dst, &spec.mounts) {
let host_path = format!("{}/{}", shared_path, filename);
// If host path exist, should add it to container mount
if Path::exists(host_path.as_ref()) {
extra_mounts.push(Mount {
destination: dst.to_string(),
r#type: "bind".to_string(),
source: format!("{}/{}", KUASAR_STATE_DIR, filename),
options: vec!["rbind", "rprivate", rw_option]
.into_iter()
.map(String::from)
.collect(),
});
}
}
};

cri_mount_handler(ETC_HOSTNAME, HOSTNAME_FILENAME, &mut extra_mounts);
cri_mount_handler(ETC_HOSTS, HOSTS_FILENAME, &mut extra_mounts);
cri_mount_handler(ETC_RESOLV, RESOLV_FILENAME, &mut extra_mounts);
spec.mounts.append(&mut extra_mounts);
}

fn is_in_cri_mounts(dst: &str, mounts: &Vec<Mount>) -> bool {
for mount in mounts {
if clean(&mount.destination) == clean(dst) {
return true;
}
}
false
}

#[cfg(test)]
mod tests {
use std::path::Path;

use containerd_sandbox::spec::{JsonSpec, Mount, Root};
use containerd_shim::util::write_str_to_file;
use temp_dir::TempDir;

use crate::container::handler::spec::{container_mounts, is_in_cri_mounts};

fn generate_cri_mounts() -> Vec<Mount> {
vec![
Mount {
destination: "/etc/hosts".to_string(),
r#type: "".to_string(),
source: "/run/kuasar-vmm/0d042b22dbaf083f704c5945488c7f63d10a664f743802a7901ac6fae6460d9b/shared/hosts".to_string(),
options: vec![],
},
Mount {
destination: "/etc/hostname".to_string(),
r#type: "".to_string(),
source: "/run/kuasar-vmm/0d042b22dbaf083f704c5945488c7f63d10a664f743802a7901ac6fae6460d9b/shared/hostname".to_string(),
options: vec![],
},
Mount {
destination: "/etc/resolv.conf".to_string(),
r#type: "".to_string(),
source: "/run/kuasar-vmm/0d042b22dbaf083f704c5945488c7f63d10a664f743802a7901ac6fae6460d9b/shared/resolv.conf".to_string(),
options: vec![],
},
Mount {
destination: "/dev/shm".to_string(),
r#type: "".to_string(),
source: "/run/kuasar-vmm/0d042b22dbaf083f704c5945488c7f63d10a664f743802a7901ac6fae6460d9b/shared/shm".to_string(),
options: vec![],
},
]
}

#[test]
fn test_is_in_cri_mounts() {
let cri_mounts = generate_cri_mounts();
assert!(is_in_cri_mounts("/etc/hostname", &cri_mounts));
assert!(is_in_cri_mounts("/etc/hosts", &cri_mounts));
assert!(is_in_cri_mounts("/etc/resolv.conf", &cri_mounts));
assert!(is_in_cri_mounts("/dev/shm", &cri_mounts));
assert!(!is_in_cri_mounts("/var/lib/kuasar", &cri_mounts));
}

#[tokio::test]
// When no mount defined in spec and kuasar doesn't create these files, expect no mount added.
async fn test_container_mounts_with_target_file_not_exist() {
let mut spec = JsonSpec::default();
spec.mounts = vec![];

let tmp_path = TempDir::new().unwrap();
let shared_path = tmp_path.path().to_str().unwrap();
container_mounts(shared_path, &mut spec);
assert_eq!(spec.mounts.len(), 0);
}

#[tokio::test]
// When no mount defined in spec and kuasar created hostname file, expect hostname mount is added.
async fn test_container_mounts_with_hostname_file_exist() {
let mut spec = JsonSpec::default();
spec.mounts = vec![];
spec.root = Some(Root::default());
assert!(!spec.root.clone().expect("root should not be None").readonly);

let tmp_path = TempDir::new().unwrap();
let shared_path = tmp_path.path().to_str().unwrap();
write_str_to_file(tmp_path.child("hostname"), "kuasar-deno-001")
.await
.unwrap();
container_mounts(shared_path, &mut spec);
assert_eq!(spec.mounts.len(), 1);
assert!(spec.mounts[0].options.contains(&"rw".to_string()));
let parent_path = Path::new(&spec.mounts[0].source)
.parent()
.unwrap()
.to_str()
.unwrap();
assert_eq!(parent_path, "/run/kuasar/state");
}

#[tokio::test]
// When readonly rootfs defined in spec, expect hostname mount is readonly.
async fn test_container_mounts_with_mounts_and_ro() {
let mut spec = JsonSpec::default();
spec.mounts = vec![];
spec.root = Some(Root {
path: "".to_string(),
readonly: true,
});
assert!(spec.root.clone().expect("root should not be None").readonly);

let tmp_path = TempDir::new().unwrap();
let shared_path = tmp_path.path().to_str().unwrap();
write_str_to_file(tmp_path.child("hostname"), "kuasar-deno-001")
.await
.unwrap();
container_mounts(shared_path, &mut spec);
assert_eq!(spec.mounts.len(), 1);
assert!(spec.mounts[0].options.contains(&"ro".to_string()));
}

#[tokio::test]
// When hostname mount already defined, expect hostname mount is no changed.
async fn test_container_mounts_with_mount_predefined() {
let mut spec = JsonSpec::default();
let cri_mount = generate_cri_mounts();
spec.mounts = cri_mount.clone();

let tmp_path = TempDir::new().unwrap();
let shared_path = tmp_path.path().to_str().unwrap();
container_mounts(shared_path, &mut spec);
assert_eq!(spec.mounts.len(), 4);
assert_eq!(cri_mount[0].source, spec.mounts[0].source);
assert_eq!(cri_mount[0].r#type, spec.mounts[0].r#type);
assert_eq!(cri_mount[0].destination, spec.mounts[0].destination);
assert_eq!(cri_mount[0].options, spec.mounts[0].options);
}
}
4 changes: 2 additions & 2 deletions vmm/sandbox/src/container/handler/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use containerd_sandbox::{
Sandbox,
};
use log::debug;
use vmm_common::{storage::ANNOTATION_KEY_STORAGE, STORAGE_FILE_PREFIX};
use vmm_common::{storage::ANNOTATION_KEY_STORAGE, DEV_SHM, STORAGE_FILE_PREFIX};

use crate::{
container::handler::Handler, sandbox::KuasarSandbox, storage::mount::is_bind_shm,
Expand Down Expand Up @@ -68,7 +68,7 @@ where
}
// TODO if vmm-task mount shm when startup, then just use the same shm
if is_bind_shm(&m) {
m.source = "/dev/shm".to_string();
m.source = DEV_SHM.to_string();
m.options.push("rbind".to_string());
}
handled_mounts.push(m);
Expand Down
Loading

0 comments on commit 6a599c0

Please sign in to comment.