mirror of
https://github.com/aljazceru/kata-containers.git
synced 2026-02-23 15:34:28 +01:00
Merge pull request #7676 from ChengyuZhu6/pull_image_in_guest
CC | image pulling in the guest without forked containerd
This commit is contained in:
@@ -81,14 +81,14 @@ impl ImageService {
|
||||
}
|
||||
|
||||
// pause image is packaged in rootfs for CC
|
||||
fn unpack_pause_image(cid: &str) -> Result<()> {
|
||||
fn unpack_pause_image(cid: &str, target_subpath: &str) -> Result<String> {
|
||||
let cc_pause_bundle = Path::new(KATA_CC_PAUSE_BUNDLE);
|
||||
if !cc_pause_bundle.exists() {
|
||||
return Err(anyhow!("Pause image not present in rootfs"));
|
||||
}
|
||||
|
||||
info!(sl(), "use guest pause image cid {:?}", cid);
|
||||
let pause_bundle = Path::new(CONTAINER_BASE).join(cid);
|
||||
let pause_bundle = Path::new(CONTAINER_BASE).join(cid).join(target_subpath);
|
||||
let pause_rootfs = pause_bundle.join("rootfs");
|
||||
let pause_config = pause_bundle.join(CONFIG_JSON);
|
||||
let pause_binary = pause_rootfs.join("pause");
|
||||
@@ -103,7 +103,7 @@ impl ImageService {
|
||||
fs::copy(cc_pause_bundle.join("rootfs").join("pause"), pause_binary)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(pause_rootfs.display().to_string())
|
||||
}
|
||||
|
||||
/// Determines the container id (cid) to use for a given request.
|
||||
@@ -127,25 +127,20 @@ impl ImageService {
|
||||
Ok(cid)
|
||||
}
|
||||
|
||||
async fn pull_image(&self, req: &image::PullImageRequest) -> Result<String> {
|
||||
/// Set proxy environment from AGENT_CONFIG
|
||||
fn set_proxy_env_vars() {
|
||||
let https_proxy = &AGENT_CONFIG.https_proxy;
|
||||
if !https_proxy.is_empty() {
|
||||
env::set_var("HTTPS_PROXY", https_proxy);
|
||||
}
|
||||
|
||||
let no_proxy = &AGENT_CONFIG.no_proxy;
|
||||
if !no_proxy.is_empty() {
|
||||
env::set_var("NO_PROXY", no_proxy);
|
||||
}
|
||||
}
|
||||
|
||||
let cid = self.cid_from_request(req)?;
|
||||
let image = req.image();
|
||||
if cid.starts_with("pause") {
|
||||
Self::unpack_pause_image(&cid)?;
|
||||
self.add_image(String::from(image), cid).await;
|
||||
return Ok(image.to_owned());
|
||||
}
|
||||
|
||||
/// init atestation agent and read config from AGENT_CONFIG
|
||||
async fn get_security_config(&self) -> Result<String> {
|
||||
let aa_kbc_params = &AGENT_CONFIG.aa_kbc_params;
|
||||
// If the attestation-agent is being used, then enable the authenticated credentials support
|
||||
info!(
|
||||
@@ -163,22 +158,24 @@ impl ImageService {
|
||||
"enable_signature_verification set to: {}", enable_signature_verification
|
||||
);
|
||||
self.image_client.lock().await.config.security_validate = *enable_signature_verification;
|
||||
Ok(decrypt_config)
|
||||
}
|
||||
|
||||
let source_creds = (!req.source_creds().is_empty()).then(|| req.source_creds());
|
||||
|
||||
let bundle_path = Path::new(CONTAINER_BASE).join(&cid);
|
||||
fs::create_dir_all(&bundle_path)?;
|
||||
|
||||
info!(sl(), "pull image {:?}, bundle path {:?}", cid, bundle_path);
|
||||
// Image layers will store at KATA_CC_IMAGE_WORK_DIR, generated bundles
|
||||
// with rootfs and config.json will store under CONTAINER_BASE/cid.
|
||||
/// Call image-rs to pull and unpack image.
|
||||
async fn common_image_pull(
|
||||
&self,
|
||||
image: &str,
|
||||
bundle_path: &Path,
|
||||
decrypt_config: &str,
|
||||
source_creds: Option<&str>,
|
||||
cid: &str,
|
||||
) -> Result<()> {
|
||||
let res = self
|
||||
.image_client
|
||||
.lock()
|
||||
.await
|
||||
.pull_image(image, &bundle_path, &source_creds, &Some(&decrypt_config))
|
||||
.pull_image(image, bundle_path, &source_creds, &Some(decrypt_config))
|
||||
.await;
|
||||
|
||||
match res {
|
||||
Ok(image) => {
|
||||
info!(
|
||||
@@ -197,8 +194,64 @@ impl ImageService {
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
self.add_image(String::from(image), String::from(cid)).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
self.add_image(String::from(image), cid).await;
|
||||
/// Pull image when creating container and return the bundle path with rootfs.
|
||||
pub async fn pull_image_for_container(
|
||||
&self,
|
||||
image: &str,
|
||||
cid: &str,
|
||||
image_metadata: &HashMap<String, String>,
|
||||
) -> Result<String> {
|
||||
info!(sl(), "image metadata: {:?}", image_metadata);
|
||||
Self::set_proxy_env_vars();
|
||||
if image_metadata["io.kubernetes.cri.container-type"] == "sandbox" {
|
||||
let mount_path = Self::unpack_pause_image(cid, "pause")?;
|
||||
self.add_image(String::from(image), String::from(cid)).await;
|
||||
return Ok(mount_path);
|
||||
}
|
||||
let bundle_path = Path::new(CONTAINER_BASE).join(cid).join("images");
|
||||
fs::create_dir_all(&bundle_path)?;
|
||||
info!(sl(), "pull image {:?}, bundle path {:?}", cid, bundle_path);
|
||||
|
||||
let decrypt_config = self.get_security_config().await?;
|
||||
|
||||
let source_creds = None; // You need to determine how to obtain this.
|
||||
|
||||
self.common_image_pull(image, &bundle_path, &decrypt_config, source_creds, cid)
|
||||
.await?;
|
||||
Ok(format! {"{}/rootfs",bundle_path.display()})
|
||||
}
|
||||
|
||||
/// Pull image when recieving the PullImageRequest and return the image digest.
|
||||
async fn pull_image(&self, req: &image::PullImageRequest) -> Result<String> {
|
||||
Self::set_proxy_env_vars();
|
||||
let cid = self.cid_from_request(req)?;
|
||||
let image = req.image();
|
||||
if cid.starts_with("pause") {
|
||||
Self::unpack_pause_image(&cid, "")?;
|
||||
self.add_image(String::from(image), cid).await;
|
||||
return Ok(image.to_owned());
|
||||
}
|
||||
|
||||
// Image layers will store at KATA_CC_IMAGE_WORK_DIR, generated bundles
|
||||
// with rootfs and config.json will store under CONTAINER_BASE/cid.
|
||||
let bundle_path = Path::new(CONTAINER_BASE).join(&cid);
|
||||
fs::create_dir_all(&bundle_path)?;
|
||||
|
||||
let decrypt_config = self.get_security_config().await?;
|
||||
let source_creds = (!req.source_creds().is_empty()).then(|| req.source_creds());
|
||||
|
||||
self.common_image_pull(
|
||||
image,
|
||||
&bundle_path,
|
||||
&decrypt_config,
|
||||
source_creds,
|
||||
cid.clone().as_str(),
|
||||
)
|
||||
.await?;
|
||||
Ok(image.to_owned())
|
||||
}
|
||||
|
||||
|
||||
102
src/agent/src/storage/image_pull_handler.rs
Normal file
102
src/agent/src/storage/image_pull_handler.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright (c) 2023 Intel Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use kata_types::mount::KATA_VIRTUAL_VOLUME_IMAGE_GUEST_PULL;
|
||||
use kata_types::mount::{ImagePullVolume, StorageDevice};
|
||||
use protocols::agent::Storage;
|
||||
use std::sync::Arc;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::image_rpc;
|
||||
use crate::storage::{StorageContext, StorageHandler};
|
||||
|
||||
use super::{common_storage_handler, new_device};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ImagePullHandler {}
|
||||
|
||||
impl ImagePullHandler {
|
||||
fn get_image_info(storage: &Storage) -> Result<ImagePullVolume> {
|
||||
for option in storage.driver_options.iter() {
|
||||
if let Some((key, value)) = option.split_once('=') {
|
||||
if key == KATA_VIRTUAL_VOLUME_IMAGE_GUEST_PULL {
|
||||
let imagepull_volume: ImagePullVolume = serde_json::from_str(value)?;
|
||||
return Ok(imagepull_volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(anyhow!("missing Image information for ImagePull volume"))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl StorageHandler for ImagePullHandler {
|
||||
#[instrument]
|
||||
async fn create_device(
|
||||
&self,
|
||||
mut storage: Storage,
|
||||
ctx: &mut StorageContext,
|
||||
) -> Result<Arc<dyn StorageDevice>> {
|
||||
//Currently the image metadata is not used to pulling image in the guest.
|
||||
let image_pull_volume = Self::get_image_info(&storage)?;
|
||||
debug!(ctx.logger, "image_pull_volume = {:?}", image_pull_volume);
|
||||
let image_name = storage.source();
|
||||
debug!(ctx.logger, "image_name = {:?}", image_name);
|
||||
|
||||
let cid = ctx
|
||||
.cid
|
||||
.clone()
|
||||
.ok_or_else(|| anyhow!("failed to get container id"))?;
|
||||
let image_service = image_rpc::ImageService::singleton().await?;
|
||||
let bundle_path = image_service
|
||||
.pull_image_for_container(image_name, &cid, &image_pull_volume.metadata)
|
||||
.await?;
|
||||
|
||||
storage.source = bundle_path;
|
||||
storage.options = vec!["bind".to_string(), "ro".to_string()];
|
||||
|
||||
common_storage_handler(ctx.logger, &storage)?;
|
||||
|
||||
new_device(storage.mount_point)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use kata_types::mount::{ImagePullVolume, KATA_VIRTUAL_VOLUME_IMAGE_GUEST_PULL};
|
||||
use protocols::agent::Storage;
|
||||
|
||||
use crate::storage::image_pull_handler::ImagePullHandler;
|
||||
|
||||
#[test]
|
||||
fn test_get_image_info() {
|
||||
let mut res = HashMap::new();
|
||||
res.insert("key1".to_string(), "value1".to_string());
|
||||
res.insert("key2".to_string(), "value2".to_string());
|
||||
|
||||
let image_pull = ImagePullVolume {
|
||||
metadata: res.clone(),
|
||||
};
|
||||
|
||||
let image_pull_str = serde_json::to_string(&image_pull);
|
||||
assert!(image_pull_str.is_ok());
|
||||
|
||||
let storage = Storage {
|
||||
driver: KATA_VIRTUAL_VOLUME_IMAGE_GUEST_PULL.to_string(),
|
||||
driver_options: vec![format!("image_guest_pull={}", image_pull_str.ok().unwrap())],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
match ImagePullHandler::get_image_info(&storage) {
|
||||
Ok(image_info) => {
|
||||
assert_eq!(image_info.metadata, res);
|
||||
}
|
||||
Err(e) => panic!("err = {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,10 @@ use std::sync::Arc;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use kata_sys_util::mount::{create_mount_destination, parse_mount_options};
|
||||
use kata_types::mount::{StorageDevice, StorageHandlerManager, KATA_SHAREDFS_GUEST_PREMOUNT_TAG};
|
||||
use kata_types::mount::{
|
||||
StorageDevice, StorageHandlerManager, KATA_SHAREDFS_GUEST_PREMOUNT_TAG,
|
||||
KATA_VIRTUAL_VOLUME_IMAGE_GUEST_PULL,
|
||||
};
|
||||
use kata_types::volume::KATA_VOLUME_TYPE_DMVERITY;
|
||||
use nix::unistd::{Gid, Uid};
|
||||
use protocols::agent::Storage;
|
||||
@@ -26,7 +29,9 @@ use self::block_handler::{PmemHandler, ScsiHandler, VirtioBlkMmioHandler, Virtio
|
||||
use self::dm_verity::DmVerityHandler;
|
||||
use self::ephemeral_handler::EphemeralHandler;
|
||||
use self::fs_handler::{OverlayfsHandler, Virtio9pHandler, VirtioFsHandler};
|
||||
use self::image_pull_handler::ImagePullHandler;
|
||||
use self::local_handler::LocalHandler;
|
||||
|
||||
use crate::device::{
|
||||
DRIVER_9P_TYPE, DRIVER_BLK_MMIO_TYPE, DRIVER_BLK_PCI_TYPE, DRIVER_EPHEMERAL_TYPE,
|
||||
DRIVER_LOCAL_TYPE, DRIVER_NVDIMM_TYPE, DRIVER_OVERLAYFS_TYPE, DRIVER_SCSI_TYPE,
|
||||
@@ -42,6 +47,7 @@ mod block_handler;
|
||||
mod dm_verity;
|
||||
mod ephemeral_handler;
|
||||
mod fs_handler;
|
||||
mod image_pull_handler;
|
||||
mod local_handler;
|
||||
|
||||
const RW_MASK: u32 = 0o660;
|
||||
@@ -149,6 +155,7 @@ lazy_static! {
|
||||
manager.add_handler(DRIVER_VIRTIOFS_TYPE, Arc::new(VirtioFsHandler{})).unwrap();
|
||||
manager.add_handler(DRIVER_WATCHABLE_BIND_TYPE, Arc::new(BindWatcherHandler{})).unwrap();
|
||||
manager.add_handler(KATA_VOLUME_TYPE_DMVERITY, Arc::new(DmVerityHandler{})).unwrap();
|
||||
manager.add_handler(KATA_VIRTUAL_VOLUME_IMAGE_GUEST_PULL, Arc::new(ImagePullHandler{})).unwrap();
|
||||
manager
|
||||
};
|
||||
}
|
||||
|
||||
@@ -821,9 +821,6 @@ func (c *Container) createVirtualVolumeDevices() ([]config.DeviceInfo, error) {
|
||||
return nil, err
|
||||
}
|
||||
deviceInfos = append(deviceInfos, *di)
|
||||
} else if virtVolume.VolumeType == types.KataVirtualVolumeImageGuestPullType {
|
||||
///TODO implement the logic with pulling image in the guest.
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,6 +472,11 @@ func handleVirtualVolume(c *Container) ([]*grpc.Storage, string, error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
} else if virtVolume.VolumeType == types.KataVirtualVolumeImageGuestPullType {
|
||||
vol, err = handleVirtualVolumeStorageObject(c, "", virtVolume)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
if vol != nil {
|
||||
volumes = append(volumes, vol)
|
||||
@@ -521,7 +526,7 @@ func (f *FilesystemShare) ShareRootFilesystem(ctx context.Context, c *Container)
|
||||
|
||||
// In the confidential computing, there is no Image information on the host,
|
||||
// so there is no Rootfs.Target.
|
||||
if f.sandbox.config.ServiceOffload && c.rootFs.Target == "" {
|
||||
if f.sandbox.config.ServiceOffload && c.rootFs.Target == "" && !HasOptionPrefix(c.rootFs.Options, VirtualVolumePrefix) {
|
||||
return &SharedFile{
|
||||
containerStorages: nil,
|
||||
guestPath: rootfsGuestPath,
|
||||
|
||||
@@ -1575,6 +1575,34 @@ func handleDmVerityBlockVolume(driverType, source string, verityInfo *types.DmVe
|
||||
return vol, nil
|
||||
}
|
||||
|
||||
func handleImageGuestPullBlockVolume(c *Container, virtualVolumeInfo *types.KataVirtualVolume, vol *grpc.Storage) (*grpc.Storage, error) {
|
||||
container_annotations := c.GetAnnotations()
|
||||
container_type := container_annotations["io.kubernetes.cri.container-type"]
|
||||
if virtualVolumeInfo.Source == "" {
|
||||
var image_ref string
|
||||
if container_type == "sandbox" {
|
||||
image_ref = "pause"
|
||||
} else {
|
||||
image_ref = container_annotations["io.kubernetes.cri.image-name"]
|
||||
if image_ref == "" {
|
||||
return nil, fmt.Errorf("Failed to get image name from annotations")
|
||||
}
|
||||
}
|
||||
virtualVolumeInfo.Source = image_ref
|
||||
virtualVolumeInfo.ImagePull.Metadata = container_annotations
|
||||
}
|
||||
|
||||
no, err := json.Marshal(virtualVolumeInfo.ImagePull)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
vol.Driver = types.KataVirtualVolumeImageGuestPullType
|
||||
vol.DriverOptions = append(vol.DriverOptions, types.KataVirtualVolumeImageGuestPullType+"="+string(no))
|
||||
vol.Source = virtualVolumeInfo.Source
|
||||
vol.Fstype = typeOverlayFS
|
||||
return vol, nil
|
||||
}
|
||||
|
||||
func handleBlockVolume(c *Container, device api.Device) (*grpc.Storage, error) {
|
||||
vol := &grpc.Storage{}
|
||||
|
||||
@@ -1631,8 +1659,13 @@ func handleVirtualVolumeStorageObject(c *Container, blockDeviceId string, virtVo
|
||||
}
|
||||
}
|
||||
} else if virtVolume.VolumeType == types.KataVirtualVolumeImageGuestPullType {
|
||||
///TODO implement the logic with pulling image in the guest.
|
||||
return nil, nil
|
||||
var err error
|
||||
vol = &grpc.Storage{}
|
||||
vol, err = handleImageGuestPullBlockVolume(c, virtVolume, vol)
|
||||
vol.MountPoint = filepath.Join("/run/kata-containers/", c.id, c.rootfsSuffix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return vol, nil
|
||||
|
||||
Reference in New Issue
Block a user