Compare commits

..

14 Commits

Author SHA1 Message Date
Aurélien Bombo
b87b4dc3be relax bind mount regex
the source path can be cached from the first container now

Signed-off-by: Aurélien Bombo <abombo@microsoft.com>
2026-02-13 12:42:49 -06:00
Aurélien Bombo
11dfe0ffac allow cached bundle-id from pause container
Signed-off-by: Aurélien Bombo <abombo@microsoft.com>
2026-02-13 12:42:49 -06:00
Aurélien Bombo
701e67cfd6 move cache handling to shared_fs=none branch
this should only be needed in that branch since virtio-fs should already handle dupes

Signed-off-by: Aurélien Bombo <abombo@microsoft.com>
2026-02-13 12:42:49 -06:00
Aurélien Bombo
d50f103a13 Revert "debug: fix cache key"
This reverts commit 2c3ee1eda5.
2026-02-13 12:42:49 -06:00
Aurélien Bombo
bd2428e19f Revert "debug: different approach"
This reverts commit c0d3c31ec8.
2026-02-13 12:42:49 -06:00
Aurélien Bombo
13b8dda322 debug: different approach
Signed-off-by: Aurélien Bombo <abombo@microsoft.com>
2026-02-13 12:42:49 -06:00
Aurélien Bombo
36ca7990aa tests: Introduce new env variables to ease development
It can be useful to set these variables during local testing:

 * AZ_REGION: Region for the cluster.
 * AZ_NODEPOOL_TAGS: Node pool tags for the cluster.
 * GENPOLICY_BINARY: Path to the genpolicy binary.
 * GENPOLICY_SETTINGS_DIR: Directory holding the genpolicy settings.

I've also made it so that tests_common.sh modifies the duplicated
genpolicy-settings.json (used for testing) instead of the original git-tracked
one.

Signed-off-by: Aurélien Bombo <abombo@microsoft.com>
2026-02-13 12:42:49 -06:00
Aurélien Bombo
9894e14e99 debug: fix cache key
Signed-off-by: Aurélien Bombo <abombo@microsoft.com>
2026-02-13 12:42:49 -06:00
Aurélien Bombo
7357373dff debug: properly invalidate cache
Signed-off-by: Aurélien Bombo <abombo@microsoft.com>
2026-02-13 12:42:49 -06:00
Aurélien Bombo
56254ecdff debug: smaller mutex critical sections
Signed-off-by: Aurélien Bombo <abombo@microsoft.com>
2026-02-13 12:42:49 -06:00
Aurélien Bombo
be8a112316 debug: enable disable_guest_empty_dir=true and shared_fs=none
Signed-off-by: Aurélien Bombo <abombo@microsoft.com>
2026-02-13 12:42:49 -06:00
Aurélien Bombo
ed415fa91a runtime-rs: Set disable_guest_empty_dir = true by default
This should be furthermore not be configurable in 4.0.

Signed-off-by: Aurélien Bombo <abombo@microsoft.com>
2026-02-13 12:42:49 -06:00
Aurélien Bombo
4a37f4c673 genpolicy: Assume disable_guest_empty_dir = true
This option should be removed for 4.0, so we don't handle `false`.

Signed-off-by: Aurélien Bombo <abombo@microsoft.com>
2026-02-13 12:42:49 -06:00
Aurélien Bombo
0db136cfa9 runtime: Set disable_guest_empty_dir = true by default
This makes the runtime share the host Kubelet emptyDir folder with the guest
instead of the agent creating an empty folder in the container rootfs. Doing so
enables the Kubelet to track emptyDir usage and evict greedy pods.

In other words, with virtio-fs the container rootfs uses host storage whether
this is true or false, however with true, Kata uses the k8s emptyDir folder so
the sizeLimit is properly enforced by k8s.

Addresses part of #12203.

Signed-off-by: Aurélien Bombo <abombo@microsoft.com>
2026-02-13 12:42:49 -06:00
53 changed files with 527 additions and 843 deletions

8
Cargo.lock generated
View File

@@ -3945,9 +3945,9 @@ dependencies = [
[[package]]
name = "rkyv"
version = "0.7.46"
version = "0.7.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1"
checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b"
dependencies = [
"bitvec",
"bytecheck",
@@ -3963,9 +3963,9 @@ dependencies = [
[[package]]
name = "rkyv_derive"
version = "0.7.46"
version = "0.7.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5"
checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0"
dependencies = [
"proc-macro2",
"quote",

8
src/agent/Cargo.lock generated
View File

@@ -3488,9 +3488,9 @@ dependencies = [
[[package]]
name = "rkyv"
version = "0.7.46"
version = "0.7.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1"
checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b"
dependencies = [
"bitvec",
"bytecheck",
@@ -3506,9 +3506,9 @@ dependencies = [
[[package]]
name = "rkyv_derive"
version = "0.7.46"
version = "0.7.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5"
checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -857,7 +857,7 @@ fn mount_from(
dest.as_str(),
Some(mount_typ.as_str()),
flags,
Some(d.as_str()).filter(|s| !s.is_empty()),
Some(d.as_str()),
)
.inspect_err(|e| log_child!(cfd_log, "mount error: {:?}", e))?;

View File

@@ -180,7 +180,7 @@ DEFNETQUEUES := 1
DEFENABLEANNOTATIONS := [\"enable_iommu\", \"virtio_fs_extra_args\", \"kernel_params\", \"kernel_verity_params\", \"default_vcpus\", \"default_memory\"]
DEFENABLEANNOTATIONS_COCO := [\"enable_iommu\", \"virtio_fs_extra_args\", \"kernel_params\", \"kernel_verity_params\", \"default_vcpus\", \"default_memory\", \"cc_init_data\"]
DEFDISABLEGUESTSECCOMP := true
DEFDISABLEGUESTEMPTYDIR := false
DEFDISABLEGUESTEMPTYDIR := true
##VAR DEFAULTEXPFEATURES=[features] Default experimental features enabled
DEFAULTEXPFEATURES := []
DEFDISABLESELINUX := false
@@ -298,7 +298,7 @@ ifneq (,$(CLHCMD))
KERNELTYPE_CLH = uncompressed
KERNEL_NAME_CLH = $(call MAKE_KERNEL_NAME,$(KERNELTYPE_CLH))
KERNELPATH_CLH = $(KERNELDIR)/$(KERNEL_NAME_CLH)
VMROOTFSDRIVER_CLH := virtio-blk-pci
VMROOTFSDRIVER_CLH := virtio-pmem
DEFSANDBOXCGROUPONLY_CLH := true
DEFSTATICRESOURCEMGMT_CLH := false

View File

@@ -22,8 +22,6 @@ rootfs_type = @DEFROOTFSTYPE@
# Block storage driver to be used for the VM rootfs is backed
# by a block device.
#
# virtio-pmem is not supported with Cloud Hypervisor.
vm_rootfs_driver = "@VMROOTFSDRIVER_CLH@"
# Path to the firmware.

View File

@@ -118,11 +118,13 @@ impl TryFrom<NamedHypervisorConfig> for VmConfig {
// Note how CH handles the different image types:
//
// - A standard image is specified in PmemConfig.
// - An initrd/initramfs is specified in PayloadConfig.
// - An image is specified in DiskConfig.
// Note: pmem is not used as it's not properly supported by Cloud Hypervisor.
// - A confidential guest image is specified by a DiskConfig.
// - If TDX is enabled, the firmware (`td-shim` [1]) must be
// specified in PayloadConfig.
// - A confidential guest initrd is specified by a PayloadConfig with
// firmware.
//
// [1] - https://github.com/confidential-containers/td-shim
let boot_info = cfg.boot_info;
@@ -138,6 +140,14 @@ impl TryFrom<NamedHypervisorConfig> for VmConfig {
return Err(VmConfigError::NoBootFile);
}
let pmem = if use_initrd || guest_protection_is_tdx(guest_protection_to_use.clone()) {
None
} else {
let pmem = PmemConfig::try_from(&boot_info).map_err(VmConfigError::PmemError)?;
Some(vec![pmem])
};
let payload = Some(
PayloadConfig::try_from((
boot_info.clone(),
@@ -149,7 +159,7 @@ impl TryFrom<NamedHypervisorConfig> for VmConfig {
let mut disks: Vec<DiskConfig> = vec![];
if use_image {
if use_image && guest_protection_is_tdx(guest_protection_to_use.clone()) {
let disk = DiskConfig::try_from(boot_info).map_err(VmConfigError::DiskError)?;
disks.push(disk);
@@ -189,6 +199,7 @@ impl TryFrom<NamedHypervisorConfig> for VmConfig {
fs,
net,
devices: host_devices,
pmem,
disks,
vsock: Some(vsock),
rng,
@@ -1645,6 +1656,7 @@ mod tests {
let (memory_info_confidential_guest, mem_config_confidential_guest) =
make_memory_objects(79, usable_max_mem_bytes, true);
let (_, pmem_config_with_image) = make_bootinfo_pmemconfig_objects(image);
let (machine_info, rng_config) = make_machineinfo_rngconfig_objects(entropy_source);
let payload_firmware = None;
@@ -1652,7 +1664,6 @@ mod tests {
let (boot_info_with_initrd, payload_config_with_initrd) =
make_bootinfo_payloadconfig_objects(kernel, initramfs, payload_firmware, None);
let (_, disk_config_with_image) = make_bootinfo_diskconfig_objects(image);
let (_, disk_config_confidential_guest_image) = make_bootinfo_diskconfig_objects(image);
let boot_info_tdx_image = BootInfo {
@@ -1751,7 +1762,7 @@ mod tests {
vsock: Some(valid_vsock.clone()),
// rootfs image specific
disks: Some(vec![disk_config_with_image]),
pmem: Some(vec![pmem_config_with_image]),
payload: Some(PayloadConfig {
kernel: Some(PathBuf::from(kernel)),

View File

@@ -123,12 +123,7 @@ impl CloudHypervisorInner {
}
}
pub fn set_hypervisor_config(&mut self, mut config: HypervisorConfig) {
// virtio-pmem is not supported for Cloud Hypervisor.
if config.boot_info.vm_rootfs_driver == crate::VM_ROOTFS_DRIVER_PMEM {
config.boot_info.vm_rootfs_driver = crate::VM_ROOTFS_DRIVER_BLK.to_string();
}
pub fn set_hypervisor_config(&mut self, config: HypervisorConfig) {
self.config = config;
}

View File

@@ -15,6 +15,7 @@ use crate::utils::vm_cleanup;
use crate::utils::{bytes_to_megs, get_jailer_root, get_sandbox_path, megs_to_bytes};
use crate::MemoryConfig;
use crate::VM_ROOTFS_DRIVER_BLK;
use crate::VM_ROOTFS_DRIVER_PMEM;
use crate::{VcpuThreadIds, VmmState};
use anyhow::{anyhow, Context, Result};
use ch_config::ch_api::cloud_hypervisor_vm_netdev_add_with_fds;
@@ -129,8 +130,12 @@ impl CloudHypervisorInner {
let confidential_guest = cfg.security_info.confidential_guest;
// Note that the configuration option hypervisor.block_device_driver is not used.
// NVDIMM is not supported for Cloud Hypervisor.
let rootfs_driver = VM_ROOTFS_DRIVER_BLK;
let rootfs_driver = if confidential_guest {
// PMEM is not available with TDX.
VM_ROOTFS_DRIVER_BLK
} else {
VM_ROOTFS_DRIVER_PMEM
};
let rootfs_type = match cfg.boot_info.rootfs_type.is_empty() {
true => DEFAULT_CH_ROOTFS_TYPE,
@@ -150,7 +155,6 @@ impl CloudHypervisorInner {
&cfg.boot_info.kernel_verity_params,
rootfs_driver,
rootfs_type,
true,
)?;
let mut console_params = if enable_debug {

View File

@@ -150,7 +150,6 @@ impl DragonballInner {
&self.config.boot_info.kernel_verity_params,
&rootfs_driver,
&self.config.boot_info.rootfs_type,
true,
)?;
kernel_params.append(&mut rootfs_params);
}

View File

@@ -90,7 +90,6 @@ impl FcInner {
&self.config.boot_info.kernel_verity_params,
&self.config.blockdev_info.block_device_driver,
&self.config.boot_info.rootfs_type,
true,
)?;
kernel_params.append(&mut rootfs_params);
kernel_params.append(&mut KernelParams::from_string(

View File

@@ -10,8 +10,8 @@ use crate::{
VM_ROOTFS_DRIVER_BLK, VM_ROOTFS_DRIVER_BLK_CCW, VM_ROOTFS_DRIVER_MMIO, VM_ROOTFS_DRIVER_PMEM,
VM_ROOTFS_ROOT_BLK, VM_ROOTFS_ROOT_PMEM,
};
use kata_types::config::hypervisor::{parse_kernel_verity_params, VERITY_BLOCK_SIZE_BYTES};
use kata_types::config::LOG_VPORT_OPTION;
use kata_types::config::hypervisor::{parse_kernel_verity_params, VERITY_BLOCK_SIZE_BYTES};
use kata_types::fs::{
VM_ROOTFS_FILESYSTEM_EROFS, VM_ROOTFS_FILESYSTEM_EXT4, VM_ROOTFS_FILESYSTEM_XFS,
};
@@ -66,7 +66,8 @@ struct KernelVerityConfig {
}
fn new_kernel_verity_params(params_string: &str) -> Result<Option<KernelVerityConfig>> {
let cfg = parse_kernel_verity_params(params_string).map_err(|err| anyhow!(err.to_string()))?;
let cfg = parse_kernel_verity_params(params_string)
.map_err(|err| anyhow!(err.to_string()))?;
Ok(cfg.map(|params| KernelVerityConfig {
root_hash: params.root_hash,
@@ -144,7 +145,6 @@ impl KernelParams {
kernel_verity_params: &str,
rootfs_driver: &str,
rootfs_type: &str,
use_dax: bool,
) -> Result<Self> {
let mut params = vec![];
@@ -153,29 +153,16 @@ impl KernelParams {
params.push(Param::new("root", VM_ROOTFS_ROOT_PMEM));
match rootfs_type {
VM_ROOTFS_FILESYSTEM_EXT4 => {
if use_dax {
params.push(Param::new(
"rootflags",
"dax,data=ordered,errors=remount-ro ro",
));
} else {
params
.push(Param::new("rootflags", "data=ordered,errors=remount-ro ro"));
}
params.push(Param::new(
"rootflags",
"dax,data=ordered,errors=remount-ro ro",
));
}
VM_ROOTFS_FILESYSTEM_XFS => {
if use_dax {
params.push(Param::new("rootflags", "dax ro"));
} else {
params.push(Param::new("rootflags", "ro"));
}
params.push(Param::new("rootflags", "dax ro"));
}
VM_ROOTFS_FILESYSTEM_EROFS => {
if use_dax {
params.push(Param::new("rootflags", "dax ro"));
} else {
params.push(Param::new("rootflags", "ro"));
}
params.push(Param::new("rootflags", "dax ro"));
}
_ => {
return Err(anyhow!("Unsupported rootfs type {}", rootfs_type));
@@ -359,7 +346,6 @@ mod tests {
struct TestData<'a> {
rootfs_driver: &'a str,
rootfs_type: &'a str,
use_dax: bool,
expect_params: KernelParams,
result: Result<()>,
}
@@ -367,11 +353,10 @@ mod tests {
#[test]
fn test_rootfs_kernel_params() {
let tests = &[
// EXT4 with DAX
// EXT4
TestData {
rootfs_driver: VM_ROOTFS_DRIVER_PMEM,
rootfs_type: VM_ROOTFS_FILESYSTEM_EXT4,
use_dax: true,
expect_params: KernelParams {
params: [
Param::new("root", VM_ROOTFS_ROOT_PMEM),
@@ -385,7 +370,6 @@ mod tests {
TestData {
rootfs_driver: VM_ROOTFS_DRIVER_BLK,
rootfs_type: VM_ROOTFS_FILESYSTEM_EXT4,
use_dax: true,
expect_params: KernelParams {
params: [
Param::new("root", VM_ROOTFS_ROOT_BLK),
@@ -396,15 +380,14 @@ mod tests {
},
result: Ok(()),
},
// XFS without DAX
// XFS
TestData {
rootfs_driver: VM_ROOTFS_DRIVER_PMEM,
rootfs_type: VM_ROOTFS_FILESYSTEM_XFS,
use_dax: false,
expect_params: KernelParams {
params: [
Param::new("root", VM_ROOTFS_ROOT_PMEM),
Param::new("rootflags", "ro"),
Param::new("rootflags", "dax ro"),
Param::new("rootfstype", VM_ROOTFS_FILESYSTEM_XFS),
]
.to_vec(),
@@ -414,7 +397,6 @@ mod tests {
TestData {
rootfs_driver: VM_ROOTFS_DRIVER_BLK,
rootfs_type: VM_ROOTFS_FILESYSTEM_XFS,
use_dax: true,
expect_params: KernelParams {
params: [
Param::new("root", VM_ROOTFS_ROOT_BLK),
@@ -425,11 +407,10 @@ mod tests {
},
result: Ok(()),
},
// EROFS with DAX
// EROFS
TestData {
rootfs_driver: VM_ROOTFS_DRIVER_PMEM,
rootfs_type: VM_ROOTFS_FILESYSTEM_EROFS,
use_dax: true,
expect_params: KernelParams {
params: [
Param::new("root", VM_ROOTFS_ROOT_PMEM),
@@ -443,7 +424,6 @@ mod tests {
TestData {
rootfs_driver: VM_ROOTFS_DRIVER_BLK,
rootfs_type: VM_ROOTFS_FILESYSTEM_EROFS,
use_dax: true,
expect_params: KernelParams {
params: [
Param::new("root", VM_ROOTFS_ROOT_BLK),
@@ -458,7 +438,6 @@ mod tests {
TestData {
rootfs_driver: "foo",
rootfs_type: VM_ROOTFS_FILESYSTEM_EXT4,
use_dax: true,
expect_params: KernelParams {
params: [
Param::new("root", VM_ROOTFS_ROOT_BLK),
@@ -473,7 +452,6 @@ mod tests {
TestData {
rootfs_driver: VM_ROOTFS_DRIVER_BLK,
rootfs_type: "foo",
use_dax: true,
expect_params: KernelParams {
params: [
Param::new("root", VM_ROOTFS_ROOT_BLK),
@@ -488,12 +466,8 @@ mod tests {
for (i, t) in tests.iter().enumerate() {
let msg = format!("test[{i}]: {t:?}");
let result = KernelParams::new_rootfs_kernel_params(
"",
t.rootfs_driver,
t.rootfs_type,
t.use_dax,
);
let result =
KernelParams::new_rootfs_kernel_params("", t.rootfs_driver, t.rootfs_type);
let msg = format!("{msg}, result: {result:?}");
if t.result.is_ok() {
assert!(result.is_ok(), "{}", msg);
@@ -512,7 +486,6 @@ mod tests {
"root_hash=abc,salt=def,data_blocks=1,data_block_size=4096,hash_block_size=4096",
VM_ROOTFS_DRIVER_BLK,
VM_ROOTFS_FILESYSTEM_EXT4,
false,
)?;
let params_string = params.to_string()?;
assert!(params_string.contains("dm-mod.create="));
@@ -523,7 +496,6 @@ mod tests {
"root_hash=abc,data_blocks=1,data_block_size=4096,hash_block_size=4096",
VM_ROOTFS_DRIVER_BLK,
VM_ROOTFS_FILESYSTEM_EXT4,
false,
)
.err()
.expect("expected missing salt error");
@@ -533,7 +505,6 @@ mod tests {
"root_hash=abc,salt=def,data_block_size=4096,hash_block_size=4096",
VM_ROOTFS_DRIVER_BLK,
VM_ROOTFS_FILESYSTEM_EXT4,
false,
)
.err()
.expect("expected missing data_blocks error");
@@ -543,7 +514,6 @@ mod tests {
"root_hash=abc,salt=def,data_blocks=foo,data_block_size=4096,hash_block_size=4096",
VM_ROOTFS_DRIVER_BLK,
VM_ROOTFS_FILESYSTEM_EXT4,
false,
)
.err()
.expect("expected invalid data_blocks error");
@@ -553,7 +523,6 @@ mod tests {
"root_hash=abc,salt=def,data_blocks=1,data_block_size=4096,hash_block_size=4096,badfield",
VM_ROOTFS_DRIVER_BLK,
VM_ROOTFS_FILESYSTEM_EXT4,
false,
)
.err()
.expect("expected invalid entry error");

View File

@@ -179,17 +179,10 @@ impl Kernel {
let mut kernel_params = KernelParams::new(config.debug_info.enable_debug);
if config.boot_info.initrd.is_empty() {
// DAX is disabled on ARM due to a kernel panic in caches_clean_inval_pou.
#[cfg(target_arch = "aarch64")]
let use_dax = false;
#[cfg(not(target_arch = "aarch64"))]
let use_dax = true;
let mut rootfs_params = KernelParams::new_rootfs_kernel_params(
&config.boot_info.kernel_verity_params,
&config.boot_info.vm_rootfs_driver,
&config.boot_info.rootfs_type,
use_dax,
)
.context("adding rootfs/verity params failed")?;
kernel_params.append(&mut rootfs_params);

View File

@@ -220,7 +220,7 @@ DEFBRIDGES := 1
DEFENABLEANNOTATIONS := [\"enable_iommu\", \"virtio_fs_extra_args\", \"kernel_params\", \"kernel_verity_params\"]
DEFENABLEANNOTATIONS_COCO := [\"enable_iommu\", \"virtio_fs_extra_args\", \"kernel_params\", \"kernel_verity_params\", \"default_vcpus\", \"default_memory\", \"cc_init_data\"]
DEFDISABLEGUESTSECCOMP := true
DEFDISABLEGUESTEMPTYDIR := false
DEFDISABLEGUESTEMPTYDIR := true
#Default experimental features enabled
DEFAULTEXPFEATURES := []
@@ -288,7 +288,6 @@ DEFSTATICRESOURCEMGMT_NV = true
DEFDISABLEIMAGENVDIMM ?= false
DEFDISABLEIMAGENVDIMM_NV = true
DEFDISABLEIMAGENVDIMM_CLH ?= true
DEFBINDMOUNTS := []
@@ -789,7 +788,6 @@ USER_VARS += DEFVFIOMODE_SE
USER_VARS += BUILDFLAGS
USER_VARS += DEFDISABLEIMAGENVDIMM
USER_VARS += DEFDISABLEIMAGENVDIMM_NV
USER_VARS += DEFDISABLEIMAGENVDIMM_CLH
USER_VARS += DEFCCAMEASUREMENTALGO
USER_VARS += DEFSHAREDFS_QEMU_CCA_VIRTIOFS
USER_VARS += DEFPODRESOURCEAPISOCK

View File

@@ -348,15 +348,6 @@ func TestCheckHostIsVMContainerCapable(t *testing.T) {
defer func() {
os.Remove(denylistModuleConf)
// reload removed modules
for mod := range archRequiredKernelModules {
cmd := exec.Command(modProbeCmd, mod)
if output, err := cmd.CombinedOutput(); err == nil {
kataLog.WithField("output", string(output)).Info("module loaded")
} else {
kataLog.WithField("output", string(output)).Warn("failed to load module")
}
}
}()
// remove the modules to force a failure

View File

@@ -222,8 +222,8 @@ hypervisor_loglevel = 1
# If false and nvdimm is supported, use nvdimm device to plug guest image.
# Otherwise virtio-block device is used.
#
# nvdimm is not supported with Cloud Hypervisor or when `confidential_guest = true`.
disable_image_nvdimm = @DEFDISABLEIMAGENVDIMM_CLH@
# nvdimm is not supported when `confidential_guest = true`.
disable_image_nvdimm = @DEFDISABLEIMAGENVDIMM@
# Enable hot-plugging of VFIO devices to a root-port.
# The default setting is "no-port"

View File

@@ -8,7 +8,6 @@
package resourcecontrol
import (
"errors"
"fmt"
"os"
"path/filepath"
@@ -51,7 +50,7 @@ type LinuxCgroup struct {
sync.Mutex
}
func sandboxDevices() ([]specs.LinuxDeviceCgroup, error) {
func sandboxDevices() []specs.LinuxDeviceCgroup {
devices := []specs.LinuxDeviceCgroup{}
defaultDevices := []string{
@@ -69,33 +68,14 @@ func sandboxDevices() ([]specs.LinuxDeviceCgroup, error) {
// In order to run Virtual Machines and create virtqueues, hypervisors
// need access to certain character devices in the host, like kvm and vhost-net.
hypervisorDevices := []string{
"/dev/kvm", // To run virtual machines with KVM
"/dev/mshv", // To run virtual machines with Hyper-V
}
virtualDevices := []string{
"/dev/kvm", // To run virtual machines with KVM
"/dev/mshv", // To run virtual machines with Hyper-V
"/dev/vhost-net", // To create virtqueues
"/dev/vfio/vfio", // To access VFIO devices
"/dev/vhost-vsock", // To interact with vsock if
}
hypervisorDeviceAdded := false
for _, hypervisor := range hypervisorDevices {
hypervisorDevice, err := DeviceToLinuxDevice(hypervisor)
if err != nil {
if !os.IsNotExist(err) {
controllerLogger.WithField("source", "cgroups").Warnf("Failed to add %s to the devices cgroup: %v", hypervisor, err)
}
continue
}
devices = append(devices, hypervisorDevice)
hypervisorDeviceAdded = true
controllerLogger.WithField("source", "cgroups").Infof("Adding %s to the devices cgroup", hypervisor)
break
}
if !hypervisorDeviceAdded {
return []specs.LinuxDeviceCgroup{}, errors.New("failed to add any hypervisor device to devices cgroup")
}
defaultDevices = append(defaultDevices, virtualDevices...)
defaultDevices = append(defaultDevices, hypervisorDevices...)
for _, device := range defaultDevices {
ldevice, err := DeviceToLinuxDevice(device)
@@ -148,7 +128,7 @@ func sandboxDevices() ([]specs.LinuxDeviceCgroup, error) {
devices = append(devices, wildcardDevices...)
return devices, nil
return devices
}
func NewResourceController(path string, resources *specs.LinuxResources) (ResourceController, error) {
@@ -188,11 +168,7 @@ func NewResourceController(path string, resources *specs.LinuxResources) (Resour
func NewSandboxResourceController(path string, resources *specs.LinuxResources, sandboxCgroupOnly bool) (ResourceController, error) {
sandboxResources := *resources
sandboxDevices, err := sandboxDevices()
if err != nil {
return nil, err
}
sandboxResources.Devices = append(sandboxResources.Devices, sandboxDevices...)
sandboxResources.Devices = append(sandboxResources.Devices, sandboxDevices()...)
// Currently we know to handle systemd cgroup path only when it's the only cgroup (no overhead group), hence,
// if sandboxCgroupOnly is not true we treat it as cgroupfs path as it used to be, although it may be incorrect.

View File

@@ -131,11 +131,6 @@ func newTestSandboxConfigKataAgent() SandboxConfig {
}
func TestCreateSandboxNoopAgentSuccessful(t *testing.T) {
// GITHUB_RUNNER_CI_NON_VIRT is set to true in .github/workflows/build-checks.yaml file for ARM64 runners because the self hosted runners do not support Virtualization
if os.Getenv("GITHUB_RUNNER_CI_NON_VIRT") == "true" {
t.Skip("Skipping the test as the GitHub self hosted runners for ARM64 do not support Virtualization")
}
assert := assert.New(t)
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(testDisabledAsNonRoot)
@@ -164,11 +159,6 @@ func TestCreateSandboxNoopAgentSuccessful(t *testing.T) {
}
func TestCreateSandboxKataAgentSuccessful(t *testing.T) {
// GITHUB_RUNNER_CI_NON_VIRT is set to true in .github/workflows/build-checks.yaml file for ARM64 runners because the self hosted runners do not support Virtualization
if os.Getenv("GITHUB_RUNNER_CI_NON_VIRT") == "true" {
t.Skip("Skipping the test as the GitHub self hosted runners for ARM64 do not support Virtualization")
}
assert := assert.New(t)
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(testDisabledAsNonRoot)
@@ -262,11 +252,6 @@ func createAndStartSandbox(ctx context.Context, config SandboxConfig) (sandbox V
}
func TestReleaseSandbox(t *testing.T) {
// GITHUB_RUNNER_CI_NON_VIRT is set to true in .github/workflows/build-checks.yaml file for ARM64 runners because the self hosted runners do not support Virtualization
if os.Getenv("GITHUB_RUNNER_CI_NON_VIRT") == "true" {
t.Skip("Skipping the test as the GitHub self hosted runners for ARM64 do not support Virtualization")
}
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(testDisabledAsNonRoot)
}
@@ -284,11 +269,6 @@ func TestReleaseSandbox(t *testing.T) {
}
func TestCleanupContainer(t *testing.T) {
// GITHUB_RUNNER_CI_NON_VIRT is set to true in .github/workflows/build-checks.yaml file for ARM64 runners because the self hosted runners do not support Virtualization
if os.Getenv("GITHUB_RUNNER_CI_NON_VIRT") == "true" {
t.Skip("Skipping the test as the GitHub self hosted runners for ARM64 do not support Virtualization")
}
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(testDisabledAsNonRoot)
}

View File

@@ -332,9 +332,6 @@ func (clh *cloudHypervisor) getClhStopSandboxTimeout() time.Duration {
func (clh *cloudHypervisor) setConfig(config *HypervisorConfig) error {
clh.config = *config
// We don't support NVDIMM with Cloud Hypervisor.
clh.config.DisableImageNvdimm = true
return nil
}
@@ -587,8 +584,8 @@ func (clh *cloudHypervisor) CreateVM(ctx context.Context, id string, network Net
// Set initial amount of cpu's for the virtual machine
clh.vmconfig.Cpus = chclient.NewCpusConfig(int32(clh.config.NumVCPUs()), int32(clh.config.DefaultMaxVCPUs))
disableNvdimm := true
enableDax := false
disableNvdimm := (clh.config.DisableImageNvdimm || clh.config.ConfidentialGuest)
enableDax := !disableNvdimm
params, err := getNonUserDefinedKernelParams(hypervisorConfig.RootfsType, disableNvdimm, enableDax, clh.config.Debug, clh.config.ConfidentialGuest, clh.config.IOMMU, hypervisorConfig.KernelVerityParams)
if err != nil {
@@ -610,19 +607,31 @@ func (clh *cloudHypervisor) CreateVM(ctx context.Context, id string, network Net
}
if assetType == types.ImageAsset {
disk := chclient.NewDiskConfig()
disk.Path = &assetPath
disk.SetReadonly(true)
if clh.config.DisableImageNvdimm || clh.config.ConfidentialGuest {
disk := chclient.NewDiskConfig()
disk.Path = &assetPath
disk.SetReadonly(true)
diskRateLimiterConfig := clh.getDiskRateLimiterConfig()
if diskRateLimiterConfig != nil {
disk.SetRateLimiterConfig(*diskRateLimiterConfig)
}
diskRateLimiterConfig := clh.getDiskRateLimiterConfig()
if diskRateLimiterConfig != nil {
disk.SetRateLimiterConfig(*diskRateLimiterConfig)
}
if clh.vmconfig.Disks != nil {
*clh.vmconfig.Disks = append(*clh.vmconfig.Disks, *disk)
if clh.vmconfig.Disks != nil {
*clh.vmconfig.Disks = append(*clh.vmconfig.Disks, *disk)
} else {
clh.vmconfig.Disks = &[]chclient.DiskConfig{*disk}
}
} else {
clh.vmconfig.Disks = &[]chclient.DiskConfig{*disk}
pmem := chclient.NewPmemConfig(assetPath)
*pmem.DiscardWrites = true
pmem.SetIommu(clh.config.IOMMU)
if clh.vmconfig.Pmem != nil {
*clh.vmconfig.Pmem = append(*clh.vmconfig.Pmem, *pmem)
} else {
clh.vmconfig.Pmem = &[]chclient.PmemConfig{*pmem}
}
}
} else {
// assetType == types.InitrdAsset

View File

@@ -69,7 +69,6 @@ func newClhConfig() (HypervisorConfig, error) {
NetRateLimiterOpsMaxRate: int64(0),
NetRateLimiterOpsOneTimeBurst: int64(0),
HotPlugVFIO: config.NoPort,
DisableImageNvdimm: true,
}, nil
}

View File

@@ -83,9 +83,13 @@ type FilesystemShare struct {
configVolRegex *regexp.Regexp
// Regex to match only the timestamped directory inside the k8's volume mount
timestampDirRegex *regexp.Regexp
// The same volume mount can be shared by multiple containers in the same sandbox (pod)
srcDstMap map[string][]string
srcDstMapLock sync.Mutex
// srcDstMap tracks file-level source to destination mappings for configmap/secret watching
srcDstMap map[string][]string
srcDstMapLock sync.Mutex
// srcGuestMap caches volume source path to guest path, enabling multiple containers
// in the same pod to share the same volume mount
srcGuestMap map[string]string
srcGuestMapLock sync.Mutex
eventLoopStarted bool
eventLoopStartedLock sync.Mutex
watcherDoneChannel chan bool
@@ -108,6 +112,7 @@ func NewFilesystemShare(s *Sandbox) (*FilesystemShare, error) {
sandbox: s,
watcherDoneChannel: make(chan bool),
srcDstMap: make(map[string][]string),
srcGuestMap: make(map[string]string),
watcher: watcher,
configVolRegex: configVolRegex,
timestampDirRegex: timestampDirRegex,
@@ -309,6 +314,13 @@ func (f *FilesystemShare) ShareFile(ctx context.Context, c *Container, m *Mount)
// bind mount it in the shared directory.
caps := f.sandbox.hypervisor.Capabilities(ctx)
if !caps.IsFsSharingSupported() {
f.srcGuestMapLock.Lock()
if guestPath, ok := f.srcGuestMap[m.Source]; ok {
f.srcGuestMapLock.Unlock()
return &SharedFile{guestPath: guestPath}, nil
}
f.srcGuestMapLock.Unlock()
f.Logger().Debug("filesystem sharing is not supported, files will be copied")
var ignored bool
@@ -418,6 +430,11 @@ func (f *FilesystemShare) ShareFile(ctx context.Context, c *Container, m *Mount)
m.HostPath = mountDest
}
// Cache the guestPath for this volume source so other containers can share it
f.srcGuestMapLock.Lock()
defer f.srcGuestMapLock.Unlock()
f.srcGuestMap[m.Source] = guestPath
return &SharedFile{
guestPath: guestPath,
}, nil
@@ -442,6 +459,10 @@ func (f *FilesystemShare) UnshareFile(ctx context.Context, c *Container, m *Moun
}
}
f.srcGuestMapLock.Lock()
delete(f.srcGuestMap, m.Source)
f.srcGuestMapLock.Unlock()
return nil
}

View File

@@ -1098,10 +1098,8 @@ func (q *qemu) LogAndWait(qemuCmd *exec.Cmd, reader io.ReadCloser) {
q.Logger().WithField("qemuPid", pid).Error(text)
}
}
q.Logger().WithField("qemuPid", pid).Infof("Stop logging QEMU")
if err := qemuCmd.Wait(); err != nil {
q.Logger().WithField("qemuPid", pid).WithField("error", err).Warn("QEMU exited with an error")
}
q.Logger().Infof("Stop logging QEMU (qemuPid=%d)", pid)
qemuCmd.Wait()
}
// StartVM will start the Sandbox's VM.

View File

@@ -332,38 +332,6 @@ func TestQemuArchBaseAppendImage(t *testing.T) {
assert.Equal(expectedOut, devices)
}
func TestQemuArchBaseAppendNvdimmImage(t *testing.T) {
var devices []govmmQemu.Device
assert := assert.New(t)
qemuArchBase := newQemuArchBase()
image, err := os.CreateTemp("", "img")
assert.NoError(err)
defer image.Close()
defer os.Remove(image.Name())
imageStat, err := image.Stat()
assert.NoError(err)
devices, err = qemuArchBase.appendNvdimmImage(devices, image.Name())
assert.NoError(err)
assert.Len(devices, 1)
expectedOut := []govmmQemu.Device{
govmmQemu.Object{
Driver: govmmQemu.NVDIMM,
Type: govmmQemu.MemoryBackendFile,
DeviceID: "nv0",
ID: "mem0",
MemPath: image.Name(),
Size: (uint64)(imageStat.Size()),
ReadOnly: true,
},
}
assert.Equal(expectedOut, devices)
}
func TestQemuArchBaseAppendBridges(t *testing.T) {
var devices []govmmQemu.Device
assert := assert.New(t)

View File

@@ -10,6 +10,7 @@ package virtcontainers
import (
"context"
"fmt"
"os"
"time"
govmmQemu "github.com/kata-containers/kata-containers/src/runtime/pkg/govmm/qemu"
@@ -68,10 +69,9 @@ func newQemuArch(config HypervisorConfig) (qemuArch, error) {
kernelParamsDebug: kernelParamsDebug,
kernelParams: kernelParams,
disableNvdimm: config.DisableImageNvdimm,
// DAX is disabled on ARM due to a kernel panic in caches_clean_inval_pou.
dax: false,
protection: noneProtection,
legacySerial: config.LegacySerial,
dax: true,
protection: noneProtection,
legacySerial: config.LegacySerial,
},
measurementAlgo: config.MeasurementAlgo,
}
@@ -109,6 +109,35 @@ func (q *qemuArm64) appendImage(ctx context.Context, devices []govmmQemu.Device,
return q.appendBlockImage(ctx, devices, path)
}
// There is no nvdimm/readonly feature in qemu 5.1 which is used by arm64 for now,
// so we temporarily add this specific implementation for arm64 here until
// the qemu used by arm64 is capable for that feature
func (q *qemuArm64) appendNvdimmImage(devices []govmmQemu.Device, path string) ([]govmmQemu.Device, error) {
imageFile, err := os.Open(path)
if err != nil {
return nil, err
}
defer imageFile.Close()
imageStat, err := imageFile.Stat()
if err != nil {
return nil, err
}
object := govmmQemu.Object{
Driver: govmmQemu.NVDIMM,
Type: govmmQemu.MemoryBackendFile,
DeviceID: "nv0",
ID: "mem0",
MemPath: path,
Size: (uint64)(imageStat.Size()),
}
devices = append(devices, object)
return devices, nil
}
func (q *qemuArm64) setIgnoreSharedMemoryMigrationCaps(_ context.Context, _ *govmmQemu.QMP) error {
// x-ignore-shared not support in arm64 for now
return nil

View File

@@ -130,6 +130,39 @@ func TestQemuArm64AppendImage(t *testing.T) {
assert.Equal(expectedOut, devices)
}
func TestQemuArm64AppendNvdimmImage(t *testing.T) {
var devices []govmmQemu.Device
assert := assert.New(t)
f, err := os.CreateTemp("", "img")
assert.NoError(err)
defer func() { _ = f.Close() }()
defer func() { _ = os.Remove(f.Name()) }()
imageStat, err := f.Stat()
assert.NoError(err)
cfg := qemuConfig(QemuVirt)
cfg.ImagePath = f.Name()
arm64, err := newQemuArch(cfg)
assert.NoError(err)
expectedOut := []govmmQemu.Device{
govmmQemu.Object{
Driver: govmmQemu.NVDIMM,
Type: govmmQemu.MemoryBackendFile,
DeviceID: "nv0",
ID: "mem0",
MemPath: f.Name(),
Size: (uint64)(imageStat.Size()),
},
}
devices, err = arm64.appendNvdimmImage(devices, f.Name())
assert.NoError(err)
assert.Equal(expectedOut, devices)
}
func TestQemuArm64WithInitrd(t *testing.T) {
assert := assert.New(t)

View File

@@ -50,11 +50,6 @@ func testCreateSandbox(t *testing.T, id string,
nconfig NetworkConfig, containers []ContainerConfig,
volumes []types.Volume) (*Sandbox, error) {
// GITHUB_RUNNER_CI_NON_VIRT is set to true in .github/workflows/build-checks.yaml file for ARM64 runners because the self hosted runners do not support Virtualization
if os.Getenv("GITHUB_RUNNER_CI_NON_VIRT") == "true" {
t.Skip("Skipping the test as the GitHub self hosted runners for ARM64 do not support Virtualization")
}
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(testDisabledAsNonRoot)
}
@@ -1312,10 +1307,6 @@ func checkSandboxRemains() error {
}
func TestSandboxCreationFromConfigRollbackFromCreateSandbox(t *testing.T) {
// GITHUB_RUNNER_CI_NON_VIRT is set to true in .github/workflows/build-checks.yaml file for ARM64 runners because the self hosted runners do not support Virtualization
if os.Getenv("GITHUB_RUNNER_CI_NON_VIRT") == "true" {
t.Skip("Skipping the test as the GitHub self hosted runners for ARM64 do not support Virtualization")
}
defer cleanUp()
assert := assert.New(t)
ctx := context.Background()
@@ -1407,10 +1398,6 @@ func TestSandboxExperimentalFeature(t *testing.T) {
}
func TestSandbox_Cgroups(t *testing.T) {
// GITHUB_RUNNER_CI_NON_VIRT is set to true in .github/workflows/build-checks.yaml file for ARM64 runners because the self hosted runners do not support Virtualization
if os.Getenv("GITHUB_RUNNER_CI_NON_VIRT") == "true" {
t.Skip("Skipping the test as the GitHub self hosted runners for ARM64 do not support Virtualization")
}
sandboxContainer := ContainerConfig{}
sandboxContainer.Annotations = make(map[string]string)
sandboxContainer.Annotations[annotations.ContainerTypeKey] = string(PodSandbox)

View File

@@ -4544,9 +4544,9 @@ dependencies = [
[[package]]
name = "rkyv"
version = "0.7.46"
version = "0.7.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1"
checksum = "527a97cdfef66f65998b5f3b637c26f5a5ec09cc52a3f9932313ac645f4190f5"
dependencies = [
"bitvec",
"bytecheck",
@@ -4562,9 +4562,9 @@ dependencies = [
[[package]]
name = "rkyv_derive"
version = "0.7.46"
version = "0.7.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5"
checksum = "b5c462a1328c8e67e4d6dbad1eb0355dd43e8ab432c6e227a43657f16ade5033"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -2449,9 +2449,9 @@ dependencies = [
[[package]]
name = "rkyv"
version = "0.7.46"
version = "0.7.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1"
checksum = "9008cd6385b9e161d8229e1f6549dd23c3d022f132a2ea37ac3a10ac4935779b"
dependencies = [
"bitvec",
"bytecheck",
@@ -2467,9 +2467,9 @@ dependencies = [
[[package]]
name = "rkyv_derive"
version = "0.7.46"
version = "0.7.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5"
checksum = "503d1d27590a2b0a3a4ca4c94755aa2875657196ecbf401a42eff41d7de532c0"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -152,17 +152,6 @@
}
},
"volumes": {
"emptyDir": {
"mount_type": "local",
"mount_source": "^$(cpath)/$(sandbox-id)/rootfs/local/",
"mount_point": "^$(cpath)/$(sandbox-id)/rootfs/local/",
"driver": "local",
"source": "local",
"fstype": "local",
"options": [
"mode=0777"
]
},
"emptyDir_memory": {
"mount_type": "bind",
"mount_source": "^/run/kata-containers/sandbox/ephemeral/",

View File

@@ -1160,7 +1160,7 @@ mount_source_allows(p_mount, i_mount, bundle_id, sandbox_id) if {
regex3 := replace(regex2, "$(cpath)", policy_data.common.cpath)
print("mount_source_allows 2: regex3 =", regex3)
regex4 := replace(regex3, "$(sandbox-id)", sandbox_id)
regex4 := replace(regex3, "$(bundle-id)", "[a-z0-9]{64}")
print("mount_source_allows 2: regex4 =", regex4)
regex.match(regex4, i_mount.source)

View File

@@ -105,7 +105,6 @@ pub fn get_mount_and_storage(
storages: &mut Vec<agent::Storage>,
yaml_volume: &volume::Volume,
yaml_mount: &pod::VolumeMount,
pod_security_context: &Option<pod::PodSecurityContext>,
) {
debug!(
"get_mount_and_storage: adding mount and storage for: {:?}",
@@ -113,27 +112,18 @@ pub fn get_mount_and_storage(
);
if let Some(emptyDir) = &yaml_volume.emptyDir {
let settings_volumes = &settings.volumes;
let mut volume: Option<&settings::EmptyDirVolume> = None;
if let Some(medium) = &emptyDir.medium {
if medium == "Memory" {
volume = Some(&settings_volumes.emptyDir_memory);
}
let is_tmpfs = emptyDir.medium.as_ref().is_some_and(|m| m == "Memory");
if is_tmpfs {
get_memory_empty_dir_mount_and_storage(settings, p_mounts, storages, yaml_mount);
} else {
let access = if yaml_mount.readOnly == Some(true) {
debug!("setting read only access for emptyDir mount");
"ro"
} else {
"rw"
};
get_shared_bind_mount(yaml_mount, p_mounts, "rprivate", access);
}
if volume.is_none() {
volume = Some(&settings_volumes.emptyDir);
}
get_empty_dir_mount_and_storage(
settings,
p_mounts,
storages,
yaml_mount,
volume.unwrap(),
pod_security_context,
);
} else if yaml_volume.persistentVolumeClaim.is_some() || yaml_volume.azureFile.is_some() {
get_shared_bind_mount(yaml_mount, p_mounts, "rprivate", "rw");
} else if yaml_volume.hostPath.is_some() {
@@ -149,50 +139,25 @@ pub fn get_mount_and_storage(
}
}
fn get_empty_dir_mount_and_storage(
fn get_memory_empty_dir_mount_and_storage(
settings: &settings::Settings,
p_mounts: &mut Vec<policy::KataMount>,
storages: &mut Vec<agent::Storage>,
yaml_mount: &pod::VolumeMount,
settings_empty_dir: &settings::EmptyDirVolume,
pod_security_context: &Option<pod::PodSecurityContext>,
) {
debug!("Settings emptyDir: {:?}", settings_empty_dir);
let settings_empty_dir = &settings.volumes.emptyDir_memory;
debug!("Settings emptyDir_memory: {:?}", settings_empty_dir);
if yaml_mount.subPathExpr.is_none() {
let mut options = settings_empty_dir.options.clone();
if let Some(gid) = pod_security_context.as_ref().and_then(|sc| sc.fsGroup) {
// This matches the runtime behavior of only setting the fsgid if the mountpoint GID is not 0.
// https://github.com/kata-containers/kata-containers/blob/b69da5f3ba8385c5833b31db41a846a203812675/src/runtime/virtcontainers/kata_agent.go#L1602-L1607
if gid != 0 {
options.push(format!("fsgid={gid}"));
}
}
storages.push(agent::Storage {
driver: settings_empty_dir.driver.clone(),
driver_options: Vec::new(),
source: settings_empty_dir.source.clone(),
fstype: settings_empty_dir.fstype.clone(),
options,
mount_point: format!("{}{}$", &settings_empty_dir.mount_point, &yaml_mount.name),
fs_group: protobuf::MessageField::none(),
special_fields: ::protobuf::SpecialFields::new(),
});
}
let source = if yaml_mount.subPathExpr.is_some() {
let file_name = Path::new(&yaml_mount.mountPath).file_name().unwrap();
let name = OsString::from(file_name).into_string().unwrap();
format!("{}{name}$", &settings.volumes.configMap.mount_source)
} else {
format!("{}{}$", &settings_empty_dir.mount_source, &yaml_mount.name)
};
let mount_type = if yaml_mount.subPathExpr.is_some() {
"bind"
} else {
&settings_empty_dir.mount_type
};
storages.push(agent::Storage {
driver: settings_empty_dir.driver.clone(),
driver_options: Vec::new(),
source: settings_empty_dir.source.clone(),
fstype: settings_empty_dir.fstype.clone(),
options: settings_empty_dir.options.clone(),
mount_point: format!("{}{}$", &settings_empty_dir.mount_point, &yaml_mount.name),
fs_group: protobuf::MessageField::none(),
special_fields: ::protobuf::SpecialFields::new(),
});
let access = match yaml_mount.readOnly {
Some(true) => {
@@ -204,8 +169,8 @@ fn get_empty_dir_mount_and_storage(
p_mounts.push(policy::KataMount {
destination: yaml_mount.mountPath.to_string(),
type_: mount_type.to_string(),
source,
type_: settings_empty_dir.mount_type.clone(),
source: format!("{}{}$", &settings_empty_dir.mount_source, &yaml_mount.name),
options: vec![
"rbind".to_string(),
"rprivate".to_string(),
@@ -318,13 +283,7 @@ fn get_shared_bind_mount(
propagation: &str,
access: &str,
) {
// The Kata Shim filepath.Base() to extract the last element of this path, in
// https://github.com/kata-containers/kata-containers/blob/5e46f814dd79ab6b34588a83825260413839735a/src/runtime/virtcontainers/fs_share_linux.go#L305
// In Rust, Path::file_name() has a similar behavior.
let path = Path::new(&yaml_mount.mountPath);
let mount_path = path.file_name().unwrap().to_str().unwrap();
let source = format!("$(sfprefix){mount_path}$");
let source = "$(sfprefix)[a-zA-Z0-9_.-]+$".to_string();
let dest = yaml_mount.mountPath.clone();
let type_ = "bind".to_string();

View File

@@ -31,7 +31,6 @@ pub struct Settings {
/// Volume settings loaded from genpolicy-settings.json.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Volumes {
pub emptyDir: EmptyDirVolume,
pub emptyDir_memory: EmptyDirVolume,
pub configMap: ConfigMapVolume,
pub image_volume: ImageVolume,

View File

@@ -304,7 +304,6 @@ pub fn get_container_mounts_and_storages(
storages,
volume,
volume_mount,
&podSpec.securityContext,
);
}
}

View File

@@ -3349,9 +3349,9 @@ dependencies = [
[[package]]
name = "rkyv"
version = "0.7.46"
version = "0.7.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1"
checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0"
dependencies = [
"bitvec",
"bytecheck",
@@ -3367,9 +3367,9 @@ dependencies = [
[[package]]
name = "rkyv_derive"
version = "0.7.46"
version = "0.7.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5"
checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -52,10 +52,7 @@ mem/B # For terms like "virtio-mem"
memdisk/B
MDEV/AB
NEMU/AB
NFD/AB # Node Feature Discovery
NIC/AB
nodeSelector/B # Kubernetes RuntimeClass scheduling field
nodeSelectors/B
nv/AB # NVIDIA abbreviation (lowercase)
NVDIMM/AB
OCI/AB
@@ -77,20 +74,15 @@ QEMU/AB
RBAC/AB
RDMA/AB
RNG/AB
RuntimeClass/B # Kubernetes resource (node.k8s.io)
RuntimeClasses/B
SaaS/B # Software as a Service
SCSI/AB
SDK/AB
seccomp # secure computing mode
SHA/AB
SEL/AB # IBM Secure Execution for Linux
SPDX/AB
SRIOV/AB
SEV-SNP/B # AMD Secure Encrypted Virtualization - Secure Nested Paging
SVG/AB
TBD/AB
TEE/AB # Trusted Execution Environment
TOC/AB
TOML/AB
TTY/AB

View File

@@ -1,4 +1,4 @@
417
409
ACPI/AB
ACS/AB
API/AB
@@ -93,7 +93,6 @@ Mellanox/B
Minikube/B
MonitorTest/A
NEMU/AB
NFD/AB
NIC/AB
NVDIMM/AB
NVIDIA/A
@@ -135,14 +134,10 @@ RBAC/AB
RDMA/AB
RHEL/B
RNG/AB
RuntimeClass/B
RuntimeClasses/B
Rustlang/B
SCSI/AB
SDK/AB
SEL/AB
SELinux/B
SEV-SNP/B
SHA/AB
SLES/B
SPDX/AB
@@ -158,7 +153,6 @@ Submodule/A
Sysbench/B
TBD/AB
TDX
TEE/AB
TOC/AB
TOML/AB
TTY/AB
@@ -312,8 +306,6 @@ nack/AB
namespace/ABCD
netlink
netns/AB
nodeSelector/B
nodeSelectors/B
nv/AB
nvidia/A
onwards

View File

@@ -9,6 +9,8 @@ source "${tests_dir}/common.bash"
kubernetes_dir="${tests_dir}/integration/kubernetes"
helm_chart_dir="${repo_root_dir}/tools/packaging/kata-deploy/helm-chart/kata-deploy"
AZ_REGION="${AZ_REGION:-eastus}"
AZ_NODEPOOL_TAGS="${AZ_NODEPOOL_TAGS:-}"
GENPOLICY_PULL_METHOD="${GENPOLICY_PULL_METHOD:-oci-distribution}"
GH_PR_NUMBER="${GH_PR_NUMBER:-}"
HELM_DEFAULT_INSTALLATION="${HELM_DEFAULT_INSTALLATION:-false}"
@@ -111,7 +113,7 @@ function create_cluster() {
"GENPOLICY_PULL_METHOD=${GENPOLICY_PULL_METHOD:0:1}")
az group create \
-l eastus \
-l "${AZ_REGION}" \
-n "${rg}"
# Required by e.g. AKS App Routing for KBS installation.
@@ -129,7 +131,8 @@ function create_cluster() {
--node-count 1 \
--generate-ssh-keys \
--tags "${tags[@]}" \
$([[ "${KATA_HOST_OS}" = "cbl-mariner" ]] && echo "--os-sku AzureLinux --workload-runtime KataVmIsolation")
$([[ "${KATA_HOST_OS}" = "cbl-mariner" ]] && echo "--os-sku AzureLinux --workload-runtime KataVmIsolation") \
$([ -n "${AZ_NODEPOOL_TAGS}" ] && echo "--nodepool-tags "${AZ_NODEPOOL_TAGS}"")
}
function install_bats() {

View File

@@ -175,7 +175,7 @@ function deploy_kata() {
ANNOTATIONS="default_vcpus"
if [[ "${KATA_HOST_OS}" = "cbl-mariner" ]]; then
ANNOTATIONS="image kernel default_vcpus cc_init_data"
ANNOTATIONS="image kernel default_vcpus disable_image_nvdimm cc_init_data"
fi
if [[ "${KATA_HYPERVISOR}" = "qemu" ]]; then
ANNOTATIONS="image initrd kernel default_vcpus"

View File

@@ -12,7 +12,7 @@ load "${BATS_TEST_DIRNAME}/tests_common.sh"
setup() {
[ "$(uname -m)" == "aarch64" ] && skip "See: https://github.com/kata-containers/kata-containers/issues/10928"
[[ "${KATA_HYPERVISOR}" == qemu-coco-dev* ]] && skip "Requires CPU hotplug which disabled by static_sandbox_resource_mgmt"
[[ "${KATA_HYPERVISOR}" == "qemu-tdx" ]] && skip "See: https://github.com/kata-containers/kata-containers/issues/12492"
[[ "${KATA_HYPERVISOR}" == qemu-tdx ]] && skip "See: https://github.com/kata-containers/kata-containers/issues/12492"
setup_common || die "setup_common failed"
@@ -53,7 +53,6 @@ setup() {
teardown() {
[ "$(uname -m)" == "aarch64" ] && skip "See: https://github.com/kata-containers/kata-containers/issues/10928"
[[ "${KATA_HYPERVISOR}" == qemu-coco-dev* ]] && skip "Requires CPU hotplug which disabled by static_sandbox_resource_mgmt"
[[ "${KATA_HYPERVISOR}" == "qemu-tdx" ]] && skip "See: https://github.com/kata-containers/kata-containers/issues/12492"
for pod in "${pods[@]}"; do
kubectl logs ${pod}

View File

@@ -99,31 +99,23 @@ add_annotations_to_yaml() {
esac
}
add_cbl_mariner_annotation_to_yaml() {
local -r yaml_file="$1"
local -r mariner_annotation_kernel="io.katacontainers.config.hypervisor.kernel"
local -r mariner_kernel_path="/usr/share/cloud-hypervisor/vmlinux.bin"
local -r mariner_annotation_image="io.katacontainers.config.hypervisor.image"
local -r mariner_image_path="/opt/kata/share/kata-containers/kata-containers-mariner.img"
add_annotations_to_yaml "${yaml_file}" "${mariner_annotation_kernel}" "${mariner_kernel_path}"
add_annotations_to_yaml "${yaml_file}" "${mariner_annotation_image}" "${mariner_image_path}"
}
add_cbl_mariner_specific_annotations() {
if [[ "${KATA_HOST_OS}" = "cbl-mariner" ]]; then
info "Adding annotations for cbl-mariner"
info "Add kernel and image path and annotations for cbl-mariner"
local mariner_annotation_kernel="io.katacontainers.config.hypervisor.kernel"
local mariner_kernel_path="/usr/share/cloud-hypervisor/vmlinux.bin"
local mariner_annotation_image="io.katacontainers.config.hypervisor.image"
local mariner_image_path="/opt/kata/share/kata-containers/kata-containers-mariner.img"
local mariner_annotation_disable_image_nvdimm="io.katacontainers.config.hypervisor.disable_image_nvdimm"
local mariner_disable_image_nvdimm=true
for K8S_TEST_YAML in runtimeclass_workloads_work/*.yaml
do
add_cbl_mariner_annotation_to_yaml "${K8S_TEST_YAML}"
done
for K8S_TEST_YAML in runtimeclass_workloads_work/openvpn/*.yaml
do
add_cbl_mariner_annotation_to_yaml "${K8S_TEST_YAML}"
add_annotations_to_yaml "${K8S_TEST_YAML}" "${mariner_annotation_kernel}" "${mariner_kernel_path}"
add_annotations_to_yaml "${K8S_TEST_YAML}" "${mariner_annotation_image}" "${mariner_image_path}"
add_annotations_to_yaml "${K8S_TEST_YAML}" "${mariner_annotation_disable_image_nvdimm}" "${mariner_disable_image_nvdimm}"
done
fi
}

View File

@@ -37,6 +37,8 @@ K8S_TEST_DIR="${kubernetes_dir:-"${BATS_TEST_DIRNAME}"}"
AUTO_GENERATE_POLICY="${AUTO_GENERATE_POLICY:-}"
GENPOLICY_PULL_METHOD="${GENPOLICY_PULL_METHOD:-}"
GENPOLICY_BINARY="${GENPOLICY_BINARY:-"/opt/kata/bin/genpolicy"}"
GENPOLICY_SETTINGS_DIR="${GENPOLICY_SETTINGS_DIR:-"/opt/kata/share/defaults/kata-containers"}"
KATA_HYPERVISOR="${KATA_HYPERVISOR:-}"
KATA_HOST_OS="${KATA_HOST_OS:-}"
@@ -191,12 +193,11 @@ adapt_common_policy_settings() {
# and change these settings to use Kata CI cluster's default namespace.
create_common_genpolicy_settings() {
declare -r genpolicy_settings_dir="$1"
declare -r default_genpolicy_settings_dir="/opt/kata/share/defaults/kata-containers"
auto_generate_policy_enabled || return 0
cp "${default_genpolicy_settings_dir}/genpolicy-settings.json" "${genpolicy_settings_dir}"
cp "${default_genpolicy_settings_dir}/rules.rego" "${genpolicy_settings_dir}"
cp "${GENPOLICY_SETTINGS_DIR}/genpolicy-settings.json" "${genpolicy_settings_dir}"
cp "${GENPOLICY_SETTINGS_DIR}/rules.rego" "${genpolicy_settings_dir}"
adapt_common_policy_settings "${genpolicy_settings_dir}"
}
@@ -247,7 +248,7 @@ auto_generate_policy_no_added_flags() {
declare -r additional_flags="${4:-""}"
auto_generate_policy_enabled || return 0
local genpolicy_command="RUST_LOG=info /opt/kata/bin/genpolicy -u -y ${yaml_file}"
local genpolicy_command="RUST_LOG=info ${GENPOLICY_BINARY} -u -y ${yaml_file}"
genpolicy_command+=" -p ${settings_dir}/rules.rego"
genpolicy_command+=" -j ${settings_dir}/genpolicy-settings.json"

View File

@@ -291,45 +291,15 @@ fn remove_custom_runtime_configs(config: &Config) -> Result<()> {
/// Note: The src parameter is kept to allow for unit testing with temporary directories,
/// even though in production it always uses /opt/kata-artifacts/opt/kata
///
/// Symlinks in the source tree are preserved at the destination (recreated as symlinks
/// instead of copying the target file). Absolute targets under the source root are
/// rewritten to the destination root so they remain valid.
fn copy_artifacts(src: &str, dst: &str) -> Result<()> {
let src_path = Path::new(src);
for entry in WalkDir::new(src).follow_links(false) {
for entry in WalkDir::new(src) {
let entry = entry?;
let src_path_entry = entry.path();
let relative_path = src_path_entry.strip_prefix(src)?;
let src_path = entry.path();
let relative_path = src_path.strip_prefix(src)?;
let dst_path = Path::new(dst).join(relative_path);
if entry.file_type().is_dir() {
fs::create_dir_all(&dst_path)?;
} else if entry.file_type().is_symlink() {
// Preserve symlinks: create a symlink at destination instead of copying the target
let link_target = fs::read_link(src_path_entry)
.with_context(|| format!("Failed to read symlink: {:?}", src_path_entry))?;
let new_target: std::path::PathBuf = if link_target.is_absolute() {
// Rewrite absolute targets that point inside the source tree
if let Ok(rel) = link_target.strip_prefix(src_path) {
Path::new(dst).join(rel)
} else {
link_target.into()
}
} else {
link_target.into()
};
if let Some(parent) = dst_path.parent() {
fs::create_dir_all(parent)?;
}
match fs::remove_file(&dst_path) {
Ok(()) => {}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
Err(e) => return Err(e.into()),
}
std::os::unix::fs::symlink(&new_target, &dst_path)
.with_context(|| format!("Failed to create symlink {:?} -> {:?}", dst_path, new_target))?;
} else {
if let Some(parent) = dst_path.parent() {
fs::create_dir_all(parent)?;
@@ -347,7 +317,7 @@ fn copy_artifacts(src: &str, dst: &str) -> Result<()> {
Err(e) => return Err(e.into()), // Other errors should be propagated
}
fs::copy(src_path_entry, &dst_path)?;
fs::copy(src_path, &dst_path)?;
}
}
Ok(())
@@ -918,54 +888,65 @@ async fn configure_mariner(config: &Config) -> Result<()> {
#[cfg(test)]
mod tests {
use super::*;
use rstest::rstest;
#[rstest]
#[case("qemu", "qemu")]
#[case("qemu-tdx", "qemu")]
#[case("qemu-snp", "qemu")]
#[case("qemu-se", "qemu")]
#[case("qemu-coco-dev", "qemu")]
#[case("qemu-cca", "qemu")]
#[case("qemu-nvidia-gpu", "qemu")]
#[case("qemu-nvidia-gpu-tdx", "qemu")]
#[case("qemu-nvidia-gpu-snp", "qemu")]
#[case("qemu-runtime-rs", "qemu")]
#[case("qemu-coco-dev-runtime-rs", "qemu")]
#[case("qemu-se-runtime-rs", "qemu")]
#[case("qemu-snp-runtime-rs", "qemu")]
#[case("qemu-tdx-runtime-rs", "qemu")]
fn test_get_hypervisor_name_qemu_variants(#[case] shim: &str, #[case] expected: &str) {
assert_eq!(get_hypervisor_name(shim).unwrap(), expected);
#[test]
fn test_get_hypervisor_name_qemu_variants() {
// Test all QEMU variants
assert_eq!(get_hypervisor_name("qemu").unwrap(), "qemu");
assert_eq!(get_hypervisor_name("qemu-tdx").unwrap(), "qemu");
assert_eq!(get_hypervisor_name("qemu-snp").unwrap(), "qemu");
assert_eq!(get_hypervisor_name("qemu-se").unwrap(), "qemu");
assert_eq!(get_hypervisor_name("qemu-coco-dev").unwrap(), "qemu");
assert_eq!(get_hypervisor_name("qemu-cca").unwrap(), "qemu");
assert_eq!(get_hypervisor_name("qemu-nvidia-gpu").unwrap(), "qemu");
assert_eq!(get_hypervisor_name("qemu-nvidia-gpu-tdx").unwrap(), "qemu");
assert_eq!(get_hypervisor_name("qemu-nvidia-gpu-snp").unwrap(), "qemu");
assert_eq!(get_hypervisor_name("qemu-runtime-rs").unwrap(), "qemu");
assert_eq!(
get_hypervisor_name("qemu-coco-dev-runtime-rs").unwrap(),
"qemu"
);
assert_eq!(get_hypervisor_name("qemu-se-runtime-rs").unwrap(), "qemu");
assert_eq!(get_hypervisor_name("qemu-snp-runtime-rs").unwrap(), "qemu");
assert_eq!(get_hypervisor_name("qemu-tdx-runtime-rs").unwrap(), "qemu");
}
#[rstest]
#[case("clh", "clh")]
#[case("cloud-hypervisor", "cloud-hypervisor")]
#[case("dragonball", "dragonball")]
#[case("fc", "firecracker")]
#[case("firecracker", "firecracker")]
#[case("remote", "remote")]
fn test_get_hypervisor_name_other_hypervisors(#[case] shim: &str, #[case] expected: &str) {
assert_eq!(get_hypervisor_name(shim).unwrap(), expected);
#[test]
fn test_get_hypervisor_name_other_hypervisors() {
// Test other hypervisors
assert_eq!(get_hypervisor_name("clh").unwrap(), "clh");
assert_eq!(
get_hypervisor_name("cloud-hypervisor").unwrap(),
"cloud-hypervisor"
);
assert_eq!(get_hypervisor_name("dragonball").unwrap(), "dragonball");
assert_eq!(get_hypervisor_name("fc").unwrap(), "firecracker");
assert_eq!(get_hypervisor_name("firecracker").unwrap(), "firecracker");
assert_eq!(get_hypervisor_name("remote").unwrap(), "remote");
}
#[rstest]
#[case("")]
#[case("unknown-shim")]
#[case("custom")]
fn test_get_hypervisor_name_unknown(#[case] shim: &str) {
let result = get_hypervisor_name(shim);
#[test]
fn test_get_hypervisor_name_unknown() {
// Test unknown shim returns error with clear message
let result = get_hypervisor_name("unknown-shim");
assert!(result.is_err(), "Unknown shim should return an error");
let err_msg = result.unwrap_err().to_string();
assert!(
err_msg.contains(&format!("Unknown shim '{}'", shim)),
err_msg.contains("Unknown shim 'unknown-shim'"),
"Error message should mention the unknown shim"
);
assert!(
err_msg.contains("Valid shims are:"),
"Error message should list valid shims"
);
let result = get_hypervisor_name("custom");
assert!(result.is_err(), "Custom shim should return an error");
let err_msg = result.unwrap_err().to_string();
assert!(
err_msg.contains("Unknown shim 'custom'"),
"Error message should mention the custom shim"
);
}
#[test]
@@ -1042,36 +1023,10 @@ mod tests {
}
#[test]
fn test_copy_artifacts_preserves_symlinks() {
let src_dir = tempfile::tempdir().unwrap();
let dst_dir = tempfile::tempdir().unwrap();
// Create a real file and a symlink pointing to it
let real_file = src_dir.path().join("real-file.txt");
fs::write(&real_file, "actual content").unwrap();
let link_path = src_dir.path().join("link-to-real");
std::os::unix::fs::symlink(&real_file, &link_path).unwrap();
copy_artifacts(
src_dir.path().to_str().unwrap(),
dst_dir.path().to_str().unwrap(),
)
.unwrap();
let dst_link = dst_dir.path().join("link-to-real");
let dst_real = dst_dir.path().join("real-file.txt");
assert!(dst_real.exists(), "real file should be copied");
assert!(dst_link.is_symlink(), "destination should be a symlink");
assert_eq!(
fs::read_link(&dst_link).unwrap(),
dst_real,
"symlink should point to the real file in the same tree"
);
assert_eq!(
fs::read_to_string(&dst_link).unwrap(),
"actual content",
"following the symlink should yield the real content"
);
fn test_get_hypervisor_name_empty() {
let result = get_hypervisor_name("");
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(err_msg.contains("Unknown shim"));
}
}

View File

@@ -25,33 +25,19 @@ struct ContainerdRuntimeParams {
snapshotter: Option<String>,
}
/// Plugin ID for CRI runtime in containerd config v3 (version = 3).
const CONTAINERD_V3_RUNTIME_PLUGIN_ID: &str = "\"io.containerd.cri.v1.runtime\"";
/// Plugin ID for CRI in containerd config v2 (version = 2).
const CONTAINERD_V2_CRI_PLUGIN_ID: &str = "\"io.containerd.grpc.v1.cri\"";
/// Legacy plugin key when config has no version (pre-v2).
const CONTAINERD_LEGACY_CRI_PLUGIN_ID: &str = "cri";
/// Plugin ID for CRI images in containerd config v3 (version = 3).
const CONTAINERD_CRI_IMAGES_PLUGIN_ID: &str = "\"io.containerd.cri.v1.images\"";
fn get_containerd_pluginid(config_file: &str) -> Result<&'static str> {
let content = fs::read_to_string(config_file)
.with_context(|| format!("Failed to read containerd config file: {}", config_file))?;
if content.contains("version = 3") {
Ok(CONTAINERD_V3_RUNTIME_PLUGIN_ID)
Ok("\"io.containerd.cri.v1.runtime\"")
} else if content.contains("version = 2") {
Ok(CONTAINERD_V2_CRI_PLUGIN_ID)
Ok("\"io.containerd.grpc.v1.cri\"")
} else {
Ok(CONTAINERD_LEGACY_CRI_PLUGIN_ID)
Ok("cri")
}
}
/// True when the containerd config is v3 (version = 3), i.e. we use the split CRI plugins.
fn is_containerd_v3_config(pluginid: &str) -> bool {
pluginid == CONTAINERD_V3_RUNTIME_PLUGIN_ID
}
fn get_containerd_output_path(paths: &ContainerdPaths) -> PathBuf {
if paths.use_drop_in {
if paths.drop_in_file.starts_with("/etc/containerd/") {
@@ -109,26 +95,6 @@ fn write_containerd_runtime_config(
&format!("{runtime_table}.snapshotter"),
snapshotter,
)?;
// In containerd config v3 the CRI plugin is split into runtime and images,
// and setting the snapshotter only on the runtime plugin is not enough for image
// pull/prepare.
//
// The images plugin must have runtime_platforms.<runtime>.snapshotter so it
// uses the correct snapshotter per runtime (e.g. nydus, erofs).
//
// A PR on the containerd side is open so we can rely on the runtime plugin
// snapshotter alone: https://github.com/containerd/containerd/pull/12836
if is_containerd_v3_config(pluginid) {
toml_utils::set_toml_value(
config_file,
&format!(
".plugins.{}.runtime_platforms.\"{}\".snapshotter",
CONTAINERD_CRI_IMAGES_PLUGIN_ID,
params.runtime_name
),
snapshotter,
)?;
}
}
Ok(())
@@ -266,7 +232,6 @@ pub async fn configure_custom_containerd_runtime(
};
write_containerd_runtime_config(&configuration_file, pluginid, &params)?;
Ok(())
}
@@ -569,167 +534,102 @@ pub fn snapshotter_handler_mapping_validation_check(config: &Config) -> Result<(
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::toml as toml_utils;
use rstest::rstest;
use std::path::Path;
use tempfile::NamedTempFile;
fn make_params(
runtime_name: &str,
snapshotter: Option<&str>,
) -> ContainerdRuntimeParams {
ContainerdRuntimeParams {
runtime_name: runtime_name.to_string(),
runtime_path: "\"/opt/kata/bin/kata-runtime\"".to_string(),
config_path: "\"/opt/kata/share/defaults/kata-containers/configuration-qemu.toml\""
.to_string(),
pod_annotations: "[\"io.katacontainers.*\"]",
snapshotter: snapshotter.map(|s| s.to_string()),
}
#[test]
fn test_check_containerd_snapshotter_version_support_1_6_with_mapping() {
// Version 1.6 with snapshotter mapping should fail
let result = check_containerd_snapshotter_version_support("containerd://1.6.28", true);
assert!(result.is_err());
assert!(result
.unwrap_err()
.to_string()
.contains("kata-deploy only supports snapshotter configuration with containerd 1.7 or newer"));
}
/// CRI images runtime_platforms snapshotter is set only for v3 config when a snapshotter is configured.
#[rstest]
#[case(CONTAINERD_V3_RUNTIME_PLUGIN_ID, Some("\"nydus\""), "kata-qemu", true)]
#[case(CONTAINERD_V2_CRI_PLUGIN_ID, Some("\"nydus\""), "kata-qemu", false)]
#[case(CONTAINERD_V3_RUNTIME_PLUGIN_ID, None, "kata-qemu", false)]
#[case(CONTAINERD_V3_RUNTIME_PLUGIN_ID, Some("\"erofs\""), "kata-clh", true)]
fn test_write_containerd_runtime_config_cri_images_runtime_platforms_snapshotter(
#[case] pluginid: &str,
#[case] snapshotter: Option<&str>,
#[case] runtime_name: &str,
#[case] expect_runtime_platforms_set: bool,
) {
let file = NamedTempFile::new().unwrap();
let path = file.path();
std::fs::write(path, "").unwrap();
#[test]
fn test_check_containerd_snapshotter_version_support_1_6_without_mapping() {
// Version 1.6 without snapshotter mapping should pass (no mapping means no check needed)
let result = check_containerd_snapshotter_version_support("containerd://1.6.28", false);
assert!(result.is_ok());
}
let params = make_params(runtime_name, snapshotter);
write_containerd_runtime_config(path, pluginid, &params).unwrap();
#[test]
fn test_check_containerd_snapshotter_version_support_1_7_with_mapping() {
// Version 1.7 with snapshotter mapping should pass
let result = check_containerd_snapshotter_version_support("containerd://1.7.15", true);
assert!(result.is_ok());
}
let images_snapshotter_path = format!(
".plugins.\"io.containerd.cri.v1.images\".runtime_platforms.\"{}\".snapshotter",
runtime_name
);
let result = toml_utils::get_toml_value(Path::new(path), &images_snapshotter_path);
#[test]
fn test_check_containerd_snapshotter_version_support_2_0_with_mapping() {
// Version 2.0 with snapshotter mapping should pass
let result = check_containerd_snapshotter_version_support("containerd://2.0.0", true);
assert!(result.is_ok());
}
if expect_runtime_platforms_set {
let value = result.unwrap_or_else(|e| {
panic!(
"expected CRI images runtime_platforms.{} snapshotter to be set: {}",
runtime_name, e
)
});
assert_eq!(
value,
snapshotter.unwrap().trim_matches('"'),
"runtime_platforms snapshotter value"
);
} else {
#[test]
fn test_check_containerd_snapshotter_version_support_without_prefix() {
// Version without containerd:// prefix should still work
let result = check_containerd_snapshotter_version_support("1.6.28", true);
assert!(result.is_err());
}
#[test]
fn test_check_containerd_snapshotter_version_support_1_6_variants() {
// Test various 1.6.x versions
assert!(check_containerd_snapshotter_version_support("containerd://1.6.0", true).is_err());
assert!(check_containerd_snapshotter_version_support("containerd://1.6.28", true).is_err());
assert!(check_containerd_snapshotter_version_support("containerd://1.6.999", true).is_err());
}
#[test]
fn test_check_containerd_snapshotter_version_support_1_7_variants() {
// Test various 1.7+ versions should pass
assert!(check_containerd_snapshotter_version_support("containerd://1.7.0", true).is_ok());
assert!(check_containerd_snapshotter_version_support("containerd://1.7.15", true).is_ok());
assert!(check_containerd_snapshotter_version_support("containerd://1.8.0", true).is_ok());
}
#[test]
fn test_check_containerd_erofs_version_support() {
// Versions that should pass (2.2.0+)
let passing_versions = [
"containerd://2.2.0",
"containerd://2.2.0-rc.1",
"containerd://2.2.1",
"containerd://2.3.0",
"containerd://3.0.0",
"containerd://2.3.0-beta.0",
"2.2.0", // without prefix
];
for version in passing_versions {
assert!(
result.is_err(),
"expected CRI images runtime_platforms.{} snapshotter not to be set for pluginid={:?} snapshotter={:?}",
runtime_name,
pluginid,
snapshotter
check_containerd_erofs_version_support(version).is_ok(),
"Expected {} to pass",
version
);
}
}
/// Written containerd config (e.g. drop-in) must not start with blank lines when written to an initially empty file.
#[rstest]
#[case(CONTAINERD_V3_RUNTIME_PLUGIN_ID)]
#[case(CONTAINERD_V2_CRI_PLUGIN_ID)]
fn test_write_containerd_runtime_config_empty_file_no_leading_newlines(
#[case] pluginid: &str,
) {
let file = NamedTempFile::new().unwrap();
let path = file.path();
std::fs::write(path, "").unwrap();
let params = make_params("kata-qemu", Some("\"nydus\""));
write_containerd_runtime_config(path, pluginid, &params).unwrap();
let content = std::fs::read_to_string(path).unwrap();
assert!(
!content.starts_with('\n'),
"containerd config must not start with newline(s), got {} leading newlines (pluginid={})",
content.chars().take_while(|&c| c == '\n').count(),
pluginid
);
assert!(
content.trim_start().starts_with('['),
"config should start with a TOML table"
);
}
#[rstest]
#[case("containerd://1.6.28", true, false, Some("kata-deploy only supports snapshotter configuration with containerd 1.7 or newer"))]
#[case("containerd://1.6.28", false, true, None)]
#[case("containerd://1.6.0", true, false, None)]
#[case("containerd://1.6.999", true, false, None)]
#[case("containerd://1.7.0", true, true, None)]
#[case("containerd://1.7.15", true, true, None)]
#[case("containerd://1.8.0", true, true, None)]
#[case("containerd://2.0.0", true, true, None)]
#[case("1.6.28", true, false, None)]
fn test_check_containerd_snapshotter_version_support(
#[case] version: &str,
#[case] has_mapping: bool,
#[case] expect_ok: bool,
#[case] expected_error_substring: Option<&str>,
) {
let result = check_containerd_snapshotter_version_support(version, has_mapping);
if expect_ok {
assert!(result.is_ok(), "expected ok for version={} has_mapping={}", version, has_mapping);
} else {
assert!(result.is_err(), "expected err for version={} has_mapping={}", version, has_mapping);
if let Some(sub) = expected_error_substring {
assert!(
result.unwrap_err().to_string().contains(sub),
"error should contain {:?}",
sub
);
}
// Versions that should fail (< 2.2.0)
let failing_versions = [
("containerd://2.1.0", "containerd must be 2.2.0 or newer"),
("containerd://2.1.5-rc.1", "containerd must be 2.2.0 or newer"),
("containerd://2.0.0", "containerd must be 2.2.0 or newer"),
("containerd://1.7.0", "containerd must be 2.2.0 or newer"),
("containerd://1.6.28", "containerd must be 2.2.0 or newer"),
("2.1.0", "containerd must be 2.2.0 or newer"), // without prefix
("invalid", "Invalid containerd version format"),
("containerd://abc.2.0", "Failed to parse major version"),
];
for (version, expected_error) in failing_versions {
let result = check_containerd_erofs_version_support(version);
assert!(result.is_err(), "Expected {} to fail", version);
assert!(
result.unwrap_err().to_string().contains(expected_error),
"Expected error for {} to contain '{}'",
version,
expected_error
);
}
}
#[rstest]
#[case("containerd://2.2.0")]
#[case("containerd://2.2.0-rc.1")]
#[case("containerd://2.2.1")]
#[case("containerd://2.3.0")]
#[case("containerd://3.0.0")]
#[case("containerd://2.3.0-beta.0")]
#[case("2.2.0")]
fn test_check_containerd_erofs_version_support_passing(#[case] version: &str) {
assert!(
check_containerd_erofs_version_support(version).is_ok(),
"Expected {} to pass",
version
);
}
#[rstest]
#[case("containerd://2.1.0", "containerd must be 2.2.0 or newer")]
#[case("containerd://2.1.5-rc.1", "containerd must be 2.2.0 or newer")]
#[case("containerd://2.0.0", "containerd must be 2.2.0 or newer")]
#[case("containerd://1.7.0", "containerd must be 2.2.0 or newer")]
#[case("containerd://1.6.28", "containerd must be 2.2.0 or newer")]
#[case("2.1.0", "containerd must be 2.2.0 or newer")]
#[case("invalid", "Invalid containerd version format")]
#[case("containerd://abc.2.0", "Failed to parse major version")]
fn test_check_containerd_erofs_version_support_failing(
#[case] version: &str,
#[case] expected_error: &str,
) {
let result = check_containerd_erofs_version_support(version);
assert!(result.is_err(), "Expected {} to fail", version);
assert!(
result.unwrap_err().to_string().contains(expected_error),
"Expected error for {} to contain '{}'",
version,
expected_error
);
}
}

View File

@@ -65,23 +65,17 @@ fn split_non_toml_header(content: &str) -> (&str, &str) {
/// Write a TOML file with an optional non-TOML header (e.g. K3s template line).
/// Ensures the header ends with a newline before the TOML body.
/// Trims leading newlines from the serialized document to avoid many blank lines
/// when the file was initially empty (e.g. containerd drop-in).
fn write_toml_with_header(
file_path: &Path,
header: &str,
doc: &DocumentMut,
) -> Result<()> {
let normalized_header = if header.is_empty() {
String::new()
} else if header.ends_with('\n') {
let normalized_header = if header.ends_with('\n') {
header.to_string()
} else {
format!("{header}\n")
};
let body = doc.to_string();
let body_trimmed = body.trim_start_matches('\n');
std::fs::write(file_path, format!("{}{}", normalized_header, body_trimmed))
std::fs::write(file_path, format!("{}{}", normalized_header, doc.to_string()))
.with_context(|| format!("Failed to write TOML file: {file_path:?}"))?;
Ok(())
}
@@ -615,37 +609,6 @@ mod tests {
assert!(content.contains("runtime_type"));
}
#[rstest]
#[case("", "")]
#[case("{{ template \"base\" . }}\n", "{{ template \"base\" . }}\n")]
fn test_set_toml_value_empty_file_no_leading_newlines(
#[case] initial_content: &str,
#[case] expected_prefix: &str,
) {
let file = NamedTempFile::new().unwrap();
let path = file.path();
std::fs::write(path, initial_content).unwrap();
set_toml_value(
path,
".plugins.\"io.containerd.cri.v1.runtime\".containerd.runtimes.kata-qemu.runtime_type",
"\"io.containerd.kata-qemu.v2\"",
)
.unwrap();
let content = std::fs::read_to_string(path).unwrap();
assert!(content.starts_with(expected_prefix), "header/prefix must be preserved");
let body_start = content.strip_prefix(expected_prefix).unwrap();
assert!(
!body_start.starts_with('\n'),
"written TOML body must not start with newline(s) after header, got {} leading newlines",
body_start.chars().take_while(|&c| c == '\n').count()
);
assert!(
body_start.trim_start().starts_with('['),
"body should start with a TOML table"
);
}
#[test]
fn test_get_toml_value() {
let file = NamedTempFile::new().unwrap();
@@ -778,22 +741,24 @@ mod tests {
assert_eq!(agent_debug, "false");
}
#[rstest]
#[case("test.string_value", "test_string", "test_string")]
#[case("test.bool_value", "true", "true")]
#[case("test.int_value", "42", "42")]
fn test_toml_value_types(
#[case] path: &str,
#[case] value: &str,
#[case] expected: &str,
) {
#[test]
fn test_toml_value_types() {
let file = NamedTempFile::new().unwrap();
let file_path = file.path();
std::fs::write(file_path, "").unwrap();
let path = file.path();
std::fs::write(path, "").unwrap();
set_toml_value(file_path, path, value).unwrap();
let got = get_toml_value(file_path, path).unwrap();
assert_eq!(got, expected);
// Test different value types
set_toml_value(path, "test.string_value", "test_string").unwrap();
set_toml_value(path, "test.bool_value", "true").unwrap();
set_toml_value(path, "test.int_value", "42").unwrap();
let string_val = get_toml_value(path, "test.string_value").unwrap();
let bool_val = get_toml_value(path, "test.bool_value").unwrap();
let int_val = get_toml_value(path, "test.int_value").unwrap();
assert_eq!(string_val, "test_string");
assert_eq!(bool_val, "true");
assert_eq!(int_val, "42");
}
#[test]
@@ -1305,20 +1270,17 @@ kernel_params = "console=hvc0"
.contains("Failed to read TOML file"));
}
#[rstest]
#[case("get")]
#[case("set")]
fn test_invalid_toml(#[case] op: &str) {
#[test]
fn test_get_toml_value_invalid_toml() {
let temp_file = NamedTempFile::new().unwrap();
let temp_path = temp_file.path();
// Write invalid TOML
std::fs::write(temp_path, "this is not [ valid toml {").unwrap();
let result = match op {
"get" => get_toml_value(temp_path, "some.path").map(drop),
"set" => set_toml_value(temp_path, "some.path", "\"value\""),
_ => panic!("unknown op"),
};
assert!(result.is_err(), "Should fail parsing invalid TOML (op={})", op);
let result = get_toml_value(temp_path, "some.path");
assert!(result.is_err(), "Should fail parsing invalid TOML");
// Just verify it's an error, don't check specific message
}
#[test]
@@ -1343,6 +1305,18 @@ kernel_params = "console=hvc0"
assert!(result.is_err());
}
#[test]
fn test_set_toml_value_invalid_toml() {
let temp_file = NamedTempFile::new().unwrap();
let temp_path = temp_file.path();
// Write invalid TOML
std::fs::write(temp_path, "this is not [ valid toml {").unwrap();
let result = set_toml_value(temp_path, "some.path", "\"value\"");
assert!(result.is_err(), "Should fail parsing invalid TOML");
}
#[test]
fn test_append_to_toml_array_nonexistent_file() {
let result = append_to_toml_array(
@@ -1353,25 +1327,30 @@ kernel_params = "console=hvc0"
assert!(result.is_err());
}
#[rstest]
#[case("append")]
#[case("get")]
fn test_toml_array_not_an_array(#[case] op: &str) {
#[test]
fn test_append_to_toml_array_not_an_array() {
let temp_file = NamedTempFile::new().unwrap();
let temp_path = temp_file.path();
// Write TOML with a string, not an array
std::fs::write(temp_path, "[section]\nkey = \"value\"").unwrap();
let result = match op {
"append" => append_to_toml_array(temp_path, "section.key", "\"item\"").map(drop),
"get" => get_toml_array(temp_path, "section.key").map(drop),
_ => panic!("unknown op"),
};
assert!(result.is_err(), "op={}", op);
assert!(
result.unwrap_err().to_string().contains("not an array"),
"op={}",
op
);
let result = append_to_toml_array(temp_path, "section.key", "\"item\"");
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("not an array"));
}
#[test]
fn test_get_toml_array_not_an_array() {
let temp_file = NamedTempFile::new().unwrap();
let temp_path = temp_file.path();
// Write TOML with a string, not an array
std::fs::write(temp_path, "[section]\nkey = \"value\"").unwrap();
let result = get_toml_array(temp_path, "section.key");
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("not an array"));
}
#[test]

View File

@@ -229,7 +229,6 @@ shims:
agent:
httpsProxy: ""
noProxy: ""
# Optional: set runtimeClass.nodeSelector to pin TEE to specific nodes (always applied). If unset, NFD TEE labels are auto-injected when NFD is detected.
# Default shim per architecture
defaultShim:
@@ -312,8 +311,8 @@ helm install kata-deploy oci://ghcr.io/kata-containers/kata-deploy-charts/kata-d
Includes:
- `qemu-snp` - AMD SEV-SNP (amd64)
- `qemu-tdx` - Intel TDX (amd64)
- `qemu-se` - IBM Secure Execution for Linux (SEL) (s390x)
- `qemu-se-runtime-rs` - IBM Secure Execution for Linux (SEL) Rust runtime (s390x)
- `qemu-se` - IBM Secure Execution (s390x)
- `qemu-se-runtime-rs` - IBM Secure Execution Rust runtime (s390x)
- `qemu-cca` - Arm Confidential Compute Architecture (arm64)
- `qemu-coco-dev` - Confidential Containers development (amd64, s390x)
- `qemu-coco-dev-runtime-rs` - Confidential Containers development Rust runtime (amd64, s390x)
@@ -335,27 +334,6 @@ Includes:
**Note**: These example files are located in the chart directory. When installing from the OCI registry, you'll need to download them separately or clone the repository to access them.
### RuntimeClass Node Selectors for TEE Shims
**Manual configuration:** Any `nodeSelector` you set under `shims.<shim>.runtimeClass.nodeSelector`
is **always applied** to that shim's RuntimeClass, whether or not NFD is present. Use this when
you want to pin TEE workloads to specific nodes (e.g. without NFD, or with custom labels).
**Auto-inject when NFD is present:** If you do *not* set a `runtimeClass.nodeSelector` for a
TEE shim, the chart can **automatically inject** NFD-based labels when NFD is detected in the
cluster (deployed by this chart with `node-feature-discovery.enabled=true` or found externally):
- AMD SEV-SNP shims: `amd.feature.node.kubernetes.io/snp: "true"`
- Intel TDX shims: `intel.feature.node.kubernetes.io/tdx: "true"`
- IBM Secure Execution for Linux (SEL) shims (s390x): `feature.node.kubernetes.io/cpu-security.se.enabled: "true"`
The chart uses Helm's `lookup` function to detect NFD (by looking for the
`node-feature-discovery-worker` DaemonSet). Auto-inject only runs when NFD is detected and
no manual `runtimeClass.nodeSelector` is set for that shim.
**Note**: NFD detection requires cluster access. During `helm template` (dry-run without a
cluster), external NFD is not seen, so auto-injected labels are not added. Manual
`runtimeClass.nodeSelector` values are still applied in all cases.
## `RuntimeClass` Management
**NEW**: Starting with Kata Containers v3.23.0, `runtimeClasses` are managed by

View File

@@ -1,68 +1,9 @@
{{- /*
Render a single RuntimeClass. Params (dict): root (.), shim, config, shimConfig,
nameOverride (optional; if set, use as metadata.name for default RC), useShimNodeSelectors.
*/ -}}
{{- define "kata-deploy.runtimeclass" -}}
---
kind: RuntimeClass
apiVersion: node.k8s.io/v1
metadata:
{{- if .root.Values.env.multiInstallSuffix }}
name: kata-{{ .shim }}-{{ .root.Values.env.multiInstallSuffix }}
{{- else if .nameOverride }}
name: {{ .nameOverride }}
{{- else }}
name: kata-{{ .shim }}
{{- end }}
labels:
app.kubernetes.io/managed-by: kata-deploy
{{- if .root.Values.env.multiInstallSuffix }}
kata-deploy/instance: {{ .root.Values.env.multiInstallSuffix | quote }}
{{- else }}
kata-deploy/instance: "default"
{{- end }}
{{- if .root.Values.env.multiInstallSuffix }}
handler: kata-{{ .shim }}-{{ .root.Values.env.multiInstallSuffix }}
{{- else }}
handler: kata-{{ .shim }}
{{- end }}
overhead:
podFixed:
memory: {{ .config.memory | quote }}
cpu: {{ .config.cpu | quote }}
scheduling:
nodeSelector:
katacontainers.io/kata-runtime: "true"
{{- /* TEE shims: snp, tdx, -se, -se-runtime-rs (SE = IBM Secure Execution / SEL) */ -}}
{{- $isTeeShim := or (contains "snp" .shim) (contains "tdx" .shim) (hasSuffix "-se" .shim) (hasSuffix "-se-runtime-rs" .shim) -}}
{{- $isPureTeeShim := and $isTeeShim (not (contains "nvidia-gpu" .shim)) -}}
{{- if or (and .shimConfig.runtimeClass .shimConfig.runtimeClass.nodeSelector) (and .useShimNodeSelectors $isPureTeeShim) }}
{{- if and .shimConfig.runtimeClass .shimConfig.runtimeClass.nodeSelector }}
{{- range $key, $value := .shimConfig.runtimeClass.nodeSelector }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- else }}
{{- if contains "snp" .shim }}
amd.feature.node.kubernetes.io/snp: "true"
{{- end }}
{{- if contains "tdx" .shim }}
intel.feature.node.kubernetes.io/tdx: "true"
{{- end }}
{{- if or (hasSuffix "-se" .shim) (hasSuffix "-se-runtime-rs" .shim) }}
feature.node.kubernetes.io/cpu-security.se.enabled: "true"
{{- end }}
{{- end }}
{{- end }}
{{- end -}}
{{- if .Values.runtimeClasses.enabled }}
{{- $multiInstallSuffix := .Values.env.multiInstallSuffix }}
{{- $createDefaultRC := .Values.runtimeClasses.createDefault }}
{{- $defaultRCName := .Values.runtimeClasses.defaultName }}
{{- $nfdEnabled := index .Values "node-feature-discovery" "enabled" | default false }}
{{- $externalNFDNamespace := include "kata-deploy.detectExistingNFD" . | trim -}}
{{- $useShimNodeSelectors := or $nfdEnabled (ne $externalNFDNamespace "") -}}
{{- /* Get enabled shims from structured config using null-aware logic */ -}}
{{- $disableAll := .Values.shims.disableAll | default false -}}
{{- $enabledShims := list -}}
{{- range $shimName, $shimConfig := .Values.shims -}}
@@ -81,6 +22,7 @@ scheduling:
{{- end -}}
{{- end -}}
{{- /* Define runtime class configurations with their overhead settings and node selectors */ -}}
{{- $runtimeClassConfigs := dict
"clh" (dict "memory" "130Mi" "cpu" "250m")
"cloud-hypervisor" (dict "memory" "130Mi" "cpu" "250m")
@@ -104,16 +46,69 @@ scheduling:
"remote" (dict "memory" "120Mi" "cpu" "250m")
}}
{{- /* Create RuntimeClass for each enabled shim; when default RC is requested, emit it by reusing the same template with nameOverride */ -}}
{{- $defaultShim := index .Values.defaultShim "amd64" | default (index .Values.defaultShim "arm64") | default (index .Values.defaultShim "s390x") | default (index .Values.defaultShim "ppc64le") }}
{{- /* Create RuntimeClass for each enabled shim */ -}}
{{- range $shim := $enabledShims }}
{{- $config := index $runtimeClassConfigs $shim }}
{{- $shimConfig := index $.Values.shims $shim }}
{{- if $config }}
{{ include "kata-deploy.runtimeclass" (dict "root" $ "shim" $shim "config" $config "shimConfig" $shimConfig "nameOverride" "" "useShimNodeSelectors" $useShimNodeSelectors) }}
{{- if and $createDefaultRC (not $multiInstallSuffix) (eq $shim $defaultShim) }}
{{ include "kata-deploy.runtimeclass" (dict "root" $ "shim" $shim "config" $config "shimConfig" $shimConfig "nameOverride" $defaultRCName "useShimNodeSelectors" $useShimNodeSelectors) }}
---
kind: RuntimeClass
apiVersion: node.k8s.io/v1
metadata:
{{- if $multiInstallSuffix }}
name: kata-{{ $shim }}-{{ $multiInstallSuffix }}
{{- else }}
name: kata-{{ $shim }}
{{- end }}
labels:
app.kubernetes.io/managed-by: kata-deploy
{{- if $multiInstallSuffix }}
kata-deploy/instance: {{ $multiInstallSuffix | quote }}
{{- else }}
kata-deploy/instance: "default"
{{- end }}
{{- if $multiInstallSuffix }}
handler: kata-{{ $shim }}-{{ $multiInstallSuffix }}
{{- else }}
handler: kata-{{ $shim }}
{{- end }}
overhead:
podFixed:
memory: {{ $config.memory | quote }}
cpu: {{ $config.cpu | quote }}
scheduling:
nodeSelector:
katacontainers.io/kata-runtime: "true"
{{- if and $shimConfig.runtimeClass $shimConfig.runtimeClass.nodeSelector }}
{{- range $key, $value := $shimConfig.runtimeClass.nodeSelector }}
{{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}
{{- /* Create default RuntimeClass if requested */ -}}
{{- if and $createDefaultRC (not $multiInstallSuffix) }}
{{- /* Get default shim from structured config - use amd64 as the primary reference */ -}}
{{- $defaultShim := index .Values.defaultShim "amd64" | default (index .Values.defaultShim "arm64") | default (index .Values.defaultShim "s390x") | default (index .Values.defaultShim "ppc64le") }}
{{- $defaultConfig := index $runtimeClassConfigs $defaultShim }}
{{- if and $defaultShim $defaultConfig }}
---
kind: RuntimeClass
apiVersion: node.k8s.io/v1
metadata:
name: {{ $defaultRCName }}
labels:
app.kubernetes.io/managed-by: kata-deploy
kata-deploy/instance: "default"
handler: kata-{{ $defaultShim }}
overhead:
podFixed:
memory: {{ $defaultConfig.memory | quote }}
cpu: {{ $defaultConfig.cpu | quote }}
scheduling:
nodeSelector:
katacontainers.io/kata-runtime: "true"
{{- end }}
{{- end }}
{{- end }}

View File

@@ -17,7 +17,6 @@ shims:
disableAll: true
# Enable TEE shims (qemu-snp, qemu-snp-runtime-rs, qemu-tdx, qemu-tdx-runtime-rs, qemu-se, qemu-se-runtime-rs, qemu-cca, qemu-coco-dev, qemu-coco-dev-runtime-rs)
# NFD TEE labels (snp, tdx, se) are auto-injected into RuntimeClasses when NFD is detected; no need to set nodeSelector here.
qemu-snp:
enabled: true
supportedArches:

View File

@@ -117,9 +117,8 @@ shims:
runtimeClass:
# This label is automatically added by gpu-operator. Override it
# if you want to use a different label.
# Uncomment once GPU Operator v26.3 is out
# nodeSelector:
# nvidia.com/cc.ready.state: "false"
nodeSelector:
nvidia.com/cc.ready.state: "false"
qemu-nvidia-gpu-snp:
enabled: ~
@@ -140,8 +139,7 @@ shims:
# If you don't have NFD, you need to add the snp label by other
# means to your SNP nodes.
nodeSelector:
# Uncomment once GPU Operator v26.3 is out
# nvidia.com/cc.ready.state: "true"
nvidia.com/cc.ready.state: "true"
amd.feature.node.kubernetes.io/snp: "true"
qemu-nvidia-gpu-tdx:
@@ -163,8 +161,7 @@ shims:
# If you don't have NFD, you need to add the tdx label by other
# means to your TDX nodes.
nodeSelector:
# Uncomment once GPU Operator v26.3 is out
# nvidia.com/cc.ready.state: "true"
nvidia.com/cc.ready.state: "true"
intel.feature.node.kubernetes.io/tdx: "true"
qemu-snp:

View File

@@ -23,21 +23,23 @@ pushd ${KATA_DEPLOY_DIR}
arch=$(uname -m)
[ "$arch" = "x86_64" ] && arch="amd64"
# Disable provenance and SBOM so each tag is a single image manifest. quay.io rejects
# pushing multi-arch manifest lists that include attestation manifests ("manifest invalid").
PLATFORM="linux/${arch}"
IMAGE_TAG="${REGISTRY}:kata-containers-$(git rev-parse HEAD)-${arch}"
echo "Building the image"
docker buildx build --platform "${PLATFORM}" --provenance false --sbom false \
--tag "${IMAGE_TAG}" --push .
docker build --tag ${IMAGE_TAG} .
echo "Pushing the image to the registry"
docker push ${IMAGE_TAG}
if [ -n "${TAG}" ]; then
ADDITIONAL_TAG="${REGISTRY}:${TAG}"
echo "Building the ${ADDITIONAL_TAG} image"
docker buildx build --platform "${PLATFORM}" --provenance false --sbom false \
--tag "${ADDITIONAL_TAG}" --push .
docker build --tag ${ADDITIONAL_TAG} .
echo "Pushing the image ${ADDITIONAL_TAG} to the registry"
docker push ${ADDITIONAL_TAG}
fi
popd

View File

@@ -1 +1 @@
181
180

View File

@@ -144,17 +144,15 @@ function _publish_multiarch_manifest()
_check_required_env_var "KATA_DEPLOY_IMAGE_TAGS"
_check_required_env_var "KATA_DEPLOY_REGISTRIES"
# Per-arch images are built without provenance/SBOM so each tag is a single image manifest;
# quay.io rejects pushing multi-arch manifest lists that include attestation manifests
# ("manifest invalid"), so we do not enable them for this workflow.
# imagetools create pushes to --tag by default.
for registry in "${REGISTRIES[@]}"; do
for tag in "${IMAGE_TAGS[@]}"; do
docker buildx imagetools create --tag "${registry}:${tag}" \
"${registry}:${tag}-amd64" \
"${registry}:${tag}-arm64" \
"${registry}:${tag}-s390x" \
"${registry}:${tag}-ppc64le"
docker manifest create ${registry}:${tag} \
--amend ${registry}:${tag}-amd64 \
--amend ${registry}:${tag}-arm64 \
--amend ${registry}:${tag}-s390x \
--amend ${registry}:${tag}-ppc64le
docker manifest push ${registry}:${tag}
done
done
}

View File

@@ -30,7 +30,6 @@ RUN apt-get update && \
curl \
g++ \
gcc \
gnupg \
libprotobuf-dev \
libssl-dev \
make \

View File

@@ -88,8 +88,8 @@ assets:
qemu:
description: "VMM that uses KVM"
url: "https://github.com/qemu/qemu"
version: "v10.2.1"
tag: "v10.2.1"
version: "v10.2.0"
tag: "v10.2.0"
# Do not include any non-full release versions
# Break the line *without CR or space being appended*, to appease
# yamllint, and note the deliberate ' ' at the end of the expression.
@@ -207,11 +207,11 @@ assets:
kernel:
description: "Linux kernel optimised for virtual machines"
url: "https://cdn.kernel.org/pub/linux/kernel/v6.x/"
version: "v6.18.12"
version: "v6.18.5"
nvidia:
description: "Linux kernel optimised for virtual machines"
url: "https://cdn.kernel.org/pub/linux/kernel/v6.x/"
version: "v6.18.12"
version: "v6.18.5"
kernel-arm-experimental:
description: "Linux kernel with cpu/mem hotplug support on arm64"