#!/bin/bash # # Copyright (c) 2017-2023 Intel Corporation # # SPDX-License-Identifier: Apache-2.0 # # shellcheck disable=SC2154 [[ "${DEBUG}" != "" ]] && set -o xtrace set -o errexit set -o nounset set -o pipefail set -o errtrace SCRIPT_PATH=$(dirname "$(readlink -f "$0")") # shellcheck source=/dev/null source "${SCRIPT_PATH}/../../common.bash" # runc is installed in /usr/local/sbin/ add that path export PATH="${PATH}:/usr/local/sbin" # golang is installed in /usr/local/go/bin/ add that path export PATH="${PATH}:/usr/local/go/bin" #the latest containerd from 2.0 need to set the CGROUP_DRIVER for e2e testing export CGROUP_DRIVER="" # Runtime to be used for testing KATA_HYPERVISOR="${KATA_HYPERVISOR:-qemu}" RUNTIME=${RUNTIME:-containerd-shim-kata-${KATA_HYPERVISOR}-v2} FACTORY_TEST=${FACTORY_TEST:-""} ARCH=$(uname -m) SANDBOXER=${SANDBOXER:-"podsandbox"} containerd_runtime_type="io.containerd.kata-${KATA_HYPERVISOR}.v2" containerd_shim_path="$(command -v containerd-shim || true)" #containerd config file tmp_dir=$(mktemp -t -d test-cri-containerd.XXXX) readonly tmp_dir export REPORT_DIR="${tmp_dir}" readonly CONTAINERD_CONFIG_FILE="${tmp_dir}/test-containerd-config.toml" readonly CONTAINERD_CONF_D="${tmp_dir}/conf.d" readonly CONTAINERD_SYSTEM_FRAGMENT_PREFIX="${CONTAINERD_SYSTEM_FRAGMENT_PREFIX:-ZZ-cri-integration-test-}" readonly CONTAINERD_CONFIG_FILE_TEMP="${CONTAINERD_CONFIG_FILE}.temp" readonly default_containerd_config="/etc/containerd/config.toml" readonly default_containerd_config_backup="${CONTAINERD_CONFIG_FILE}.backup" readonly kata_config="/etc/kata-containers/configuration.toml" readonly kata_config_backup="${kata_config}.backup" function ci_config() { sudo mkdir -p "$(dirname "${kata_config}")" [[ -f "${kata_config}" ]] && sudo cp "${kata_config}" "${kata_config_backup}" # shellcheck disable=SC2154 sudo cp -f "${KATA_CONFIG_PATH}" "${kata_config}" source /etc/os-release || source /usr/lib/os-release ID=${ID:-""} if [[ "${ID}" == ubuntu ]]; then # https://github.com/kata-containers/tests/issues/352 if [[ -n "${FACTORY_TEST}" ]]; then # Handle both commented and uncommented enable_template sudo sed -i -e 's/^#\?enable_template.*$/enable_template = true/g' "${kata_config}" echo "init vm template" sudo -E PATH="${PATH}" "${RUNTIME}" factory init fi fi echo "enable debug for kata-runtime" # Handle both commented and uncommented enable_debug sudo sed -i 's/^#\?enable_debug = .*$/enable_debug = true/g' "${kata_config}" } function ci_cleanup() { source /etc/os-release || source /usr/lib/os-release if [[ -n "${FACTORY_TEST}" ]]; then echo "destroy vm template" sudo -E PATH="${PATH}" "${RUNTIME}" factory destroy fi if [[ -e "${default_containerd_config_backup}" ]]; then echo "restore containerd config" sudo systemctl stop containerd sudo rm -f /etc/containerd/conf.d/"${CONTAINERD_SYSTEM_FRAGMENT_PREFIX}"*.toml sudo cp "${default_containerd_config_backup}" "${default_containerd_config}" fi if [[ -f "${kata_config_backup}" ]]; then sudo mv "${kata_config_backup}" "${kata_config}" else sudo rm "${kata_config}" fi } function generate_runtime_options_block() { local runtime="${1:?}" local pluginid="${2:?}" local runtime_config_path="${3:-}" local runtime_binary_path="${4:-}" if [[ "${runtime}" == "runc" ]]; then return 0 fi cat << EOF [plugins.${pluginid}.containerd.runtimes.${runtime}.options] ConfigPath = "${runtime_config_path}" BinaryName = "${runtime_binary_path}" EOF } function create_containerd_config() { local runtime="$1" # kata_annotations is set to 1 if caller want containerd setup with # kata annotations support. local kata_annotations=${2-0} [[ -n "${runtime}" ]] || die "need runtime to create config" local runtime_type="${containerd_runtime_type}" local runtime_config_path="${kata_config}" local containerd_runtime containerd_runtime=$(command -v "containerd-shim-${runtime}-v2") local runtime_binary_path="${containerd_runtime}" if [[ "${runtime}" == "runc" ]]; then runtime_type="io.containerd.runc.v2" runtime_config_path="" runtime_binary_path="" fi local cfg_schema cfg_schema="$(_containerd_blob_schema_version "$(PATH="${PATH}:/usr/local/bin:/usr/local/sbin" containerd config default 2>/dev/null || true)")" [[ "${cfg_schema}" =~ ^[0-9]+$ ]] || die "could not read containerd config schema from config default" info "Kata Config Path ${runtime_config_path}, Runtime Binary Name ${runtime_binary_path}" if [[ "${cfg_schema}" -ge 3 ]]; then # containerd v2.x (schema v3+): base config (imports conf.d) plus drop-in # fragments for sockets, debug, runtime and the linux shim. local pluginid=\"io.containerd.cri.v1.runtime\" local runtime_options_block="" runtime_options_block="$(generate_runtime_options_block \ "${runtime}" "${pluginid}" "${runtime_config_path}" "${runtime_binary_path}")" mkdir -p "${CONTAINERD_CONF_D}" containerd_render_config_default_with_imports "${CONTAINERD_CONFIG_FILE}" "${CONTAINERD_CONF_D}" containerd_emit_rootful_api_socket_overrides "${cfg_schema}" > "${CONTAINERD_CONF_D}/10-cri-test-api-sockets.toml" cat <<'EOF' > "${CONTAINERD_CONF_D}/20-cri-test-debug.toml" [debug] level = "debug" EOF cat << EOF > "${CONTAINERD_CONF_D}/30-cri-test-runtime.toml" [plugins] [plugins.${pluginid}] [plugins.${pluginid}.containerd] default_runtime_name = "${runtime}" [plugins.${pluginid}.containerd.runtimes.${runtime}] runtime_type = "${runtime_type}" sandboxer = "${SANDBOXER}" $( [[ "${kata_annotations}" -eq 1 ]] && \ echo 'pod_annotations = ["io.katacontainers.*"]' && \ echo ' container_annotations = ["io.katacontainers.*"]' ) ${runtime_options_block} EOF if [[ -n "${containerd_shim_path}" ]]; then cat << EOF > "${CONTAINERD_CONF_D}/40-cri-test-linux.toml" [plugins.linux] shim = "${containerd_shim_path}" EOF else rm -f "${CONTAINERD_CONF_D}/40-cri-test-linux.toml" fi else # containerd v1.x (schema v2): conf.d is not honoured the same way, so # write a single complete, self-contained config file. The v1.x default # API sockets are already root-owned, so no socket override is needed. local pluginid=\"io.containerd.grpc.v1.cri\" local runtime_options_block="" runtime_options_block="$(generate_runtime_options_block \ "${runtime}" "${pluginid}" "${runtime_config_path}" "${runtime_binary_path}")" mkdir -p "${CONTAINERD_CONF_D}" cat << EOF > "${CONTAINERD_CONFIG_FILE}" version = 2 [debug] level = "debug" [plugins] [plugins.${pluginid}] [plugins.${pluginid}.containerd] default_runtime_name = "${runtime}" [plugins.${pluginid}.containerd.runtimes.${runtime}] runtime_type = "${runtime_type}" $( [[ "${kata_annotations}" -eq 1 ]] && \ echo 'pod_annotations = ["io.katacontainers.*"]' && \ echo ' container_annotations = ["io.katacontainers.*"]' ) ${runtime_options_block} EOF fi } # Push the test containerd config to the host paths used by crictl. function deploy_cri_integration_containerd_host_config() { sudo cp "${default_containerd_config}" "${default_containerd_config_backup}" local cfg_schema cfg_schema="$(_containerd_blob_schema_version "$(PATH="${PATH}:/usr/local/bin:/usr/local/sbin" containerd config default 2>/dev/null || true)")" if [[ "${cfg_schema}" =~ ^[0-9]+$ ]] && [[ "${cfg_schema}" -ge 3 ]]; then # containerd v2.x (schema v3+): base config (imports conf.d) + drop-ins. local host_main_fragment="${REPORT_DIR}/containerd-main-for-host.toml" containerd_render_config_default_with_imports "${host_main_fragment}" "/etc/containerd/conf.d" sudo mkdir -p /etc/containerd/conf.d sudo cp "${host_main_fragment}" "${default_containerd_config}" shopt -s nullglob for f in "${CONTAINERD_CONF_D}"/*.toml; do sudo cp "${f}" "/etc/containerd/conf.d/${CONTAINERD_SYSTEM_FRAGMENT_PREFIX}$(basename "${f}")" done shopt -u nullglob else # containerd v1.x (schema v2): deploy the complete, self-contained config. sudo cp "${CONTAINERD_CONFIG_FILE}" "${default_containerd_config}" fi restart_containerd_service } function restore_cri_integration_containerd_host_config() { sudo rm -f /etc/containerd/conf.d/"${CONTAINERD_SYSTEM_FRAGMENT_PREFIX}"*.toml sudo cp "${default_containerd_config_backup}" "${default_containerd_config}" restart_containerd_service } function cleanup() { ci_cleanup [[ -d "${tmp_dir}" ]] && rm -rf "${tmp_dir}" } trap cleanup EXIT function err_report() { local log_file="${REPORT_DIR}/containerd.log" if [[ -f "${log_file}" ]]; then echo "::group::ERROR: containerd log :" echo "-------------------------------------" cat "${log_file}" echo "-------------------------------------" echo "::endgroup::" fi echo "::group::ERROR: Kata Containers logs : " echo "-------------------------------------" sudo journalctl -xe -t kata echo "-------------------------------------" echo "::endgroup::" } function check_daemon_setup() { info "containerd(cri): Check daemon works with runc" # Use podsandbox for the runc sanity check: the shim sandboxer has a known # containerd-side bug where the OCI spec is not populated before NewBundle is # called, so config.json is never written and containerd-shim-runc-v2 fails. # See https://github.com/containerd/containerd/issues/11640 # This check only verifies that containerd + runc are functional before the # real kata tests run, so the sandboxer choice doesn't matter here. local SANDBOXER="podsandbox" create_containerd_config "runc" # containerd cri-integration will modify the passed in config file. Let's # give it a temp one. cp "${CONTAINERD_CONFIG_FILE}" "${CONTAINERD_CONFIG_FILE_TEMP}" # in some distros(AlibabaCloud), there is no btrfs-devel package available, # so pass GO_BUILDTAGS="no_btrfs" to make to not use btrfs. if ! sudo -E PATH="${PATH}:/usr/local/bin" \ REPORT_DIR="${REPORT_DIR}" \ FOCUS="TestImageLoad" \ RUNTIME="" \ CONTAINERD_CONFIG_FILE="${CONTAINERD_CONFIG_FILE_TEMP}" \ make GO_BUILDTAGS="no_btrfs" -e cri-integration then echo "::group::ERROR: cri-integration sanity-check config" echo "-------------------------------------" cat "${CONTAINERD_CONFIG_FILE_TEMP}" || true echo "-------------------------------------" echo "::endgroup::" err_report return 1 fi } function testContainerStart() { # no_container_yaml set to 1 will not create container_yaml # because caller has created its own container_yaml. no_container_yaml=${1:-0} local pod_yaml=${REPORT_DIR}/pod.yaml local container_yaml=${REPORT_DIR}/container.yaml local image="busybox:latest" cat << EOF > "${pod_yaml}" metadata: name: busybox-sandbox1 namespace: default uid: busybox-sandbox1-uid EOF #TestContainerSwap has created its own container_yaml. if [[ "${no_container_yaml}" -ne 1 ]]; then cat << EOF > "${container_yaml}" metadata: name: busybox-killed-vmm namespace: default uid: busybox-killed-vmm-uid image: image: "${image}" command: - top EOF fi deploy_cri_integration_containerd_host_config sudo crictl pull "${image}" podid=$(sudo crictl --timeout=5s runp "${pod_yaml}") cid=$(sudo crictl create "${podid}" "${container_yaml}" "${pod_yaml}") sudo crictl start "${cid}" } function testContainerStop() { info "show pod ${podid}" sudo crictl --timeout=20s pods --id "${podid}" info "stop pod ${podid}" sudo crictl --timeout=20s stopp "${podid}" info "remove pod ${podid}" sudo crictl --timeout=20s rmp "${podid}" restore_cri_integration_containerd_host_config } function TestKilledVmmCleanup() { if [[ "${KATA_HYPERVISOR}" != "qemu" ]]; then info "TestKilledVmmCleanup is skipped for ${KATA_HYPERVISOR}, only QEMU is currently tested" return 0 fi info "test killed vmm cleanup" testContainerStart qemu_pid=$(pgrep -f qemu || true) info "kill qemu ${qemu_pid}" sudo kill -SIGKILL "${qemu_pid}" # sleep to let shimv2 exit sleep 1 remained=$(pgrep -f shimv2 || true) [[ -z "${remained}" ]] || die "found remaining shimv2 process ${remained}" testContainerStop info "stop containerd" } function TestContainerMemoryUpdate() { # there's no need to set true for enable_virtio_mem in dragonball # As it can support virtio-mem by default. if [[ "${KATA_HYPERVISOR}" == "dragonball" ]]; then # Currently, dragonball fails at decrease memory, just test increasing memory. # We'll re-enable it as soon as we get it to work. # Reference: https://github.com/kata-containers/kata-containers/issues/8804 # DoContainerMemoryUpdate 0 info "TestContainerMemoryUpdate skipped for dragonball" fi if [[ "${KATA_HYPERVISOR}" == "qemu-runtime-rs" ]]; then # Remove TestContainerMemoryUpdate from passing_test info "TestContainerMemoryUpdate skipped for qemu with runtime-rs" info "Please check out https://github.com/kata-containers/kata-containers/issues/9375" return elif [[ "${KATA_HYPERVISOR}" != "qemu" ]] || [[ "${ARCH}" == "ppc64le" ]]; then return fi for virtio_mem_enabled in 1 0; do # On s390x, only run the test when virtio_mem is enabled if [[ "${ARCH}" == "s390x" ]] && [[ ${virtio_mem_enabled} -eq 0 ]]; then continue fi PrepareContainerMemoryUpdate "${virtio_mem_enabled}" DoContainerMemoryUpdate "${virtio_mem_enabled}" done } function PrepareContainerMemoryUpdate() { test_virtio_mem=$1 if [[ "${test_virtio_mem}" -eq 1 ]]; then if [[ "${ARCH}" != "x86_64" ]] && [[ "${ARCH}" != "aarch64" ]] && [[ "${ARCH}" != "s390x" ]]; then return fi info "Test container memory update with virtio-mem" # Handle both commented and uncommented enable_virtio_mem sudo sed -i -e 's/^#\?enable_virtio_mem.*$/enable_virtio_mem = true/g' "${kata_config}" else info "Test container memory update without virtio-mem" # Set to false instead of commenting out sudo sed -i -e 's/^#\?enable_virtio_mem.*$/enable_virtio_mem = false/g' "${kata_config}" fi } function DoContainerMemoryUpdate() { descrease_memory=$1 # start a test container testContainerStart vm_size=$(($(sudo crictl exec "${cid}" cat /proc/meminfo | grep "MemTotal:" | awk '{print $2}')*1024)) if [[ "${vm_size}" -gt $((2*1024*1024*1024)) ]] || [[ "${vm_size}" -lt $((2*1024*1024*1024-128*1024*1024)) ]]; then testContainerStop die "The VM memory size ${vm_size} before update is not right" fi sudo crictl update --memory $((2*1024*1024*1024)) "${cid}" sleep 1 vm_size=$(($(sudo crictl exec "${cid}" cat /proc/meminfo | grep "MemTotal:" | awk '{print $2}')*1024)) if [[ "${vm_size}" -gt $((4*1024*1024*1024)) ]] || [[ "${vm_size}" -lt $((4*1024*1024*1024-128*1024*1024)) ]]; then testContainerStop die "The VM memory size ${vm_size} after increase is not right" fi if [[ "${descrease_memory}" -eq 1 ]]; then sudo crictl update --memory $((1*1024*1024*1024)) "${cid}" sleep 1 vm_size=$(($(sudo crictl exec "${cid}" cat /proc/meminfo | grep "MemTotal:" | awk '{print $2}')*1024)) if [[ "${vm_size}" -gt $((3*1024*1024*1024)) ]] || [[ "${vm_size}" -lt $((3*1024*1024*1024-128*1024*1024)) ]]; then testContainerStop die "The VM memory size ${vm_size} after decrease is not right" fi fi # stop the test container testContainerStop } function getContainerSwapInfo() { swap_size=$(($(sudo crictl exec "${cid}" cat /proc/meminfo | grep "SwapTotal:" | awk '{print $2}')*1024)) # NOTE: these below two checks only works on cgroup v1 swappiness=$(sudo crictl exec "${cid}" cat /sys/fs/cgroup/memory/memory.swappiness) swap_in_bytes=$(sudo crictl exec "${cid}" cat /sys/fs/cgroup/memory/memory.memsw.limit_in_bytes) } function TestContainerSwap() { if [[ "${KATA_HYPERVISOR}" != "qemu" ]] || [[ "${ARCH}" != "x86_64" ]]; then return fi local container_yaml=${REPORT_DIR}/container.yaml local image="busybox:latest" info "Test container with guest swap" create_containerd_config "kata-${KATA_HYPERVISOR}" 1 # Handle both commented and uncommented enable_guest_swap sudo sed -i -e 's/^#\?enable_guest_swap.*$/enable_guest_swap = true/g' "${kata_config}" # Test without swap device testContainerStart getContainerSwapInfo # Current default swappiness is 60 if [[ "${swappiness}" -ne 60 ]]; then testContainerStop die "The VM swappiness ${swappiness} without swap device is not right" fi if [[ "${swap_in_bytes}" -lt 1125899906842624 ]]; then testContainerStop die "The VM swap_in_bytes ${swap_in_bytes} without swap device is not right" fi if [[ "${swap_size}" -ne 0 ]]; then testContainerStop die "The VM swap size ${swap_size} without swap device is not right" fi testContainerStop # Test with swap device cat << EOF > "${container_yaml}" metadata: name: busybox-swap namespace: default uid: busybox-swap-uid annotations: io.katacontainers.container.resource.swappiness: "100" io.katacontainers.container.resource.swap_in_bytes: "1610612736" linux: resources: memory_limit_in_bytes: 1073741824 image: image: "${image}" command: - top EOF testContainerStart 1 getContainerSwapInfo testContainerStop if [[ "${swappiness}" -ne 100 ]]; then die "The VM swappiness ${swappiness} with swap device is not right" fi if [[ "${swap_in_bytes}" -ne 1610612736 ]]; then die "The VM swap_in_bytes ${swap_in_bytes} with swap device is not right" fi if [[ "${swap_size}" -ne 536870912 ]]; then die "The VM swap size ${swap_size} with swap device is not right" fi # Test without swap_in_bytes cat << EOF > "${container_yaml}" metadata: name: busybox-swap namespace: default uid: busybox-swap-uid annotations: io.katacontainers.container.resource.swappiness: "100" linux: resources: memory_limit_in_bytes: 1073741824 image: image: "${image}" command: - top EOF testContainerStart 1 getContainerSwapInfo testContainerStop if [[ "${swappiness}" -ne 100 ]]; then die "The VM swappiness ${swappiness} without swap_in_bytes is not right" fi # swap_in_bytes is not set, it should be a value that bigger than 1125899906842624 if [[ "${swap_in_bytes}" -lt 1125899906842624 ]]; then die "The VM swap_in_bytes ${swap_in_bytes} without swap_in_bytes is not right" fi if [[ "${swap_size}" -ne 1073741824 ]]; then die "The VM swap size ${swap_size} without swap_in_bytes is not right" fi # Test without memory_limit_in_bytes cat << EOF > "${container_yaml}" metadata: name: busybox-swap namespace: default uid: busybox-swap-uid annotations: io.katacontainers.container.resource.swappiness: "100" image: image: "${image}" command: - top EOF testContainerStart 1 getContainerSwapInfo testContainerStop if [[ "${swappiness}" -ne 100 ]]; then die "The VM swappiness ${swappiness} without memory_limit_in_bytes is not right" fi # swap_in_bytes is not set, it should be a value that bigger than 1125899906842624 if [[ "${swap_in_bytes}" -lt 1125899906842624 ]]; then die "The VM swap_in_bytes ${swap_in_bytes} without memory_limit_in_bytes is not right" fi if [[ "${swap_size}" -ne 2147483648 ]]; then die "The VM swap size ${swap_size} without memory_limit_in_bytes is not right" fi create_containerd_config "kata-${KATA_HYPERVISOR}" } # k8s may restart docker which will impact on containerd stop function stop_containerd() { local tmp tmp=$(pgrep kubelet || true) [[ -n "${tmp}" ]] && sudo kubeadm reset -f sudo systemctl stop containerd } function mountLoopDevice() { local loop_file="$1" if [[ -e "${loop_file}" ]]; then sudo rm -f "${loop_file}" info "${loop_file} was removed" fi sudo dd if=/dev/zero of="${loop_file}" bs=100M count=2 sudo mkfs.ext4 "${loop_file}" sudo losetup -fP "${loop_file}" local loinfo loinfo=$(sudo losetup -a | grep "${loop_file}" || true) local device device=$(echo "${loinfo}" | awk -F'[: ]' '{print $1}') echo "${device}" } function startDeviceCgroupContainers() { local pod_yaml=${REPORT_DIR}/device-cgroup-pod.yaml local container1_yaml=${REPORT_DIR}/device-cgroup-container1.yaml local container2_yaml=${REPORT_DIR}/device-cgroup-container2.yaml local image="busybox:latest" cat > "${pod_yaml}" < "${container1_yaml}" < "${container2_yaml}" <