Files
kata-containers/src/tools/runk/libcontainer/src/builder.rs
Manabu Sugimoto 648b8d0aec runk: Return error when tty is used without console socket
runk always launches containers with detached mode,
so users have to use a console socket with run or
create operation when a terminal is used.
If users set `terminal` to `true` in `config.json` and
try to launch a container without specifying a console
socket, runk returns an error with a message early.

Fixes: #4324

Signed-off-by: Manabu Sugimoto <Manabu.Sugimoto@sony.com>
2022-05-31 09:55:39 +09:00

165 lines
5.2 KiB
Rust

// Copyright 2021-2022 Sony Group Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
use crate::container::{get_config_path, ContainerContext};
use anyhow::{anyhow, Result};
use derive_builder::Builder;
use oci::Spec;
use std::path::{Path, PathBuf};
#[derive(Default, Builder, Debug)]
pub struct Container {
id: String,
bundle: PathBuf,
root: PathBuf,
console_socket: Option<PathBuf>,
}
impl Container {
pub fn create_ctx(self) -> Result<ContainerContext> {
let bundle_canon = self.bundle.canonicalize()?;
let config_path = get_config_path(&bundle_canon);
let mut spec = Spec::load(
config_path
.to_str()
.ok_or_else(|| anyhow!("invalid config path"))?,
)?;
if spec.root.is_some() {
let mut spec_root = spec
.root
.as_mut()
.ok_or_else(|| anyhow!("root config was not present in the spec file"))?;
let rootfs_path = Path::new(&spec_root.path);
// If the rootfs path in the spec file is a relative path,
// convert it into a canonical path to pass validation of rootfs in the agent.
if !&rootfs_path.is_absolute() {
let rootfs_name = rootfs_path
.file_name()
.ok_or_else(|| anyhow!("invalid rootfs name"))?;
spec_root.path = bundle_canon
.join(rootfs_name)
.to_str()
.map(|s| s.to_string())
.ok_or_else(|| anyhow!("failed to convert bundle path"))?;
}
}
if let Some(process) = spec.process.as_ref() {
// runk always launches containers with detached mode, so users have to
// use a console socket with run or create operation when a terminal is used.
if process.terminal && self.console_socket.is_none() {
return Err(anyhow!(
"cannot allocate a pseudo-TTY without setting a console socket"
));
}
}
Ok(ContainerContext {
id: self.id,
bundle: self.bundle,
state_root: self.root,
spec,
// TODO: liboci-cli does not support --no-pivot option for create and run command.
// After liboci-cli supports the option, we will change the following code.
// no_pivot_root: self.no_pivot,
no_pivot_root: false,
console_socket: self.console_socket,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::container::CONFIG_FILE_NAME;
use oci::{self, Spec};
use std::{fs::File, path::PathBuf};
use tempfile::tempdir;
#[derive(Debug)]
struct TestData {
id: String,
bundle: PathBuf,
root: PathBuf,
console_socket: Option<PathBuf>,
spec: Spec,
no_pivot_root: bool,
}
#[test]
fn test_create_ctx() {
let bundle_dir = tempdir().unwrap();
let config_file = bundle_dir.path().join(CONFIG_FILE_NAME);
let spec = Spec::default();
let file = File::create(config_file).unwrap();
serde_json::to_writer(&file, &spec).unwrap();
let test_data = TestData {
id: String::from("test"),
bundle: PathBuf::from(bundle_dir.into_path()),
root: PathBuf::from("test"),
console_socket: Some(PathBuf::from("test")),
spec: Spec::default(),
no_pivot_root: false,
};
let test_ctx = ContainerContext {
id: test_data.id.clone(),
bundle: test_data.bundle.clone(),
state_root: test_data.root.clone(),
spec: test_data.spec.clone(),
no_pivot_root: test_data.no_pivot_root,
console_socket: test_data.console_socket.clone(),
};
let ctx = ContainerBuilder::default()
.id(test_data.id.clone())
.bundle(test_data.bundle.clone())
.root(test_data.root.clone())
.console_socket(test_data.console_socket.clone())
.build()
.unwrap()
.create_ctx()
.unwrap();
assert_eq!(test_ctx, ctx);
}
#[test]
fn test_create_ctx_tty_err() {
let bundle_dir = tempdir().unwrap();
let config_file = bundle_dir.path().join(CONFIG_FILE_NAME);
let mut spec = Spec::default();
spec.process = Some(oci::Process::default());
spec.process.as_mut().unwrap().terminal = true;
let file = File::create(config_file).unwrap();
serde_json::to_writer(&file, &spec).unwrap();
let test_data = TestData {
id: String::from("test"),
bundle: PathBuf::from(bundle_dir.into_path()),
root: PathBuf::from("test"),
console_socket: None,
spec: Spec::default(),
no_pivot_root: false,
};
let ctx = ContainerBuilder::default()
.id(test_data.id.clone())
.bundle(test_data.bundle.clone())
.root(test_data.root.clone())
.console_socket(test_data.console_socket.clone())
.build()
.unwrap()
.create_ctx();
assert!(ctx.is_err());
}
}