From 46753a85c201f070ed6c2aa6ed671842f377e399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Giba=C5=82a?= Date: Mon, 14 Feb 2022 09:20:50 +0100 Subject: [PATCH] Add unit tests for determine_root_path --- crates/youki/src/main.rs | 128 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 2 deletions(-) diff --git a/crates/youki/src/main.rs b/crates/youki/src/main.rs index 3c57258778..71d84864ef 100644 --- a/crates/youki/src/main.rs +++ b/crates/youki/src/main.rs @@ -9,6 +9,7 @@ use anyhow::Context; use anyhow::Result; use clap::IntoApp; use clap::{crate_version, Parser}; +use nix::libc; use std::fs; use std::path::{Path, PathBuf}; @@ -132,7 +133,7 @@ fn determine_root_path(root_path: Option) -> Result { let uid = getuid().as_raw(); if !rootless_required() { - let path = PathBuf::from("/run/youki"); + let path = get_default_not_rootless_path(); create_dir_all_with_mode(&path, uid, Mode::S_IRWXU)?; return Ok(path); } @@ -146,7 +147,7 @@ fn determine_root_path(root_path: Option) -> Result { } // XDG_RUNTIME_DIR is not set, try the usual location - let path = PathBuf::from(format!("/run/user/{}/youki", uid)); + let path = get_default_rootless_path(uid); if create_dir_all_with_mode(&path, uid, Mode::S_IRWXU).is_ok() { return Ok(path); } @@ -167,3 +168,126 @@ fn determine_root_path(root_path: Option) -> Result { bail!("could not find a storage location with suitable permissions for the current user"); } + +#[cfg(not(test))] +fn get_default_not_rootless_path() -> PathBuf { + PathBuf::from("/run/youki") +} + +#[cfg(test)] +fn get_default_not_rootless_path() -> PathBuf { + libcontainer::utils::get_temp_dir_path("default_youki_path") +} + +#[cfg(not(test))] +fn get_default_rootless_path(uid: libc::uid_t) -> PathBuf { + PathBuf::from(format!("/run/user/{}/youki", uid)) +} + +#[cfg(test)] +fn get_default_rootless_path(uid: libc::uid_t) -> PathBuf { + libcontainer::utils::get_temp_dir_path(format!("default_rootless_youki_path_{}", uid).as_str()) +} + +#[cfg(test)] +mod tests { + use crate::determine_root_path; + use anyhow::{Context, Result}; + use libcontainer::utils::{get_temp_dir_path, TempDir}; + use nix::sys::stat::Mode; + use nix::unistd::getuid; + use std::fs; + use std::fs::Permissions; + use std::os::unix::fs::PermissionsExt; + use std::path::PathBuf; + + #[test] + fn test_determine_root_path_use_specified_by_user() -> Result<()> { + let specified_path = PathBuf::from(get_temp_dir_path("provided_path")); + let path = determine_root_path(Some(specified_path.clone())) + .context("failed with specified path")?; + assert_eq!(path, specified_path); + + Ok(()) + } + + #[test] + fn test_determine_root_path_non_rootless() -> Result<()> { + let expected_path = PathBuf::from(get_temp_dir_path("default_youki_path")); + + let path = determine_root_path(None).context("failed with default non rootless path")?; + assert_eq!(path, expected_path.clone()); + assert!(path.exists()); + + fs::remove_dir(&expected_path).context("failed to remove dir")?; + + // Setup TempDir with invalid permissions so it is cleaned up after test. + let _temp_dir = TempDir::new(&expected_path).context("failed to create temp dir")?; + fs::set_permissions(&expected_path, Permissions::from_mode(Mode::S_IRUSR.bits())) + .context("failed to set invalid permissions")?; + + assert!(determine_root_path(None).is_err()); + + Ok(()) + } + + #[test] + fn test_determine_root_path_rootless() -> Result<()> { + std::env::set_var("YOUKI_USE_ROOTLESS", "true"); + + // XDG_RUNTIME_DIR + let xdg_dir = get_temp_dir_path("xdg_runtime"); + std::env::set_var("XDG_RUNTIME_DIR", &xdg_dir); + let path = determine_root_path(None).context("failed with $XDG_RUNTIME_DIR path")?; + assert_eq!(path, xdg_dir.join("youki")); + assert!(path.exists()); + + std::env::remove_var("XDG_RUNTIME_DIR"); + + // Default rootless location + let uid = getuid().as_raw(); + let default_rootless_path = + get_temp_dir_path(format!("default_rootless_youki_path_{}", uid).as_str()); + // Create temp dir so it gets cleaned up. This is needed as we later switch permissions of this directory. + let _temp_dir = + TempDir::new(&default_rootless_path).context("failed to create temp dir")?; + let path = determine_root_path(None).context("failed with default rootless path")?; + assert_eq!(path, default_rootless_path.clone()); + assert!(path.exists()); + + // Set invalid permissions to default rootless path so that it fails for the next test. + fs::set_permissions( + default_rootless_path, + Permissions::from_mode(Mode::S_IRUSR.bits()), + ) + .context("failed to set invalid permissions")?; + + // Use HOME env var + let home_path = get_temp_dir_path("youki_home"); + fs::create_dir_all(&home_path).context("failed to create fake home path")?; + std::env::set_var("HOME", &home_path); + let path = determine_root_path(None).context("failed with $HOME path")?; + assert_eq!(path, home_path.join(".youki/run")); + assert!(path.exists()); + + std::env::remove_var("HOME"); + + // Use temp dir + let expected_temp_path = PathBuf::from(format!("/tmp/youki-{}", uid)); + // Create temp dir so it gets cleaned up. This is needed as we later switch permissions of this directory. + let _temp_dir = TempDir::new(&expected_temp_path).context("failed to create temp dir")?; + let path = determine_root_path(None).context("failed with temp path")?; + assert_eq!(path, expected_temp_path.clone()); + + // Set invalid permissions to temp path so determine_root_path fails. + fs::set_permissions( + expected_temp_path, + Permissions::from_mode(Mode::S_IRUSR.bits()), + ) + .context("failed to set invalid permissions")?; + + assert!(determine_root_path(None).is_err()); + + Ok(()) + } +}