agent: Append the container OCI spec with the image bundle one

We do not get a root filesystem path from the agent when creating a
new container for which the container image was not pulled by
containerd. That prevents the agent from creating the container.

To fix that, we populate the container root path with the internal
rootfs path by fetching the containerd added image name annotation and
mapping it back to a path through our image hash map.

Fixes #3009

Signed-off-by: wllenyj <wllenyj@linux.alibaba.com>
Signed-off-by: Samuel Ortiz <s.ortiz@apple.com>
This commit is contained in:
wllenyj 2021-11-10 15:56:29 +08:00 committed by Samuel Ortiz
parent 5691e66e1b
commit ab6b1cbfe9

View File

@ -79,6 +79,8 @@ use std::path::PathBuf;
pub const CONTAINER_BASE: &str = "/run/kata-containers"; pub const CONTAINER_BASE: &str = "/run/kata-containers";
const MODPROBE_PATH: &str = "/sbin/modprobe"; const MODPROBE_PATH: &str = "/sbin/modprobe";
const ANNO_K8S_IMAGE_NAME: &str = "io.kubernetes.cri.image-name";
const CONFIG_JSON: &str = "config.json";
// Convenience macro to obtain the scope logger // Convenience macro to obtain the scope logger
macro_rules! sl { macro_rules! sl {
@ -124,6 +126,19 @@ pub fn verify_cid(id: &str) -> Result<()> {
} }
} }
// Partially merge an OCI process specification into another one.
fn merge_oci_process(target: &mut oci::Process, source: &oci::Process) {
if target.args.is_empty() && !source.args.is_empty() {
target.args.append(&mut source.args.clone());
}
if target.cwd.is_empty() && !source.cwd.is_empty() {
target.cwd = String::from(&source.cwd);
}
target.env.append(&mut source.env.clone());
}
impl AgentService { impl AgentService {
#[instrument] #[instrument]
async fn do_create_container( async fn do_create_container(
@ -150,6 +165,9 @@ impl AgentService {
info!(sl!(), "receive createcontainer, spec: {:?}", &oci); info!(sl!(), "receive createcontainer, spec: {:?}", &oci);
// Merge the image bundle OCI spec into the container creation request OCI spec.
self.merge_bundle_oci(&mut oci).await?;
// Some devices need some extra processing (the ones invoked with // Some devices need some extra processing (the ones invoked with
// --device for instance), and that's what this call is doing. It // --device for instance), and that's what this call is doing. It
// updates the devices listed in the OCI spec, so that they actually // updates the devices listed in the OCI spec, so that they actually
@ -537,6 +555,54 @@ impl AgentService {
} }
} }
} }
// When being passed an image name through a container annotation, merge its
// corresponding bundle OCI specification into the passed container creation one.
async fn merge_bundle_oci(&self, container_oci: &mut oci::Spec) -> Result<()> {
if let Some(image_name) = container_oci
.annotations
.get(&ANNO_K8S_IMAGE_NAME.to_string())
{
if let Some(container_id) = self.sandbox.clone().lock().await.images.get(image_name) {
let image_oci_config_path = Path::new(CONTAINER_BASE)
.join(container_id)
.join(CONFIG_JSON);
debug!(
sl!(),
"Image bundle config path: {:?}", image_oci_config_path
);
let image_oci =
oci::Spec::load(image_oci_config_path.to_str().ok_or_else(|| {
anyhow!(
"Invalid container image OCI config path {:?}",
image_oci_config_path
)
})?)
.context("load image bundle")?;
if let Some(container_root) = container_oci.root.as_mut() {
if let Some(image_root) = image_oci.root.as_ref() {
let root_path = Path::new(CONTAINER_BASE)
.join(container_id)
.join(image_root.path.clone());
container_root.path =
String::from(root_path.to_str().ok_or_else(|| {
anyhow!("Invalid container image root path {:?}", root_path)
})?);
}
}
if let Some(container_process) = container_oci.process.as_mut() {
if let Some(image_process) = image_oci.process.as_ref() {
merge_oci_process(container_process, image_process);
}
}
}
}
Ok(())
}
} }
#[async_trait] #[async_trait]
@ -1620,7 +1686,7 @@ fn setup_bundle(cid: &str, spec: &mut Spec) -> Result<PathBuf> {
let spec_root = spec.root.as_ref().unwrap(); let spec_root = spec.root.as_ref().unwrap();
let bundle_path = Path::new(CONTAINER_BASE).join(cid); let bundle_path = Path::new(CONTAINER_BASE).join(cid);
let config_path = bundle_path.join("config.json"); let config_path = bundle_path.join(CONFIG_JSON);
let rootfs_path = bundle_path.join("rootfs"); let rootfs_path = bundle_path.join("rootfs");
let rootfs_exists = Path::new(&rootfs_path).exists(); let rootfs_exists = Path::new(&rootfs_path).exists();