Files
kata-containers/virtcontainers/qemu_test.go
Salvador Fuentes ad1563196e virtiofsd: Do not use posix lock.
We have some issues trying to run `apt upgrade` on
a container that uses virtiofsd with `-o posix_lock`.

Add virtiofsd `-o no_posix_lock` argument to not use the
posix lock.

Signed-off-by: Salvador Fuentes <salvador.fuentes@intel.com>
2019-10-07 16:21:33 -05:00

551 lines
13 KiB
Go

// Copyright (c) 2016 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package virtcontainers
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
govmmQemu "github.com/intel/govmm/qemu"
"github.com/kata-containers/runtime/virtcontainers/device/config"
"github.com/kata-containers/runtime/virtcontainers/store"
"github.com/kata-containers/runtime/virtcontainers/types"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
func newQemuConfig() HypervisorConfig {
return HypervisorConfig{
KernelPath: testQemuKernelPath,
ImagePath: testQemuImagePath,
InitrdPath: testQemuInitrdPath,
HypervisorPath: testQemuPath,
NumVCPUs: defaultVCPUs,
MemorySize: defaultMemSzMiB,
DefaultBridges: defaultBridges,
BlockDeviceDriver: defaultBlockDriver,
DefaultMaxVCPUs: defaultMaxQemuVCPUs,
Msize9p: defaultMsize9p,
}
}
func testQemuKernelParameters(t *testing.T, kernelParams []Param, expected string, debug bool) {
qemuConfig := newQemuConfig()
qemuConfig.KernelParams = kernelParams
assert := assert.New(t)
if debug == true {
qemuConfig.Debug = true
}
q := &qemu{
config: qemuConfig,
arch: &qemuArchBase{},
}
params := q.kernelParameters()
assert.Equal(params, expected)
}
func TestQemuKernelParameters(t *testing.T) {
expectedOut := fmt.Sprintf("panic=1 nr_cpus=%d agent.use_vsock=false foo=foo bar=bar", MaxQemuVCPUs())
params := []Param{
{
Key: "foo",
Value: "foo",
},
{
Key: "bar",
Value: "bar",
},
}
testQemuKernelParameters(t, params, expectedOut, true)
testQemuKernelParameters(t, params, expectedOut, false)
}
func TestQemuCreateSandbox(t *testing.T) {
qemuConfig := newQemuConfig()
q := &qemu{}
assert := assert.New(t)
sandbox := &Sandbox{
ctx: context.Background(),
id: "testSandbox",
config: &SandboxConfig{
HypervisorConfig: qemuConfig,
},
}
vcStore, err := store.NewVCSandboxStore(sandbox.ctx, sandbox.id)
assert.NoError(err)
sandbox.store = vcStore
// Create the hypervisor fake binary
testQemuPath := filepath.Join(testDir, testHypervisor)
_, err = os.Create(testQemuPath)
assert.NoError(err)
// Create parent dir path for hypervisor.json
parentDir := store.SandboxConfigurationRootPath(sandbox.id)
assert.NoError(os.MkdirAll(parentDir, store.DirMode))
err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, sandbox.store)
assert.NoError(err)
assert.NoError(os.RemoveAll(parentDir))
assert.Exactly(qemuConfig, q.config)
}
func TestQemuCreateSandboxMissingParentDirFail(t *testing.T) {
qemuConfig := newQemuConfig()
q := &qemu{}
assert := assert.New(t)
sandbox := &Sandbox{
ctx: context.Background(),
id: "testSandbox",
config: &SandboxConfig{
HypervisorConfig: qemuConfig,
},
}
vcStore, err := store.NewVCSandboxStore(sandbox.ctx, sandbox.id)
assert.NoError(err)
sandbox.store = vcStore
// Create the hypervisor fake binary
testQemuPath := filepath.Join(testDir, testHypervisor)
_, err = os.Create(testQemuPath)
assert.NoError(err)
// Ensure parent dir path for hypervisor.json does not exist.
parentDir := store.SandboxConfigurationRootPath(sandbox.id)
assert.NoError(os.RemoveAll(parentDir))
err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, sandbox.store)
assert.NoError(err)
}
func TestQemuCPUTopology(t *testing.T) {
assert := assert.New(t)
vcpus := 1
q := &qemu{
arch: &qemuArchBase{},
config: HypervisorConfig{
NumVCPUs: uint32(vcpus),
DefaultMaxVCPUs: uint32(vcpus),
},
}
expectedOut := govmmQemu.SMP{
CPUs: uint32(vcpus),
Sockets: uint32(vcpus),
Cores: defaultCores,
Threads: defaultThreads,
MaxCPUs: uint32(vcpus),
}
smp := q.cpuTopology()
assert.Exactly(smp, expectedOut)
}
func TestQemuMemoryTopology(t *testing.T) {
mem := uint32(1000)
slots := uint32(8)
assert := assert.New(t)
q := &qemu{
arch: &qemuArchBase{},
config: HypervisorConfig{
MemorySize: mem,
MemSlots: slots,
},
}
hostMemKb, err := getHostMemorySizeKb(procMemInfo)
assert.NoError(err)
memMax := fmt.Sprintf("%dM", int(float64(hostMemKb)/1024))
expectedOut := govmmQemu.Memory{
Size: fmt.Sprintf("%dM", mem),
Slots: uint8(slots),
MaxMem: memMax,
}
memory, err := q.memoryTopology()
assert.NoError(err)
assert.Exactly(memory, expectedOut)
}
func testQemuAddDevice(t *testing.T, devInfo interface{}, devType deviceType, expected []govmmQemu.Device) {
assert := assert.New(t)
q := &qemu{
ctx: context.Background(),
arch: &qemuArchBase{},
}
err := q.addDevice(devInfo, devType)
assert.NoError(err)
assert.Exactly(q.qemuConfig.Devices, expected)
}
func TestQemuAddDeviceFsDev(t *testing.T) {
mountTag := "testMountTag"
hostPath := "testHostPath"
expectedOut := []govmmQemu.Device{
govmmQemu.FSDevice{
Driver: govmmQemu.Virtio9P,
FSDriver: govmmQemu.Local,
ID: fmt.Sprintf("extra-9p-%s", mountTag),
Path: hostPath,
MountTag: mountTag,
SecurityModel: govmmQemu.None,
},
}
volume := types.Volume{
MountTag: mountTag,
HostPath: hostPath,
}
testQemuAddDevice(t, volume, fsDev, expectedOut)
}
func TestQemuAddDeviceSerialPortDev(t *testing.T) {
deviceID := "channelTest"
id := "charchTest"
hostPath := "/tmp/hyper_test.sock"
name := "sh.hyper.channel.test"
expectedOut := []govmmQemu.Device{
govmmQemu.CharDevice{
Driver: govmmQemu.VirtioSerialPort,
Backend: govmmQemu.Socket,
DeviceID: deviceID,
ID: id,
Path: hostPath,
Name: name,
},
}
socket := types.Socket{
DeviceID: deviceID,
ID: id,
HostPath: hostPath,
Name: name,
}
testQemuAddDevice(t, socket, serialPortDev, expectedOut)
}
func TestQemuAddDeviceKataVSOCK(t *testing.T) {
assert := assert.New(t)
dir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(dir)
vsockFilename := filepath.Join(dir, "vsock")
contextID := uint64(3)
port := uint32(1024)
vsockFile, err := os.Create(vsockFilename)
assert.NoError(err)
defer vsockFile.Close()
expectedOut := []govmmQemu.Device{
govmmQemu.VSOCKDevice{
ID: fmt.Sprintf("vsock-%d", contextID),
ContextID: contextID,
VHostFD: vsockFile,
},
}
vsock := types.VSock{
ContextID: contextID,
Port: port,
VhostFd: vsockFile,
}
testQemuAddDevice(t, vsock, vSockPCIDev, expectedOut)
}
func TestQemuGetSandboxConsole(t *testing.T) {
assert := assert.New(t)
q := &qemu{
ctx: context.Background(),
}
sandboxID := "testSandboxID"
expected := filepath.Join(store.RunVMStoragePath(), sandboxID, consoleSocket)
result, err := q.getSandboxConsole(sandboxID)
assert.NoError(err)
assert.Equal(result, expected)
}
func TestQemuCapabilities(t *testing.T) {
assert := assert.New(t)
q := &qemu{
ctx: context.Background(),
arch: &qemuArchBase{},
}
caps := q.capabilities()
assert.True(caps.IsBlockDeviceHotplugSupported())
}
func TestQemuQemuPath(t *testing.T) {
assert := assert.New(t)
f, err := ioutil.TempFile("", "qemu")
assert.NoError(err)
defer func() { _ = f.Close() }()
defer func() { _ = os.Remove(f.Name()) }()
expectedPath := f.Name()
qemuConfig := newQemuConfig()
qemuConfig.HypervisorPath = expectedPath
qkvm := &qemuArchBase{
machineType: "pc",
qemuPaths: map[string]string{
"pc": expectedPath,
},
}
q := &qemu{
config: qemuConfig,
arch: qkvm,
}
// get config hypervisor path
path, err := q.qemuPath()
assert.NoError(err)
assert.Equal(path, expectedPath)
// config hypervisor path does not exist
q.config.HypervisorPath = "/abc/rgb/123"
path, err = q.qemuPath()
assert.Error(err)
assert.Equal(path, "")
// get arch hypervisor path
q.config.HypervisorPath = ""
path, err = q.qemuPath()
assert.NoError(err)
assert.Equal(path, expectedPath)
// bad machine type, arch should fail
qkvm.machineType = "rgb"
q.arch = qkvm
path, err = q.qemuPath()
assert.Error(err)
assert.Equal(path, "")
}
func TestHotplugUnsupportedDeviceType(t *testing.T) {
assert := assert.New(t)
qemuConfig := newQemuConfig()
q := &qemu{
ctx: context.Background(),
id: "qemuTest",
config: qemuConfig,
}
vcStore, err := store.NewVCSandboxStore(q.ctx, q.id)
assert.NoError(err)
q.store = vcStore
_, err = q.hotplugAddDevice(&memoryDevice{0, 128, uint64(0), false}, fsDev)
assert.Error(err)
_, err = q.hotplugRemoveDevice(&memoryDevice{0, 128, uint64(0), false}, fsDev)
assert.Error(err)
}
func TestQMPSetupShutdown(t *testing.T) {
assert := assert.New(t)
qemuConfig := newQemuConfig()
q := &qemu{
config: qemuConfig,
}
q.qmpShutdown()
q.qmpMonitorCh.qmp = &govmmQemu.QMP{}
err := q.qmpSetup()
assert.Nil(err)
}
func TestQemuCleanup(t *testing.T) {
assert := assert.New(t)
q := &qemu{
ctx: context.Background(),
config: newQemuConfig(),
}
err := q.cleanup()
assert.Nil(err)
}
func TestQemuGrpc(t *testing.T) {
assert := assert.New(t)
config := newQemuConfig()
q := &qemu{
id: "testqemu",
config: config,
}
json, err := q.toGrpc()
assert.Nil(err)
var q2 qemu
err = q2.fromGrpc(context.Background(), &config, nil, json)
assert.Nil(err)
assert.True(q.id == q2.id)
}
func TestQemuFileBackedMem(t *testing.T) {
assert := assert.New(t)
// Check default Filebackedmem location for virtio-fs
sandbox, err := createQemuSandboxConfig()
assert.NoError(err)
q := &qemu{}
sandbox.config.HypervisorConfig.SharedFS = config.VirtioFS
err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, sandbox.store)
assert.NoError(err)
assert.Equal(q.qemuConfig.Knobs.FileBackedMem, true)
assert.Equal(q.qemuConfig.Knobs.MemShared, true)
assert.Equal(q.qemuConfig.Memory.Path, fallbackFileBackedMemDir)
// Check failure for VM templating
sandbox, err = createQemuSandboxConfig()
assert.NoError(err)
q = &qemu{}
sandbox.config.HypervisorConfig.BootToBeTemplate = true
sandbox.config.HypervisorConfig.SharedFS = config.VirtioFS
sandbox.config.HypervisorConfig.MemoryPath = fallbackFileBackedMemDir
err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, sandbox.store)
expectErr := errors.New("VM templating has been enabled with either virtio-fs or file backed memory and this configuration will not work")
assert.Equal(expectErr.Error(), err.Error())
// Check Setting of non-existent shared-mem path
sandbox, err = createQemuSandboxConfig()
assert.NoError(err)
q = &qemu{}
sandbox.config.HypervisorConfig.FileBackedMemRootDir = "/tmp/xyzabc"
err = q.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, sandbox.store)
assert.NoError(err)
assert.Equal(q.qemuConfig.Knobs.FileBackedMem, false)
assert.Equal(q.qemuConfig.Knobs.MemShared, false)
assert.Equal(q.qemuConfig.Memory.Path, "")
}
func createQemuSandboxConfig() (*Sandbox, error) {
qemuConfig := newQemuConfig()
sandbox := Sandbox{
ctx: context.Background(),
id: "testSandbox",
config: &SandboxConfig{
HypervisorConfig: qemuConfig,
},
}
vcStore, err := store.NewVCSandboxStore(sandbox.ctx, sandbox.id)
if err != nil {
return &Sandbox{}, err
}
sandbox.store = vcStore
return &sandbox, nil
}
func TestQemuVirtiofsdArgs(t *testing.T) {
assert := assert.New(t)
q := &qemu{
id: "foo",
config: HypervisorConfig{
VirtioFSCache: "none",
Debug: true,
},
}
savedKataHostSharedDir := kataHostSharedDir
kataHostSharedDir = func() string {
return "test-share-dir"
}
defer func() {
kataHostSharedDir = savedKataHostSharedDir
}()
result := "--fd=123 -o source=test-share-dir/foo -o cache=none --syslog -o no_posix_lock -d"
args := q.virtiofsdArgs(123)
assert.Equal(strings.Join(args, " "), result)
q.config.Debug = false
result = "--fd=123 -o source=test-share-dir/foo -o cache=none --syslog -o no_posix_lock -f"
args = q.virtiofsdArgs(123)
assert.Equal(strings.Join(args, " "), result)
}
func TestQemuGetpids(t *testing.T) {
assert := assert.New(t)
qemuConfig := newQemuConfig()
q := &qemu{}
pids := q.getPids()
assert.NotNil(pids)
assert.True(len(pids) == 1)
assert.True(pids[0] == 0)
q = &qemu{
config: qemuConfig,
}
f, err := ioutil.TempFile("", "qemu-test-")
assert.Nil(err)
tmpfile := f.Name()
f.Close()
defer os.Remove(tmpfile)
q.qemuConfig.PidFile = tmpfile
pids = q.getPids()
assert.True(len(pids) == 1)
assert.True(pids[0] == 0)
err = ioutil.WriteFile(tmpfile, []byte("100"), 0)
assert.Nil(err)
pids = q.getPids()
assert.True(len(pids) == 1)
assert.True(pids[0] == 100)
q.state.VirtiofsdPid = 200
pids = q.getPids()
assert.True(len(pids) == 2)
assert.True(pids[0] == 100)
assert.True(pids[1] == 200)
}