From ab6b1cbfe94667b54d14f4f72fbd9fa7b1538b2a Mon Sep 17 00:00:00 2001 From: wllenyj Date: Wed, 10 Nov 2021 15:56:29 +0800 Subject: [PATCH] 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 Signed-off-by: Samuel Ortiz --- src/agent/src/rpc.rs | 68 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/src/agent/src/rpc.rs b/src/agent/src/rpc.rs index 5cbbaa0c98..974fafc25c 100644 --- a/src/agent/src/rpc.rs +++ b/src/agent/src/rpc.rs @@ -79,6 +79,8 @@ use std::path::PathBuf; pub const CONTAINER_BASE: &str = "/run/kata-containers"; 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 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 { #[instrument] async fn do_create_container( @@ -150,6 +165,9 @@ impl AgentService { 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 // --device for instance), and that's what this call is doing. It // 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] @@ -1620,7 +1686,7 @@ fn setup_bundle(cid: &str, spec: &mut Spec) -> Result { let spec_root = spec.root.as_ref().unwrap(); 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_exists = Path::new(&rootfs_path).exists();