Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vmm: setup hosts, hostname and resolv.conf files #100

Merged
merged 2 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading