mirror of
https://github.com/aljazceru/kata-containers.git
synced 2025-12-18 14:54:19 +01:00
Change io/ioutil to io/os packages because io/ioutil package is deprecated from 1.16: Discard => io.Discard NopCloser => io.NopCloser ReadAll => io.ReadAll ReadDir => os.ReadDir ReadFile => os.ReadFile TempDir => os.MkdirTemp TempFile => os.CreateTemp WriteFile => os.WriteFile Details: https://go.dev/doc/go1.16#ioutil Fixes: #3265 Signed-off-by: bin <bin@hyper.sh>
1016 lines
28 KiB
Go
1016 lines
28 KiB
Go
// Copyright (c) 2017 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
package oci
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/cri-o/cri-o/pkg/annotations"
|
|
crioAnnotations "github.com/cri-o/cri-o/pkg/annotations"
|
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/stretchr/testify/assert"
|
|
"golang.org/x/sys/unix"
|
|
|
|
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
|
|
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/config"
|
|
vcAnnotations "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
|
|
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/compatoci"
|
|
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
|
|
)
|
|
|
|
const (
|
|
containerID = "virtc-oci-test"
|
|
fileMode = os.FileMode(0640)
|
|
dirMode = os.FileMode(0750)
|
|
)
|
|
|
|
var (
|
|
tempRoot = ""
|
|
tempBundlePath = ""
|
|
consolePath = ""
|
|
)
|
|
|
|
func createConfig(fileName string, fileData string) (string, error) {
|
|
configPath := path.Join(tempBundlePath, fileName)
|
|
|
|
err := os.WriteFile(configPath, []byte(fileData), fileMode)
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "Unable to create config file %s %v\n", configPath, err)
|
|
return "", err
|
|
}
|
|
|
|
return configPath, nil
|
|
}
|
|
|
|
func TestMinimalSandboxConfig(t *testing.T) {
|
|
assert := assert.New(t)
|
|
configPath, err := createConfig("config.json", minimalConfig)
|
|
assert.NoError(err)
|
|
|
|
savedFunc := config.GetHostPathFunc
|
|
|
|
// Simply assign container path to host path for device.
|
|
config.GetHostPathFunc = func(devInfo config.DeviceInfo, vhostUserStoreEnabled bool,
|
|
vhostUserStorePath string) (string, error) {
|
|
return devInfo.ContainerPath, nil
|
|
}
|
|
|
|
defer func() {
|
|
config.GetHostPathFunc = savedFunc
|
|
}()
|
|
|
|
runtimeConfig := RuntimeConfig{
|
|
HypervisorType: vc.QemuHypervisor,
|
|
Console: consolePath,
|
|
}
|
|
|
|
capList := []string{"CAP_AUDIT_WRITE", "CAP_KILL", "CAP_NET_BIND_SERVICE"}
|
|
|
|
expectedCmd := types.Cmd{
|
|
Args: []string{"sh"},
|
|
Envs: []types.EnvVar{
|
|
{
|
|
Var: "PATH",
|
|
Value: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
|
},
|
|
{
|
|
Var: "TERM",
|
|
Value: "xterm",
|
|
},
|
|
},
|
|
WorkDir: "/",
|
|
User: "0",
|
|
PrimaryGroup: "0",
|
|
SupplementaryGroups: []string{"10", "29"},
|
|
Interactive: true,
|
|
Console: consolePath,
|
|
NoNewPrivileges: true,
|
|
Capabilities: &specs.LinuxCapabilities{
|
|
Bounding: capList,
|
|
Effective: capList,
|
|
Inheritable: capList,
|
|
Permitted: capList,
|
|
Ambient: capList,
|
|
},
|
|
}
|
|
|
|
expectedMounts := []vc.Mount{
|
|
{
|
|
Source: "proc",
|
|
Destination: "/proc",
|
|
Type: "proc",
|
|
Options: nil,
|
|
HostPath: "",
|
|
},
|
|
{
|
|
Source: "tmpfs",
|
|
Destination: "/dev",
|
|
Type: "tmpfs",
|
|
Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
|
|
HostPath: "",
|
|
},
|
|
{
|
|
Source: "devpts",
|
|
Destination: "/dev/pts",
|
|
Type: "devpts",
|
|
Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
|
|
HostPath: "",
|
|
},
|
|
}
|
|
|
|
spec, err := compatoci.ParseConfigJSON(tempBundlePath)
|
|
assert.NoError(err)
|
|
|
|
devInfo := config.DeviceInfo{
|
|
ContainerPath: "/dev/vfio/17",
|
|
Major: 242,
|
|
Minor: 0,
|
|
DevType: "c",
|
|
UID: 0,
|
|
GID: 0,
|
|
}
|
|
|
|
expectedDeviceInfo := []config.DeviceInfo{
|
|
devInfo,
|
|
}
|
|
|
|
expectedContainerConfig := vc.ContainerConfig{
|
|
ID: containerID,
|
|
RootFs: vc.RootFs{Target: path.Join(tempBundlePath, "rootfs"), Mounted: true},
|
|
ReadonlyRootfs: true,
|
|
Cmd: expectedCmd,
|
|
Annotations: map[string]string{
|
|
vcAnnotations.BundlePathKey: tempBundlePath,
|
|
vcAnnotations.ContainerTypeKey: string(vc.PodSandbox),
|
|
},
|
|
Mounts: expectedMounts,
|
|
DeviceInfos: expectedDeviceInfo,
|
|
Resources: specs.LinuxResources{Devices: []specs.LinuxDeviceCgroup{
|
|
{Allow: false, Type: "", Major: (*int64)(nil), Minor: (*int64)(nil), Access: "rwm"},
|
|
}},
|
|
CustomSpec: &spec,
|
|
}
|
|
|
|
expectedNetworkConfig := vc.NetworkConfig{}
|
|
|
|
expectedSandboxConfig := vc.SandboxConfig{
|
|
ID: containerID,
|
|
Hostname: "testHostname",
|
|
|
|
HypervisorType: vc.QemuHypervisor,
|
|
|
|
NetworkConfig: expectedNetworkConfig,
|
|
|
|
Containers: []vc.ContainerConfig{expectedContainerConfig},
|
|
|
|
Annotations: map[string]string{
|
|
vcAnnotations.BundlePathKey: tempBundlePath,
|
|
},
|
|
|
|
SystemdCgroup: true,
|
|
}
|
|
|
|
sandboxConfig, err := SandboxConfig(spec, runtimeConfig, tempBundlePath, containerID, consolePath, false, true)
|
|
assert.NoError(err)
|
|
|
|
assert.Exactly(sandboxConfig, expectedSandboxConfig)
|
|
assert.NoError(os.Remove(configPath))
|
|
}
|
|
|
|
func testContainerTypeSuccessful(t *testing.T, ociSpec specs.Spec, expected vc.ContainerType) {
|
|
containerType, err := ContainerType(ociSpec)
|
|
assert := assert.New(t)
|
|
|
|
assert.NoError(err)
|
|
assert.Equal(containerType, expected)
|
|
}
|
|
|
|
func TestContainerTypePodSandbox(t *testing.T) {
|
|
var ociSpec specs.Spec
|
|
|
|
ociSpec.Annotations = map[string]string{
|
|
annotations.ContainerType: annotations.ContainerTypeSandbox,
|
|
}
|
|
|
|
testContainerTypeSuccessful(t, ociSpec, vc.PodSandbox)
|
|
}
|
|
|
|
func TestContainerTypePodContainer(t *testing.T) {
|
|
var ociSpec specs.Spec
|
|
|
|
ociSpec.Annotations = map[string]string{
|
|
annotations.ContainerType: annotations.ContainerTypeContainer,
|
|
}
|
|
|
|
testContainerTypeSuccessful(t, ociSpec, vc.PodContainer)
|
|
}
|
|
|
|
func TestContainerTypePodSandboxEmptyAnnotation(t *testing.T) {
|
|
testContainerTypeSuccessful(t, specs.Spec{}, vc.PodSandbox)
|
|
}
|
|
|
|
func TestContainerTypeFailure(t *testing.T) {
|
|
var ociSpec specs.Spec
|
|
expected := vc.UnknownContainerType
|
|
unknownType := "unknown_type"
|
|
assert := assert.New(t)
|
|
|
|
ociSpec.Annotations = map[string]string{
|
|
annotations.ContainerType: unknownType,
|
|
}
|
|
|
|
containerType, err := ContainerType(ociSpec)
|
|
assert.Error(err)
|
|
assert.Equal(containerType, expected)
|
|
}
|
|
|
|
func TestSandboxIDSuccessful(t *testing.T) {
|
|
var ociSpec specs.Spec
|
|
testSandboxID := "testSandboxID"
|
|
assert := assert.New(t)
|
|
|
|
ociSpec.Annotations = map[string]string{
|
|
annotations.SandboxID: testSandboxID,
|
|
}
|
|
|
|
sandboxID, err := SandboxID(ociSpec)
|
|
assert.NoError(err)
|
|
assert.Equal(sandboxID, testSandboxID)
|
|
}
|
|
|
|
func TestSandboxIDFailure(t *testing.T) {
|
|
var ociSpec specs.Spec
|
|
assert := assert.New(t)
|
|
|
|
sandboxID, err := SandboxID(ociSpec)
|
|
assert.Error(err)
|
|
assert.Empty(sandboxID)
|
|
}
|
|
|
|
func TestAddKernelParamValid(t *testing.T) {
|
|
var config RuntimeConfig
|
|
assert := assert.New(t)
|
|
|
|
expected := []vc.Param{
|
|
{
|
|
Key: "foo",
|
|
Value: "bar",
|
|
},
|
|
}
|
|
|
|
err := config.AddKernelParam(expected[0])
|
|
assert.NoError(err)
|
|
assert.Exactly(config.HypervisorConfig.KernelParams, expected)
|
|
}
|
|
|
|
func TestAddKernelParamInvalid(t *testing.T) {
|
|
var config RuntimeConfig
|
|
|
|
invalid := []vc.Param{
|
|
{
|
|
Key: "",
|
|
Value: "bar",
|
|
},
|
|
}
|
|
|
|
err := config.AddKernelParam(invalid[0])
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestDeviceTypeFailure(t *testing.T) {
|
|
var ociSpec specs.Spec
|
|
|
|
invalidDeviceType := "f"
|
|
ociSpec.Linux = &specs.Linux{}
|
|
ociSpec.Linux.Devices = []specs.LinuxDevice{
|
|
{
|
|
Path: "/dev/vfio",
|
|
Type: invalidDeviceType,
|
|
},
|
|
}
|
|
|
|
_, err := containerDeviceInfos(ociSpec)
|
|
assert.NotNil(t, err, "This test should fail as device type [%s] is invalid ", invalidDeviceType)
|
|
}
|
|
|
|
func TestContains(t *testing.T) {
|
|
s := []string{"char", "block", "pipe"}
|
|
|
|
assert.True(t, contains(s, "char"))
|
|
assert.True(t, contains(s, "pipe"))
|
|
assert.False(t, contains(s, "chara"))
|
|
assert.False(t, contains(s, "socket"))
|
|
}
|
|
|
|
func TestDevicePathEmpty(t *testing.T) {
|
|
var ociSpec specs.Spec
|
|
|
|
ociSpec.Linux = &specs.Linux{}
|
|
ociSpec.Linux.Devices = []specs.LinuxDevice{
|
|
{
|
|
Type: "c",
|
|
Major: 252,
|
|
Minor: 1,
|
|
},
|
|
}
|
|
|
|
_, err := containerDeviceInfos(ociSpec)
|
|
assert.NotNil(t, err, "This test should fail as path cannot be empty for device")
|
|
}
|
|
|
|
func TestGetShmSize(t *testing.T) {
|
|
containerConfig := vc.ContainerConfig{
|
|
Mounts: []vc.Mount{},
|
|
}
|
|
|
|
shmSize, err := getShmSize(containerConfig)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, shmSize, uint64(0))
|
|
|
|
m := vc.Mount{
|
|
Source: "/dev/shm",
|
|
Destination: "/dev/shm",
|
|
Type: "tmpfs",
|
|
Options: nil,
|
|
}
|
|
|
|
containerConfig.Mounts = append(containerConfig.Mounts, m)
|
|
shmSize, err = getShmSize(containerConfig)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, shmSize, uint64(vc.DefaultShmSize))
|
|
|
|
containerConfig.Mounts[0].Source = "/var/run/shared/shm"
|
|
containerConfig.Mounts[0].Type = "bind"
|
|
_, err = getShmSize(containerConfig)
|
|
assert.NotNil(t, err)
|
|
}
|
|
|
|
func TestGetShmSizeBindMounted(t *testing.T) {
|
|
if os.Geteuid() != 0 {
|
|
t.Skip("Test disabled as requires root privileges")
|
|
}
|
|
|
|
dir, err := os.MkdirTemp("", "")
|
|
assert.Nil(t, err)
|
|
defer os.RemoveAll(dir)
|
|
|
|
shmPath := filepath.Join(dir, "shm")
|
|
err = os.Mkdir(shmPath, 0700)
|
|
assert.Nil(t, err)
|
|
|
|
size := 8192
|
|
if runtime.GOARCH == "ppc64le" {
|
|
// PAGE_SIZE on ppc64le is 65536
|
|
size = 65536
|
|
}
|
|
|
|
shmOptions := "mode=1777,size=" + strconv.Itoa(size)
|
|
err = unix.Mount("shm", shmPath, "tmpfs", unix.MS_NOEXEC|unix.MS_NOSUID|unix.MS_NODEV, shmOptions)
|
|
assert.Nil(t, err)
|
|
|
|
defer unix.Unmount(shmPath, 0)
|
|
|
|
containerConfig := vc.ContainerConfig{
|
|
Mounts: []vc.Mount{
|
|
{
|
|
Source: shmPath,
|
|
Destination: "/dev/shm",
|
|
Type: "bind",
|
|
Options: nil,
|
|
},
|
|
},
|
|
}
|
|
|
|
shmSize, err := getShmSize(containerConfig)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, shmSize, uint64(size))
|
|
}
|
|
|
|
func TestMain(m *testing.M) {
|
|
var err error
|
|
tempRoot, err = os.MkdirTemp("", "virtc-")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
tempBundlePath = filepath.Join(tempRoot, "ocibundle")
|
|
consolePath = filepath.Join(tempRoot, "console")
|
|
|
|
/* Create temp bundle directory if necessary */
|
|
err = os.MkdirAll(tempBundlePath, dirMode)
|
|
if err != nil {
|
|
fmt.Printf("Unable to create %s %v\n", tempBundlePath, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
ret := m.Run()
|
|
|
|
os.RemoveAll(tempRoot)
|
|
|
|
os.Exit(ret)
|
|
}
|
|
|
|
func TestAddAssetAnnotations(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
tmpdir, err := os.MkdirTemp("", "")
|
|
assert.NoError(err)
|
|
defer os.RemoveAll(tmpdir)
|
|
|
|
// Create a pretend asset file
|
|
// (required since the existence of binary asset annotations is verified).
|
|
fakeAssetFile := filepath.Join(tmpdir, "fake-binary")
|
|
|
|
err = os.WriteFile(fakeAssetFile, []byte(""), fileMode)
|
|
assert.NoError(err)
|
|
|
|
expectedAnnotations := map[string]string{
|
|
vcAnnotations.FirmwarePath: fakeAssetFile,
|
|
vcAnnotations.FirmwareHash: "ffff",
|
|
|
|
vcAnnotations.HypervisorPath: fakeAssetFile,
|
|
vcAnnotations.HypervisorHash: "bbbbb",
|
|
|
|
vcAnnotations.HypervisorCtlPath: fakeAssetFile,
|
|
vcAnnotations.HypervisorCtlHash: "cc",
|
|
|
|
vcAnnotations.ImagePath: fakeAssetFile,
|
|
vcAnnotations.ImageHash: "52ss2550983",
|
|
|
|
vcAnnotations.InitrdPath: fakeAssetFile,
|
|
vcAnnotations.InitrdHash: "aaaa",
|
|
|
|
vcAnnotations.JailerPath: fakeAssetFile,
|
|
vcAnnotations.JailerHash: "dddd",
|
|
|
|
vcAnnotations.KernelPath: fakeAssetFile,
|
|
vcAnnotations.KernelHash: "3l2353we871g",
|
|
}
|
|
|
|
config := vc.SandboxConfig{
|
|
Annotations: make(map[string]string),
|
|
}
|
|
|
|
ocispec := specs.Spec{
|
|
Annotations: expectedAnnotations,
|
|
}
|
|
|
|
runtimeConfig := RuntimeConfig{
|
|
HypervisorType: vc.QemuHypervisor,
|
|
Console: consolePath,
|
|
}
|
|
|
|
// Try annotations without enabling them first
|
|
err = addAnnotations(ocispec, &config, runtimeConfig)
|
|
|
|
assert.Error(err)
|
|
assert.Exactly(map[string]string{}, config.Annotations)
|
|
|
|
// Check if annotation not enabled correctly
|
|
runtimeConfig.HypervisorConfig.EnableAnnotations = []string{"nonexistent"}
|
|
err = addAnnotations(ocispec, &config, runtimeConfig)
|
|
|
|
assert.Error(err)
|
|
|
|
// Ensure it fails if all annotations enabled but path lists are not set
|
|
runtimeConfig.HypervisorConfig.EnableAnnotations = []string{".*"}
|
|
err = addAnnotations(ocispec, &config, runtimeConfig)
|
|
assert.Error(err)
|
|
|
|
tmpdirGlob := tmpdir + "/*"
|
|
|
|
// Check that it works if all path lists are enabled
|
|
runtimeConfig.HypervisorConfig.HypervisorPathList = []string{tmpdirGlob}
|
|
runtimeConfig.HypervisorConfig.JailerPathList = []string{tmpdirGlob}
|
|
runtimeConfig.HypervisorConfig.HypervisorCtlPathList = []string{tmpdirGlob}
|
|
|
|
err = addAnnotations(ocispec, &config, runtimeConfig)
|
|
assert.NoError(err)
|
|
assert.Exactly(expectedAnnotations, config.Annotations)
|
|
}
|
|
|
|
func TestAddAgentAnnotations(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
config := vc.SandboxConfig{
|
|
Annotations: make(map[string]string),
|
|
AgentConfig: vc.KataAgentConfig{},
|
|
}
|
|
|
|
ocispec := specs.Spec{
|
|
Annotations: make(map[string]string),
|
|
}
|
|
|
|
expectedAgentConfig := vc.KataAgentConfig{
|
|
KernelModules: []string{
|
|
"e1000e InterruptThrottleRate=3000,3000,3000 EEE=1",
|
|
"i915 enable_ppgtt=0",
|
|
},
|
|
ContainerPipeSize: 1024,
|
|
}
|
|
|
|
runtimeConfig := RuntimeConfig{
|
|
HypervisorType: vc.QemuHypervisor,
|
|
Console: consolePath,
|
|
}
|
|
|
|
ocispec.Annotations[vcAnnotations.KernelModules] = strings.Join(expectedAgentConfig.KernelModules, KernelModulesSeparator)
|
|
ocispec.Annotations[vcAnnotations.AgentContainerPipeSize] = "1024"
|
|
addAnnotations(ocispec, &config, runtimeConfig)
|
|
assert.Exactly(expectedAgentConfig, config.AgentConfig)
|
|
}
|
|
|
|
func TestContainerPipeSizeAnnotation(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
config := vc.SandboxConfig{
|
|
Annotations: make(map[string]string),
|
|
AgentConfig: vc.KataAgentConfig{},
|
|
}
|
|
|
|
ocispec := specs.Spec{
|
|
Annotations: make(map[string]string),
|
|
}
|
|
|
|
expectedAgentConfig := vc.KataAgentConfig{
|
|
ContainerPipeSize: 0,
|
|
}
|
|
|
|
runtimeConfig := RuntimeConfig{
|
|
HypervisorType: vc.QemuHypervisor,
|
|
Console: consolePath,
|
|
}
|
|
|
|
ocispec.Annotations[vcAnnotations.AgentContainerPipeSize] = "foo"
|
|
err := addAnnotations(ocispec, &config, runtimeConfig)
|
|
assert.Error(err)
|
|
assert.Exactly(expectedAgentConfig, config.AgentConfig)
|
|
}
|
|
|
|
func TestAddHypervisorAnnotations(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
config := vc.SandboxConfig{
|
|
Annotations: make(map[string]string),
|
|
}
|
|
|
|
ocispec := specs.Spec{
|
|
Annotations: make(map[string]string),
|
|
}
|
|
|
|
expectedHyperConfig := vc.HypervisorConfig{
|
|
KernelParams: []vc.Param{
|
|
{
|
|
Key: "vsyscall",
|
|
Value: "emulate",
|
|
},
|
|
{
|
|
Key: "iommu",
|
|
Value: "on",
|
|
},
|
|
},
|
|
}
|
|
|
|
runtimeConfig := RuntimeConfig{
|
|
HypervisorType: vc.QemuHypervisor,
|
|
Console: consolePath,
|
|
}
|
|
runtimeConfig.HypervisorConfig.EnableAnnotations = []string{".*"}
|
|
runtimeConfig.HypervisorConfig.FileBackedMemRootList = []string{"/dev/shm*"}
|
|
runtimeConfig.HypervisorConfig.VirtioFSDaemonList = []string{"/bin/*ls*"}
|
|
|
|
ocispec.Annotations[vcAnnotations.KernelParams] = "vsyscall=emulate iommu=on"
|
|
addHypervisorConfigOverrides(ocispec, &config, runtimeConfig)
|
|
assert.Exactly(expectedHyperConfig, config.HypervisorConfig)
|
|
|
|
ocispec.Annotations[vcAnnotations.DefaultVCPUs] = "1"
|
|
ocispec.Annotations[vcAnnotations.DefaultMaxVCPUs] = "1"
|
|
ocispec.Annotations[vcAnnotations.DefaultMemory] = "1024"
|
|
ocispec.Annotations[vcAnnotations.MemSlots] = "20"
|
|
ocispec.Annotations[vcAnnotations.MemOffset] = "512"
|
|
ocispec.Annotations[vcAnnotations.VirtioMem] = "true"
|
|
ocispec.Annotations[vcAnnotations.MemPrealloc] = "true"
|
|
ocispec.Annotations[vcAnnotations.EnableSwap] = "true"
|
|
ocispec.Annotations[vcAnnotations.FileBackedMemRootDir] = "/dev/shm"
|
|
ocispec.Annotations[vcAnnotations.HugePages] = "true"
|
|
ocispec.Annotations[vcAnnotations.IOMMU] = "true"
|
|
ocispec.Annotations[vcAnnotations.BlockDeviceDriver] = "virtio-scsi"
|
|
ocispec.Annotations[vcAnnotations.DisableBlockDeviceUse] = "true"
|
|
ocispec.Annotations[vcAnnotations.EnableIOThreads] = "true"
|
|
ocispec.Annotations[vcAnnotations.BlockDeviceCacheSet] = "true"
|
|
ocispec.Annotations[vcAnnotations.BlockDeviceCacheDirect] = "true"
|
|
ocispec.Annotations[vcAnnotations.BlockDeviceCacheNoflush] = "true"
|
|
ocispec.Annotations[vcAnnotations.SharedFS] = "virtio-fs"
|
|
ocispec.Annotations[vcAnnotations.VirtioFSDaemon] = "/bin/false"
|
|
ocispec.Annotations[vcAnnotations.VirtioFSCache] = "/home/cache"
|
|
ocispec.Annotations[vcAnnotations.VirtioFSExtraArgs] = "[ \"arg0\", \"arg1\" ]"
|
|
ocispec.Annotations[vcAnnotations.Msize9p] = "512"
|
|
ocispec.Annotations[vcAnnotations.MachineType] = "q35"
|
|
ocispec.Annotations[vcAnnotations.MachineAccelerators] = "nofw"
|
|
ocispec.Annotations[vcAnnotations.CPUFeatures] = "pmu=off"
|
|
ocispec.Annotations[vcAnnotations.DisableVhostNet] = "true"
|
|
ocispec.Annotations[vcAnnotations.GuestHookPath] = "/usr/bin/"
|
|
ocispec.Annotations[vcAnnotations.DisableImageNvdimm] = "true"
|
|
ocispec.Annotations[vcAnnotations.HotplugVFIOOnRootBus] = "true"
|
|
ocispec.Annotations[vcAnnotations.PCIeRootPort] = "2"
|
|
ocispec.Annotations[vcAnnotations.IOMMUPlatform] = "true"
|
|
ocispec.Annotations[vcAnnotations.SGXEPC] = "64Mi"
|
|
// 10Mbit
|
|
ocispec.Annotations[vcAnnotations.RxRateLimiterMaxRate] = "10000000"
|
|
ocispec.Annotations[vcAnnotations.TxRateLimiterMaxRate] = "10000000"
|
|
|
|
addAnnotations(ocispec, &config, runtimeConfig)
|
|
assert.Equal(config.HypervisorConfig.NumVCPUs, uint32(1))
|
|
assert.Equal(config.HypervisorConfig.DefaultMaxVCPUs, uint32(1))
|
|
assert.Equal(config.HypervisorConfig.MemorySize, uint32(1024))
|
|
assert.Equal(config.HypervisorConfig.MemSlots, uint32(20))
|
|
assert.Equal(config.HypervisorConfig.MemOffset, uint64(512))
|
|
assert.Equal(config.HypervisorConfig.VirtioMem, true)
|
|
assert.Equal(config.HypervisorConfig.MemPrealloc, true)
|
|
assert.Equal(config.HypervisorConfig.Mlock, false)
|
|
assert.Equal(config.HypervisorConfig.FileBackedMemRootDir, "/dev/shm")
|
|
assert.Equal(config.HypervisorConfig.HugePages, true)
|
|
assert.Equal(config.HypervisorConfig.IOMMU, true)
|
|
assert.Equal(config.HypervisorConfig.BlockDeviceDriver, "virtio-scsi")
|
|
assert.Equal(config.HypervisorConfig.DisableBlockDeviceUse, true)
|
|
assert.Equal(config.HypervisorConfig.EnableIOThreads, true)
|
|
assert.Equal(config.HypervisorConfig.BlockDeviceCacheSet, true)
|
|
assert.Equal(config.HypervisorConfig.BlockDeviceCacheDirect, true)
|
|
assert.Equal(config.HypervisorConfig.BlockDeviceCacheNoflush, true)
|
|
assert.Equal(config.HypervisorConfig.SharedFS, "virtio-fs")
|
|
assert.Equal(config.HypervisorConfig.VirtioFSDaemon, "/bin/false")
|
|
assert.Equal(config.HypervisorConfig.VirtioFSCache, "/home/cache")
|
|
assert.ElementsMatch(config.HypervisorConfig.VirtioFSExtraArgs, [2]string{"arg0", "arg1"})
|
|
assert.Equal(config.HypervisorConfig.Msize9p, uint32(512))
|
|
assert.Equal(config.HypervisorConfig.HypervisorMachineType, "q35")
|
|
assert.Equal(config.HypervisorConfig.MachineAccelerators, "nofw")
|
|
assert.Equal(config.HypervisorConfig.CPUFeatures, "pmu=off")
|
|
assert.Equal(config.HypervisorConfig.DisableVhostNet, true)
|
|
assert.Equal(config.HypervisorConfig.GuestHookPath, "/usr/bin/")
|
|
assert.Equal(config.HypervisorConfig.DisableImageNvdimm, true)
|
|
assert.Equal(config.HypervisorConfig.HotplugVFIOOnRootBus, true)
|
|
assert.Equal(config.HypervisorConfig.PCIeRootPort, uint32(2))
|
|
assert.Equal(config.HypervisorConfig.IOMMUPlatform, true)
|
|
assert.Equal(config.HypervisorConfig.SGXEPCSize, int64(67108864))
|
|
assert.Equal(config.HypervisorConfig.RxRateLimiterMaxRate, uint64(10000000))
|
|
assert.Equal(config.HypervisorConfig.TxRateLimiterMaxRate, uint64(10000000))
|
|
|
|
// In case an absurd large value is provided, the config value if not over-ridden
|
|
ocispec.Annotations[vcAnnotations.DefaultVCPUs] = "655536"
|
|
err := addAnnotations(ocispec, &config, runtimeConfig)
|
|
assert.Error(err)
|
|
|
|
ocispec.Annotations[vcAnnotations.DefaultVCPUs] = "-1"
|
|
err = addAnnotations(ocispec, &config, runtimeConfig)
|
|
assert.Error(err)
|
|
|
|
ocispec.Annotations[vcAnnotations.DefaultVCPUs] = "1"
|
|
ocispec.Annotations[vcAnnotations.DefaultMaxVCPUs] = "-1"
|
|
err = addAnnotations(ocispec, &config, runtimeConfig)
|
|
assert.Error(err)
|
|
|
|
ocispec.Annotations[vcAnnotations.DefaultMaxVCPUs] = "1"
|
|
ocispec.Annotations[vcAnnotations.DefaultMemory] = fmt.Sprintf("%d", vc.MinHypervisorMemory+1)
|
|
assert.Error(err)
|
|
}
|
|
|
|
func TestAddProtectedHypervisorAnnotations(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
config := vc.SandboxConfig{
|
|
Annotations: make(map[string]string),
|
|
}
|
|
|
|
ocispec := specs.Spec{
|
|
Annotations: make(map[string]string),
|
|
}
|
|
|
|
runtimeConfig := RuntimeConfig{
|
|
HypervisorType: vc.QemuHypervisor,
|
|
Console: consolePath,
|
|
}
|
|
ocispec.Annotations[vcAnnotations.KernelParams] = "vsyscall=emulate iommu=on"
|
|
err := addAnnotations(ocispec, &config, runtimeConfig)
|
|
assert.Error(err)
|
|
assert.Exactly(vc.HypervisorConfig{}, config.HypervisorConfig)
|
|
|
|
// Enable annotations
|
|
runtimeConfig.HypervisorConfig.EnableAnnotations = []string{".*"}
|
|
|
|
ocispec.Annotations[vcAnnotations.FileBackedMemRootDir] = "/dev/shm"
|
|
ocispec.Annotations[vcAnnotations.VirtioFSDaemon] = "/bin/false"
|
|
ocispec.Annotations[vcAnnotations.EntropySource] = "/dev/urandom"
|
|
|
|
config.HypervisorConfig.FileBackedMemRootDir = "do-not-touch"
|
|
config.HypervisorConfig.VirtioFSDaemon = "dangerous-daemon"
|
|
config.HypervisorConfig.EntropySource = "truly-random"
|
|
|
|
err = addAnnotations(ocispec, &config, runtimeConfig)
|
|
assert.Error(err)
|
|
assert.Equal(config.HypervisorConfig.FileBackedMemRootDir, "do-not-touch")
|
|
assert.Equal(config.HypervisorConfig.VirtioFSDaemon, "dangerous-daemon")
|
|
assert.Equal(config.HypervisorConfig.EntropySource, "truly-random")
|
|
|
|
// Now enable them and check again
|
|
runtimeConfig.HypervisorConfig.FileBackedMemRootList = []string{"/dev/*m"}
|
|
runtimeConfig.HypervisorConfig.VirtioFSDaemonList = []string{"/bin/*ls*"}
|
|
runtimeConfig.HypervisorConfig.EntropySourceList = []string{"/dev/*random*"}
|
|
err = addAnnotations(ocispec, &config, runtimeConfig)
|
|
assert.NoError(err)
|
|
assert.Equal(config.HypervisorConfig.FileBackedMemRootDir, "/dev/shm")
|
|
assert.Equal(config.HypervisorConfig.VirtioFSDaemon, "/bin/false")
|
|
assert.Equal(config.HypervisorConfig.EntropySource, "/dev/urandom")
|
|
|
|
// In case an absurd large value is provided, the config value if not over-ridden
|
|
ocispec.Annotations[vcAnnotations.DefaultVCPUs] = "655536"
|
|
err = addAnnotations(ocispec, &config, runtimeConfig)
|
|
assert.Error(err)
|
|
|
|
ocispec.Annotations[vcAnnotations.DefaultVCPUs] = "-1"
|
|
err = addAnnotations(ocispec, &config, runtimeConfig)
|
|
assert.Error(err)
|
|
|
|
ocispec.Annotations[vcAnnotations.DefaultVCPUs] = "1"
|
|
ocispec.Annotations[vcAnnotations.DefaultMaxVCPUs] = "-1"
|
|
err = addAnnotations(ocispec, &config, runtimeConfig)
|
|
assert.Error(err)
|
|
|
|
ocispec.Annotations[vcAnnotations.DefaultMaxVCPUs] = "1"
|
|
ocispec.Annotations[vcAnnotations.DefaultMemory] = fmt.Sprintf("%d", vc.MinHypervisorMemory+1)
|
|
assert.Error(err)
|
|
}
|
|
|
|
func TestAddRuntimeAnnotations(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
config := vc.SandboxConfig{
|
|
Annotations: make(map[string]string),
|
|
}
|
|
|
|
ocispec := specs.Spec{
|
|
Annotations: make(map[string]string),
|
|
}
|
|
|
|
runtimeConfig := RuntimeConfig{
|
|
HypervisorType: vc.QemuHypervisor,
|
|
Console: consolePath,
|
|
}
|
|
|
|
ocispec.Annotations[vcAnnotations.DisableGuestSeccomp] = "true"
|
|
ocispec.Annotations[vcAnnotations.SandboxCgroupOnly] = "true"
|
|
ocispec.Annotations[vcAnnotations.DisableNewNetNs] = "true"
|
|
ocispec.Annotations[vcAnnotations.InterNetworkModel] = "macvtap"
|
|
|
|
addAnnotations(ocispec, &config, runtimeConfig)
|
|
assert.Equal(config.DisableGuestSeccomp, true)
|
|
assert.Equal(config.SandboxCgroupOnly, true)
|
|
assert.Equal(config.NetworkConfig.DisableNewNetNs, true)
|
|
assert.Equal(config.NetworkConfig.InterworkingModel, vc.NetXConnectMacVtapModel)
|
|
}
|
|
|
|
func TestRegexpContains(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
//nolint: govet
|
|
type testData struct {
|
|
regexps []string
|
|
toMatch string
|
|
expected bool
|
|
}
|
|
|
|
data := []testData{
|
|
{[]string{}, "", false},
|
|
{[]string{}, "nonempty", false},
|
|
{[]string{"simple"}, "simple", true},
|
|
{[]string{"simple"}, "some_simple_text", true},
|
|
{[]string{"simple"}, "simp", false},
|
|
{[]string{"one", "two"}, "one", true},
|
|
{[]string{"one", "two"}, "two", true},
|
|
{[]string{"o*"}, "oooo", true},
|
|
{[]string{"o*"}, "oooa", true},
|
|
{[]string{"^o*$"}, "oooa", false},
|
|
}
|
|
|
|
for _, d := range data {
|
|
matched := regexpContains(d.regexps, d.toMatch)
|
|
assert.Equal(d.expected, matched, "%+v", d)
|
|
}
|
|
}
|
|
|
|
func TestCheckPathIsInGlobs(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
//nolint: govet
|
|
type testData struct {
|
|
globs []string
|
|
toMatch string
|
|
expected bool
|
|
}
|
|
|
|
data := []testData{
|
|
{[]string{}, "", false},
|
|
{[]string{}, "nonempty", false},
|
|
{[]string{"simple"}, "simple", false},
|
|
{[]string{"simple"}, "some_simple_text", false},
|
|
{[]string{"/bin/ls"}, "/bin/ls", true},
|
|
{[]string{"/bin/ls", "/bin/false"}, "/bin/ls", true},
|
|
{[]string{"/bin/ls", "/bin/false"}, "/bin/false", true},
|
|
{[]string{"/bin/ls", "/bin/false"}, "/bin/bar", false},
|
|
{[]string{"/bin/*ls*"}, "/bin/ls", true},
|
|
{[]string{"/bin/*ls*"}, "/bin/false", true},
|
|
{[]string{"bin/ls"}, "/bin/ls", false},
|
|
{[]string{"./bin/ls"}, "/bin/ls", false},
|
|
{[]string{"*/bin/ls"}, "/bin/ls", false},
|
|
}
|
|
|
|
for _, d := range data {
|
|
matched := checkPathIsInGlobs(d.globs, d.toMatch)
|
|
assert.Equal(d.expected, matched, "%+v", d)
|
|
}
|
|
}
|
|
|
|
func TestIsCRIOContainerManager(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
testCases := []struct {
|
|
annotations map[string]string
|
|
result bool
|
|
}{
|
|
{
|
|
annotations: map[string]string{crioAnnotations.ContainerType: "abc"},
|
|
result: false,
|
|
},
|
|
{
|
|
annotations: map[string]string{crioAnnotations.ContainerType: crioAnnotations.ContainerTypeSandbox},
|
|
result: true,
|
|
},
|
|
{
|
|
annotations: map[string]string{crioAnnotations.ContainerType: crioAnnotations.ContainerTypeContainer},
|
|
result: true,
|
|
},
|
|
}
|
|
|
|
for i := range testCases {
|
|
tc := testCases[i]
|
|
ocispec := specs.Spec{
|
|
Annotations: tc.annotations,
|
|
}
|
|
result := IsCRIOContainerManager(&ocispec)
|
|
assert.Equal(tc.result, result, "test case %d", (i + 1))
|
|
}
|
|
}
|
|
|
|
func TestParseAnnotationUintConfiguration(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
const key = "my_key"
|
|
|
|
validErr := fmt.Errorf("invalid value range: must between [10-1000]")
|
|
validFunc := func(v uint64) error {
|
|
if v < 10 || v > 1000 {
|
|
return validErr
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// nolint: govet
|
|
testCases := []struct {
|
|
annotations map[string]string
|
|
expected uint64
|
|
err error
|
|
validFunc func(uint64) error
|
|
}{
|
|
{
|
|
annotations: map[string]string{key: ""},
|
|
expected: 0,
|
|
err: fmt.Errorf(errAnnotationPositiveNumericKey, key),
|
|
validFunc: nil,
|
|
},
|
|
{
|
|
annotations: map[string]string{key: "a"},
|
|
expected: 0,
|
|
err: fmt.Errorf(errAnnotationPositiveNumericKey, key),
|
|
validFunc: nil,
|
|
},
|
|
{
|
|
annotations: map[string]string{key: "16"},
|
|
expected: 16,
|
|
err: nil,
|
|
validFunc: nil,
|
|
},
|
|
{
|
|
annotations: map[string]string{key: "16"},
|
|
expected: 16,
|
|
err: nil,
|
|
validFunc: validFunc,
|
|
},
|
|
{
|
|
annotations: map[string]string{key: "8"},
|
|
expected: 0,
|
|
err: validErr,
|
|
validFunc: validFunc,
|
|
},
|
|
{
|
|
annotations: map[string]string{key: "0"},
|
|
expected: 0,
|
|
err: nil,
|
|
validFunc: nil,
|
|
},
|
|
{
|
|
annotations: map[string]string{key: "-1"},
|
|
expected: 0,
|
|
err: fmt.Errorf(errAnnotationPositiveNumericKey, key),
|
|
validFunc: nil,
|
|
},
|
|
}
|
|
|
|
for i := range testCases {
|
|
tc := testCases[i]
|
|
ocispec := specs.Spec{
|
|
Annotations: tc.annotations,
|
|
}
|
|
var val uint64 = 0
|
|
|
|
err := newAnnotationConfiguration(ocispec, key).setUintWithCheck(func(v uint64) error {
|
|
if tc.validFunc != nil {
|
|
if err := tc.validFunc(v); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
val = v
|
|
return nil
|
|
})
|
|
|
|
assert.Equal(tc.err, err, "test case %d check error", (i + 1))
|
|
if tc.err == nil {
|
|
assert.Equal(tc.expected, val, "test case %d check parsed result", (i + 1))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParseAnnotationBoolConfiguration(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
const (
|
|
u32Key = "u32_key"
|
|
u64Key = "u64_key"
|
|
boolKey = "bool_key"
|
|
)
|
|
|
|
// nolint: govet
|
|
testCases := []struct {
|
|
annotationKey string
|
|
annotationValueList []string
|
|
expected bool
|
|
err error
|
|
}{
|
|
{
|
|
annotationKey: boolKey,
|
|
annotationValueList: []string{"1", "t", "T", "true", "TRUE", "True"},
|
|
expected: true,
|
|
err: nil,
|
|
},
|
|
{
|
|
annotationKey: boolKey,
|
|
annotationValueList: []string{"0", "f", "F", "false", "FALSE", "False"},
|
|
expected: false,
|
|
err: nil,
|
|
},
|
|
{
|
|
annotationKey: boolKey,
|
|
annotationValueList: []string{"a", "FalSE", "Fal", "TRue", "TRU", "falsE"},
|
|
expected: false,
|
|
err: fmt.Errorf(errAnnotationBoolKey, boolKey),
|
|
},
|
|
}
|
|
|
|
for i := range testCases {
|
|
tc := testCases[i]
|
|
for _, annotaionValue := range tc.annotationValueList {
|
|
ocispec := specs.Spec{
|
|
Annotations: map[string]string{tc.annotationKey: annotaionValue},
|
|
}
|
|
var val bool = false
|
|
|
|
err := newAnnotationConfiguration(ocispec, tc.annotationKey).setBool(func(v bool) {
|
|
val = v
|
|
})
|
|
|
|
assert.Equal(tc.err, err, "test case %d check error", (i + 1))
|
|
if tc.err == nil {
|
|
assert.Equal(tc.expected, val, "test case %d check parsed result", (i + 1))
|
|
}
|
|
}
|
|
}
|
|
}
|