mirror of
https://github.com/aljazceru/kata-containers.git
synced 2026-01-05 23:44:21 +01:00
With the recently added VFIO fixes and support, we should not need that anymore. Fixes #3108 Signed-off-by: Samuel Ortiz <s.ortiz@apple.com>
1681 lines
46 KiB
Go
1681 lines
46 KiB
Go
// Copyright (c) 2016 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
package virtcontainers
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"syscall"
|
|
"testing"
|
|
|
|
ktu "github.com/kata-containers/kata-containers/src/runtime/pkg/katatestutils"
|
|
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/config"
|
|
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/drivers"
|
|
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/device/manager"
|
|
exp "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/experimental"
|
|
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/persist/fs"
|
|
|
|
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
|
|
vcAnnotations "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
|
|
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/types"
|
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/stretchr/testify/assert"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// dirMode is the permission bits used for creating a directory
|
|
const dirMode = os.FileMode(0750) | os.ModeDir
|
|
|
|
func newHypervisorConfig(kernelParams []Param, hParams []Param) HypervisorConfig {
|
|
return HypervisorConfig{
|
|
KernelPath: filepath.Join(testDir, testKernel),
|
|
ImagePath: filepath.Join(testDir, testImage),
|
|
HypervisorPath: filepath.Join(testDir, testHypervisor),
|
|
KernelParams: kernelParams,
|
|
HypervisorParams: hParams,
|
|
}
|
|
|
|
}
|
|
|
|
func testCreateSandbox(t *testing.T, id string,
|
|
htype HypervisorType, hconfig HypervisorConfig,
|
|
nconfig NetworkConfig, containers []ContainerConfig,
|
|
volumes []types.Volume) (*Sandbox, error) {
|
|
|
|
if tc.NotValid(ktu.NeedRoot()) {
|
|
t.Skip(testDisabledAsNonRoot)
|
|
}
|
|
|
|
sconfig := SandboxConfig{
|
|
ID: id,
|
|
HypervisorType: htype,
|
|
HypervisorConfig: hconfig,
|
|
NetworkConfig: nconfig,
|
|
Volumes: volumes,
|
|
Containers: containers,
|
|
Annotations: sandboxAnnotations,
|
|
}
|
|
|
|
ctx := WithNewAgentFunc(context.Background(), newMockAgent)
|
|
sandbox, err := createSandbox(ctx, sconfig, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Could not create sandbox: %s", err)
|
|
}
|
|
|
|
if err := sandbox.agent.startSandbox(context.Background(), sandbox); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := sandbox.createContainers(context.Background()); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if sandbox.id == "" {
|
|
return sandbox, fmt.Errorf("Invalid empty sandbox ID")
|
|
}
|
|
|
|
if id != "" && sandbox.id != id {
|
|
return sandbox, fmt.Errorf("Invalid ID %s vs %s", id, sandbox.id)
|
|
}
|
|
|
|
return sandbox, nil
|
|
}
|
|
|
|
func TestCreateEmptySandbox(t *testing.T) {
|
|
_, err := testCreateSandbox(t, testSandboxID, MockHypervisor, HypervisorConfig{}, NetworkConfig{}, nil, nil)
|
|
assert.Error(t, err)
|
|
defer cleanUp()
|
|
}
|
|
|
|
func TestCreateEmptyHypervisorSandbox(t *testing.T) {
|
|
_, err := testCreateSandbox(t, testSandboxID, QemuHypervisor, HypervisorConfig{}, NetworkConfig{}, nil, nil)
|
|
assert.Error(t, err)
|
|
defer cleanUp()
|
|
}
|
|
|
|
func TestCreateMockSandbox(t *testing.T) {
|
|
hConfig := newHypervisorConfig(nil, nil)
|
|
_, err := testCreateSandbox(t, testSandboxID, MockHypervisor, hConfig, NetworkConfig{}, nil, nil)
|
|
assert.NoError(t, err)
|
|
defer cleanUp()
|
|
}
|
|
|
|
func TestCalculateSandboxCPUs(t *testing.T) {
|
|
sandbox := &Sandbox{}
|
|
sandbox.config = &SandboxConfig{}
|
|
|
|
unconstrained := newTestContainerConfigNoop("cont-00001")
|
|
constrained := newTestContainerConfigNoop("cont-00002")
|
|
unconstrainedCpusets0_1 := newTestContainerConfigNoop("cont-00003")
|
|
unconstrainedCpusets2 := newTestContainerConfigNoop("cont-00004")
|
|
constrainedCpusets0_7 := newTestContainerConfigNoop("cont-00005")
|
|
quota := int64(4000)
|
|
period := uint64(1000)
|
|
constrained.Resources.CPU = &specs.LinuxCPU{Period: &period, Quota: "a}
|
|
unconstrainedCpusets0_1.Resources.CPU = &specs.LinuxCPU{Cpus: "0-1"}
|
|
unconstrainedCpusets2.Resources.CPU = &specs.LinuxCPU{Cpus: "2"}
|
|
constrainedCpusets0_7.Resources.CPU = &specs.LinuxCPU{Period: &period, Quota: "a, Cpus: "0-7"}
|
|
tests := []struct {
|
|
name string
|
|
containers []ContainerConfig
|
|
want uint32
|
|
}{
|
|
{"1-unconstrained", []ContainerConfig{unconstrained}, 0},
|
|
{"2-unconstrained", []ContainerConfig{unconstrained, unconstrained}, 0},
|
|
{"1-constrained", []ContainerConfig{constrained}, 4},
|
|
{"2-constrained", []ContainerConfig{constrained, constrained}, 8},
|
|
{"3-mix-constraints", []ContainerConfig{unconstrained, constrained, constrained}, 8},
|
|
{"3-constrained", []ContainerConfig{constrained, constrained, constrained}, 12},
|
|
{"unconstrained-1-cpuset", []ContainerConfig{unconstrained, unconstrained, unconstrainedCpusets0_1}, 2},
|
|
{"unconstrained-2-cpuset", []ContainerConfig{unconstrainedCpusets0_1, unconstrainedCpusets2}, 3},
|
|
{"constrained-cpuset", []ContainerConfig{constrainedCpusets0_7}, 4},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
sandbox.config.Containers = tt.containers
|
|
got, _ := sandbox.calculateSandboxCPUs()
|
|
assert.Equal(t, got, tt.want)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCalculateSandboxMem(t *testing.T) {
|
|
sandbox := &Sandbox{}
|
|
sandbox.config = &SandboxConfig{}
|
|
unconstrained := newTestContainerConfigNoop("cont-00001")
|
|
constrained := newTestContainerConfigNoop("cont-00001")
|
|
mlimit := int64(4000)
|
|
limit := uint64(4000)
|
|
constrained.Resources.Memory = &specs.LinuxMemory{Limit: &mlimit}
|
|
|
|
tests := []struct {
|
|
name string
|
|
containers []ContainerConfig
|
|
want uint64
|
|
}{
|
|
{"1-unconstrained", []ContainerConfig{unconstrained}, 0},
|
|
{"2-unconstrained", []ContainerConfig{unconstrained, unconstrained}, 0},
|
|
{"1-constrained", []ContainerConfig{constrained}, limit},
|
|
{"2-constrained", []ContainerConfig{constrained, constrained}, limit * 2},
|
|
{"3-mix-constraints", []ContainerConfig{unconstrained, constrained, constrained}, limit * 2},
|
|
{"3-constrained", []ContainerConfig{constrained, constrained, constrained}, limit * 3},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
sandbox.config.Containers = tt.containers
|
|
mem, needSwap, swap := sandbox.calculateSandboxMemory()
|
|
assert.Equal(t, mem, tt.want)
|
|
assert.Equal(t, needSwap, false)
|
|
assert.Equal(t, swap, int64(0))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCalculateSandboxMemHandlesNegativeLimits(t *testing.T) {
|
|
sandbox := &Sandbox{}
|
|
sandbox.config = &SandboxConfig{}
|
|
container := newTestContainerConfigNoop("cont-00001")
|
|
limit := int64(-1)
|
|
container.Resources.Memory = &specs.LinuxMemory{Limit: &limit}
|
|
|
|
sandbox.config.Containers = []ContainerConfig{container}
|
|
mem, needSwap, swap := sandbox.calculateSandboxMemory()
|
|
assert.Equal(t, mem, uint64(0))
|
|
assert.Equal(t, needSwap, false)
|
|
assert.Equal(t, swap, int64(0))
|
|
}
|
|
|
|
func TestCreateSandboxEmptyID(t *testing.T) {
|
|
hConfig := newHypervisorConfig(nil, nil)
|
|
_, err := testCreateSandbox(t, "", MockHypervisor, hConfig, NetworkConfig{}, nil, nil)
|
|
assert.Error(t, err)
|
|
defer cleanUp()
|
|
}
|
|
|
|
func testCheckInitSandboxAndContainerStates(p *Sandbox, initialSandboxState types.SandboxState, c *Container, initialContainerState types.ContainerState) error {
|
|
if p.state.State != initialSandboxState.State {
|
|
return fmt.Errorf("Expected sandbox state %v, got %v", initialSandboxState.State, p.state.State)
|
|
}
|
|
|
|
if c.state.State != initialContainerState.State {
|
|
return fmt.Errorf("Expected container state %v, got %v", initialContainerState.State, c.state.State)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func testForceSandboxStateChangeAndCheck(t *testing.T, p *Sandbox, newSandboxState types.SandboxState) error {
|
|
// force sandbox state change
|
|
err := p.setSandboxState(newSandboxState.State)
|
|
assert.NoError(t, err)
|
|
// Check the in-memory state is correct
|
|
if p.state.State != newSandboxState.State {
|
|
return fmt.Errorf("Expected state %v, got %v", newSandboxState.State, p.state.State)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func testForceContainerStateChangeAndCheck(t *testing.T, p *Sandbox, c *Container, newContainerState types.ContainerState) error {
|
|
// force container state change
|
|
err := c.setContainerState(newContainerState.State)
|
|
assert.NoError(t, err)
|
|
|
|
// Check the in-memory state is correct
|
|
if c.state.State != newContainerState.State {
|
|
return fmt.Errorf("Expected state %v, got %v", newContainerState.State, c.state.State)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func testCheckSandboxOnDiskState(p *Sandbox, sandboxState types.SandboxState) error {
|
|
// Check on-disk state is correct
|
|
if p.state.State != sandboxState.State {
|
|
return fmt.Errorf("Expected state %v, got %v", sandboxState.State, p.state.State)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func testCheckContainerOnDiskState(c *Container, containerState types.ContainerState) error {
|
|
// Check on-disk state is correct
|
|
if c.state.State != containerState.State {
|
|
return fmt.Errorf("Expected state %v, got %v", containerState.State, c.state.State)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// writeContainerConfig write config.json to bundle path
|
|
// and return bundle path.
|
|
// NOTE: don't forget to delete the bundle path
|
|
func writeContainerConfig() (string, error) {
|
|
|
|
basicSpec := `
|
|
{
|
|
"ociVersion": "1.0.0-rc2-dev",
|
|
"process": {
|
|
"Capabilities": [
|
|
]
|
|
}
|
|
}`
|
|
|
|
configDir, err := os.MkdirTemp("", "vc-tmp-")
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
err = os.MkdirAll(configDir, DirMode)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
configFilePath := filepath.Join(configDir, "config.json")
|
|
err = os.WriteFile(configFilePath, []byte(basicSpec), 0644)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return configDir, nil
|
|
}
|
|
|
|
func TestSandboxSetSandboxAndContainerState(t *testing.T) {
|
|
contID := "505"
|
|
contConfig := newTestContainerConfigNoop(contID)
|
|
assert := assert.New(t)
|
|
|
|
configDir, err := writeContainerConfig()
|
|
if err != nil {
|
|
os.RemoveAll(configDir)
|
|
}
|
|
assert.NoError(err)
|
|
|
|
// set bundle path annotation, fetchSandbox need this annotation to get containers
|
|
contConfig.Annotations[vcAnnotations.BundlePathKey] = configDir
|
|
|
|
hConfig := newHypervisorConfig(nil, nil)
|
|
|
|
// create a sandbox
|
|
p, err := testCreateSandbox(t, testSandboxID, MockHypervisor, hConfig, NetworkConfig{}, []ContainerConfig{contConfig}, nil)
|
|
assert.NoError(err)
|
|
defer cleanUp()
|
|
|
|
l := len(p.GetAllContainers())
|
|
assert.Equal(l, 1)
|
|
|
|
initialSandboxState := types.SandboxState{
|
|
State: types.StateReady,
|
|
}
|
|
|
|
// After a sandbox creation, a container has a READY state
|
|
initialContainerState := types.ContainerState{
|
|
State: types.StateReady,
|
|
}
|
|
|
|
c, err := p.findContainer(contID)
|
|
assert.NoError(err)
|
|
|
|
// Check initial sandbox and container states
|
|
if err := testCheckInitSandboxAndContainerStates(p, initialSandboxState, c, initialContainerState); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
// persist to disk
|
|
err = p.storeSandbox(p.ctx)
|
|
assert.NoError(err)
|
|
|
|
newSandboxState := types.SandboxState{
|
|
State: types.StateRunning,
|
|
}
|
|
|
|
if err := testForceSandboxStateChangeAndCheck(t, p, newSandboxState); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
newContainerState := types.ContainerState{
|
|
State: types.StateStopped,
|
|
}
|
|
|
|
if err := testForceContainerStateChangeAndCheck(t, p, c, newContainerState); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
// force state to be read from disk
|
|
p2, err := fetchSandbox(context.Background(), p.ID())
|
|
assert.NoError(err)
|
|
|
|
if err := testCheckSandboxOnDiskState(p2, newSandboxState); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
c2, err := p2.findContainer(contID)
|
|
assert.NoError(err)
|
|
|
|
if err := testCheckContainerOnDiskState(c2, newContainerState); err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
// revert sandbox state to allow it to be deleted
|
|
err = p.setSandboxState(initialSandboxState.State)
|
|
assert.NoError(err)
|
|
|
|
// clean up
|
|
err = p.Delete(context.Background())
|
|
assert.NoError(err)
|
|
}
|
|
|
|
func TestGetContainer(t *testing.T) {
|
|
containerIDs := []string{"abc", "123", "xyz", "rgb"}
|
|
containers := map[string]*Container{}
|
|
|
|
for _, id := range containerIDs {
|
|
c := Container{id: id}
|
|
containers[id] = &c
|
|
}
|
|
|
|
sandbox := Sandbox{
|
|
containers: containers,
|
|
}
|
|
|
|
c := sandbox.GetContainer("noid")
|
|
assert.Nil(t, c)
|
|
|
|
for _, id := range containerIDs {
|
|
c = sandbox.GetContainer(id)
|
|
assert.NotNil(t, c)
|
|
}
|
|
}
|
|
|
|
func TestGetAllContainers(t *testing.T) {
|
|
containerIDs := []string{"abc", "123", "xyz", "rgb"}
|
|
containers := map[string]*Container{}
|
|
|
|
for _, id := range containerIDs {
|
|
c := &Container{id: id}
|
|
containers[id] = c
|
|
}
|
|
|
|
sandbox := Sandbox{
|
|
containers: containers,
|
|
}
|
|
|
|
list := sandbox.GetAllContainers()
|
|
|
|
for _, c := range list {
|
|
assert.NotNil(t, containers[c.ID()], nil)
|
|
}
|
|
}
|
|
|
|
func TestSetAnnotations(t *testing.T) {
|
|
assert := assert.New(t)
|
|
sandbox := Sandbox{
|
|
ctx: context.Background(),
|
|
id: "abcxyz123",
|
|
annotationsLock: &sync.RWMutex{},
|
|
config: &SandboxConfig{
|
|
Annotations: map[string]string{
|
|
"annotation1": "abc",
|
|
},
|
|
},
|
|
}
|
|
|
|
keyAnnotation := "annotation2"
|
|
valueAnnotation := "xyz"
|
|
newAnnotations := map[string]string{
|
|
keyAnnotation: valueAnnotation,
|
|
}
|
|
|
|
// Add a new annotation
|
|
sandbox.SetAnnotations(newAnnotations)
|
|
|
|
v, err := sandbox.Annotations(keyAnnotation)
|
|
assert.NoError(err)
|
|
assert.Equal(v, valueAnnotation)
|
|
|
|
//Change the value of an annotation
|
|
valueAnnotation = "123"
|
|
newAnnotations[keyAnnotation] = valueAnnotation
|
|
|
|
sandbox.SetAnnotations(newAnnotations)
|
|
|
|
v, err = sandbox.Annotations(keyAnnotation)
|
|
assert.NoError(err)
|
|
assert.Equal(v, valueAnnotation)
|
|
}
|
|
|
|
func TestSandboxGetContainer(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
emptySandbox := Sandbox{}
|
|
_, err := emptySandbox.findContainer("")
|
|
assert.Error(err)
|
|
|
|
_, err = emptySandbox.findContainer("foo")
|
|
assert.Error(err)
|
|
|
|
hConfig := newHypervisorConfig(nil, nil)
|
|
p, err := testCreateSandbox(t, testSandboxID, MockHypervisor, hConfig, NetworkConfig{}, nil, nil)
|
|
assert.NoError(err)
|
|
defer cleanUp()
|
|
|
|
contID := "999"
|
|
contConfig := newTestContainerConfigNoop(contID)
|
|
nc, err := newContainer(context.Background(), p, &contConfig)
|
|
assert.NoError(err)
|
|
|
|
err = p.addContainer(nc)
|
|
assert.NoError(err)
|
|
|
|
got := false
|
|
for _, c := range p.GetAllContainers() {
|
|
c2, err := p.findContainer(c.ID())
|
|
assert.NoError(err)
|
|
assert.Equal(c2.ID(), c.ID())
|
|
|
|
if c2.ID() == contID {
|
|
got = true
|
|
}
|
|
}
|
|
|
|
assert.True(got)
|
|
}
|
|
|
|
func TestContainerStateSetFstype(t *testing.T) {
|
|
var err error
|
|
assert := assert.New(t)
|
|
|
|
containers := []ContainerConfig{
|
|
{
|
|
ID: "100",
|
|
Annotations: containerAnnotations,
|
|
CustomSpec: newEmptySpec(),
|
|
},
|
|
}
|
|
|
|
hConfig := newHypervisorConfig(nil, nil)
|
|
sandbox, err := testCreateSandbox(t, testSandboxID, MockHypervisor, hConfig, NetworkConfig{}, containers, nil)
|
|
assert.Nil(err)
|
|
defer cleanUp()
|
|
|
|
c := sandbox.GetContainer("100")
|
|
assert.NotNil(c)
|
|
|
|
cImpl, ok := c.(*Container)
|
|
assert.True(ok)
|
|
|
|
state := types.ContainerState{
|
|
State: "ready",
|
|
Fstype: "vfs",
|
|
}
|
|
|
|
cImpl.state = state
|
|
|
|
newFstype := "ext4"
|
|
err = cImpl.setStateFstype(newFstype)
|
|
assert.NoError(err)
|
|
assert.Equal(cImpl.state.Fstype, newFstype)
|
|
}
|
|
|
|
func TestSandboxAttachDevicesVFIO(t *testing.T) {
|
|
tmpDir, err := os.MkdirTemp("", "")
|
|
assert.Nil(t, err)
|
|
os.RemoveAll(tmpDir)
|
|
|
|
testFDIOGroup := "2"
|
|
testDeviceBDFPath := "0000:00:1c.0"
|
|
|
|
devicesDir := filepath.Join(tmpDir, testFDIOGroup, "devices")
|
|
err = os.MkdirAll(devicesDir, DirMode)
|
|
assert.Nil(t, err)
|
|
|
|
deviceFile := filepath.Join(devicesDir, testDeviceBDFPath)
|
|
_, err = os.Create(deviceFile)
|
|
assert.Nil(t, err)
|
|
|
|
savedIOMMUPath := config.SysIOMMUPath
|
|
config.SysIOMMUPath = tmpDir
|
|
|
|
defer func() {
|
|
config.SysIOMMUPath = savedIOMMUPath
|
|
}()
|
|
|
|
dm := manager.NewDeviceManager(manager.VirtioSCSI, false, "", nil)
|
|
path := filepath.Join(vfioPath, testFDIOGroup)
|
|
deviceInfo := config.DeviceInfo{
|
|
HostPath: path,
|
|
ContainerPath: path,
|
|
DevType: "c",
|
|
}
|
|
dev, err := dm.NewDevice(deviceInfo)
|
|
assert.Nil(t, err, "deviceManager.NewDevice return error: %v", err)
|
|
|
|
c := &Container{
|
|
id: "100",
|
|
devices: []ContainerDevice{
|
|
{
|
|
ID: dev.DeviceID(),
|
|
ContainerPath: path,
|
|
},
|
|
},
|
|
}
|
|
|
|
containers := map[string]*Container{}
|
|
containers[c.id] = c
|
|
|
|
sandbox := Sandbox{
|
|
id: "100",
|
|
containers: containers,
|
|
hypervisor: &mockHypervisor{},
|
|
devManager: dm,
|
|
ctx: context.Background(),
|
|
config: &SandboxConfig{},
|
|
}
|
|
|
|
containers[c.id].sandbox = &sandbox
|
|
|
|
err = containers[c.id].attachDevices(context.Background())
|
|
assert.Nil(t, err, "Error while attaching devices %s", err)
|
|
|
|
err = containers[c.id].detachDevices(context.Background())
|
|
assert.Nil(t, err, "Error while detaching devices %s", err)
|
|
}
|
|
|
|
func TestSandboxAttachDevicesVhostUserBlk(t *testing.T) {
|
|
rootEnabled := true
|
|
tc := ktu.NewTestConstraint(false)
|
|
if tc.NotValid(ktu.NeedRoot()) {
|
|
rootEnabled = false
|
|
}
|
|
|
|
tmpDir, err := os.MkdirTemp("", "")
|
|
assert.Nil(t, err)
|
|
os.RemoveAll(tmpDir)
|
|
dm := manager.NewDeviceManager(manager.VirtioSCSI, true, tmpDir, nil)
|
|
|
|
vhostUserDevNodePath := filepath.Join(tmpDir, "/block/devices/")
|
|
vhostUserSockPath := filepath.Join(tmpDir, "/block/sockets/")
|
|
deviceNodePath := filepath.Join(vhostUserDevNodePath, "vhostblk0")
|
|
deviceSockPath := filepath.Join(vhostUserSockPath, "vhostblk0")
|
|
|
|
err = os.MkdirAll(vhostUserDevNodePath, dirMode)
|
|
assert.Nil(t, err)
|
|
err = os.MkdirAll(vhostUserSockPath, dirMode)
|
|
assert.Nil(t, err)
|
|
_, err = os.Create(deviceSockPath)
|
|
assert.Nil(t, err)
|
|
|
|
// mknod requires root privilege, call mock function for non-root to
|
|
// get VhostUserBlk device type.
|
|
if rootEnabled == true {
|
|
err = unix.Mknod(deviceNodePath, unix.S_IFBLK, int(unix.Mkdev(config.VhostUserBlkMajor, 0)))
|
|
assert.Nil(t, err)
|
|
} else {
|
|
savedFunc := config.GetVhostUserNodeStatFunc
|
|
|
|
_, err = os.Create(deviceNodePath)
|
|
assert.Nil(t, err)
|
|
|
|
config.GetVhostUserNodeStatFunc = func(devNodePath string,
|
|
devNodeStat *unix.Stat_t) error {
|
|
if deviceNodePath != devNodePath {
|
|
return fmt.Errorf("mock GetVhostUserNodeStatFunc error")
|
|
}
|
|
|
|
devNodeStat.Rdev = unix.Mkdev(config.VhostUserBlkMajor, 0)
|
|
return nil
|
|
}
|
|
|
|
defer func() {
|
|
config.GetVhostUserNodeStatFunc = savedFunc
|
|
}()
|
|
}
|
|
|
|
path := "/dev/vda"
|
|
deviceInfo := config.DeviceInfo{
|
|
HostPath: deviceNodePath,
|
|
ContainerPath: path,
|
|
DevType: "b",
|
|
Major: config.VhostUserBlkMajor,
|
|
Minor: 0,
|
|
}
|
|
|
|
device, err := dm.NewDevice(deviceInfo)
|
|
assert.Nil(t, err)
|
|
_, ok := device.(*drivers.VhostUserBlkDevice)
|
|
assert.True(t, ok)
|
|
|
|
c := &Container{
|
|
id: "100",
|
|
devices: []ContainerDevice{
|
|
{
|
|
ID: device.DeviceID(),
|
|
ContainerPath: path,
|
|
},
|
|
},
|
|
}
|
|
|
|
containers := map[string]*Container{}
|
|
containers[c.id] = c
|
|
|
|
sandbox := Sandbox{
|
|
id: "100",
|
|
containers: containers,
|
|
hypervisor: &mockHypervisor{},
|
|
devManager: dm,
|
|
ctx: context.Background(),
|
|
config: &SandboxConfig{},
|
|
}
|
|
|
|
containers[c.id].sandbox = &sandbox
|
|
|
|
err = containers[c.id].attachDevices(context.Background())
|
|
assert.Nil(t, err, "Error while attaching vhost-user-blk devices %s", err)
|
|
|
|
err = containers[c.id].detachDevices(context.Background())
|
|
assert.Nil(t, err, "Error while detaching vhost-user-blk devices %s", err)
|
|
}
|
|
|
|
var assetContent = []byte("FakeAsset fake asset FAKE ASSET")
|
|
var assetContentHash = "92549f8d2018a95a294d28a65e795ed7d1a9d150009a28cea108ae10101178676f04ab82a6950d0099e4924f9c5e41dcba8ece56b75fc8b4e0a7492cb2a8c880"
|
|
var assetContentWrongHash = "92549f8d2018a95a294d28a65e795ed7d1a9d150009a28cea108ae10101178676f04ab82a6950d0099e4924f9c5e41dcba8ece56b75fc8b4e0a7492cb2a8c881"
|
|
|
|
func TestSandboxCreateAssets(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
// nolint: govet
|
|
type testData struct {
|
|
assetType types.AssetType
|
|
annotations map[string]string
|
|
}
|
|
|
|
tmpfile, err := os.CreateTemp("", "virtcontainers-test-")
|
|
assert.Nil(err)
|
|
|
|
filename := tmpfile.Name()
|
|
|
|
defer func() {
|
|
tmpfile.Close()
|
|
os.Remove(filename) // clean up
|
|
}()
|
|
|
|
_, err = tmpfile.Write(assetContent)
|
|
assert.Nil(err)
|
|
|
|
originalKernelPath := filepath.Join(testDir, testKernel)
|
|
originalImagePath := filepath.Join(testDir, testImage)
|
|
originalInitrdPath := filepath.Join(testDir, testInitrd)
|
|
originalFirmwarePath := filepath.Join(testDir, testFirmware)
|
|
originalHypervisorPath := filepath.Join(testDir, testHypervisor)
|
|
originalHypervisorCtlPath := filepath.Join(testDir, testHypervisorCtl)
|
|
originalJailerPath := filepath.Join(testDir, testJailer)
|
|
|
|
hc := HypervisorConfig{
|
|
KernelPath: originalKernelPath,
|
|
ImagePath: originalImagePath,
|
|
InitrdPath: originalInitrdPath,
|
|
FirmwarePath: originalFirmwarePath,
|
|
HypervisorPath: originalHypervisorPath,
|
|
HypervisorCtlPath: originalHypervisorCtlPath,
|
|
JailerPath: originalJailerPath,
|
|
}
|
|
|
|
data := []testData{
|
|
{
|
|
types.FirmwareAsset,
|
|
map[string]string{
|
|
annotations.FirmwarePath: filename,
|
|
annotations.FirmwareHash: assetContentHash,
|
|
},
|
|
},
|
|
{
|
|
types.HypervisorAsset,
|
|
map[string]string{
|
|
annotations.HypervisorPath: filename,
|
|
annotations.HypervisorHash: assetContentHash,
|
|
},
|
|
},
|
|
{
|
|
types.HypervisorCtlAsset,
|
|
map[string]string{
|
|
annotations.HypervisorCtlPath: filename,
|
|
annotations.HypervisorCtlHash: assetContentHash,
|
|
},
|
|
},
|
|
{
|
|
types.ImageAsset,
|
|
map[string]string{
|
|
annotations.ImagePath: filename,
|
|
annotations.ImageHash: assetContentHash,
|
|
},
|
|
},
|
|
{
|
|
types.InitrdAsset,
|
|
map[string]string{
|
|
annotations.InitrdPath: filename,
|
|
annotations.InitrdHash: assetContentHash,
|
|
},
|
|
},
|
|
{
|
|
types.JailerAsset,
|
|
map[string]string{
|
|
annotations.JailerPath: filename,
|
|
annotations.JailerHash: assetContentHash,
|
|
},
|
|
},
|
|
{
|
|
types.KernelAsset,
|
|
map[string]string{
|
|
annotations.KernelPath: filename,
|
|
annotations.KernelHash: assetContentHash,
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, d := range data {
|
|
msg := fmt.Sprintf("test[%d]: %+v", i, d)
|
|
|
|
config := &SandboxConfig{
|
|
Annotations: d.annotations,
|
|
HypervisorConfig: hc,
|
|
}
|
|
|
|
err = createAssets(context.Background(), config)
|
|
assert.NoError(err, msg)
|
|
|
|
a, ok := config.HypervisorConfig.customAssets[d.assetType]
|
|
assert.True(ok, msg)
|
|
assert.Equal(a.Path(), filename, msg)
|
|
|
|
// Now test with invalid hashes
|
|
badHashAnnotations := make(map[string]string)
|
|
for k, v := range d.annotations {
|
|
if strings.HasSuffix(k, "_hash") {
|
|
badHashAnnotations[k] = assetContentWrongHash
|
|
} else {
|
|
badHashAnnotations[k] = v
|
|
}
|
|
}
|
|
|
|
config = &SandboxConfig{
|
|
Annotations: badHashAnnotations,
|
|
HypervisorConfig: hc,
|
|
}
|
|
|
|
err = createAssets(context.Background(), config)
|
|
assert.Error(err, msg)
|
|
}
|
|
}
|
|
|
|
func testFindContainerFailure(t *testing.T, sandbox *Sandbox, cid string) {
|
|
c, err := sandbox.findContainer(cid)
|
|
assert.Nil(t, c, "Container pointer should be nil")
|
|
assert.NotNil(t, err, "Should have returned an error")
|
|
}
|
|
|
|
func TestFindContainerSandboxNilFailure(t *testing.T) {
|
|
testFindContainerFailure(t, nil, testContainerID)
|
|
}
|
|
|
|
func TestFindContainerContainerIDEmptyFailure(t *testing.T) {
|
|
sandbox := &Sandbox{}
|
|
testFindContainerFailure(t, sandbox, "")
|
|
}
|
|
|
|
func TestFindContainerNoContainerMatchFailure(t *testing.T) {
|
|
sandbox := &Sandbox{}
|
|
testFindContainerFailure(t, sandbox, testContainerID)
|
|
}
|
|
|
|
func TestFindContainerSuccess(t *testing.T) {
|
|
sandbox := &Sandbox{
|
|
containers: map[string]*Container{
|
|
testContainerID: {id: testContainerID},
|
|
},
|
|
}
|
|
c, err := sandbox.findContainer(testContainerID)
|
|
assert.NotNil(t, c, "Container pointer should not be nil")
|
|
assert.Nil(t, err, "Should not have returned an error: %v", err)
|
|
|
|
assert.True(t, c == sandbox.containers[testContainerID], "Container pointers should point to the same address")
|
|
}
|
|
|
|
func TestRemoveContainerSandboxNilFailure(t *testing.T) {
|
|
testFindContainerFailure(t, nil, testContainerID)
|
|
}
|
|
|
|
func TestRemoveContainerContainerIDEmptyFailure(t *testing.T) {
|
|
sandbox := &Sandbox{}
|
|
testFindContainerFailure(t, sandbox, "")
|
|
}
|
|
|
|
func TestRemoveContainerNoContainerMatchFailure(t *testing.T) {
|
|
sandbox := &Sandbox{}
|
|
testFindContainerFailure(t, sandbox, testContainerID)
|
|
}
|
|
|
|
func TestRemoveContainerSuccess(t *testing.T) {
|
|
sandbox := &Sandbox{
|
|
containers: map[string]*Container{
|
|
testContainerID: {id: testContainerID},
|
|
},
|
|
}
|
|
err := sandbox.removeContainer(testContainerID)
|
|
assert.Nil(t, err, "Should not have returned an error: %v", err)
|
|
|
|
assert.Equal(t, len(sandbox.containers), 0, "Containers list from sandbox structure should be empty")
|
|
}
|
|
|
|
func TestCreateContainer(t *testing.T) {
|
|
s, err := testCreateSandbox(t, testSandboxID, MockHypervisor, newHypervisorConfig(nil, nil), NetworkConfig{}, nil, nil)
|
|
assert.Nil(t, err, "VirtContainers should not allow empty sandboxes")
|
|
defer cleanUp()
|
|
|
|
contID := "999"
|
|
contConfig := newTestContainerConfigNoop(contID)
|
|
_, err = s.CreateContainer(context.Background(), contConfig)
|
|
assert.Nil(t, err, "Failed to create container %+v in sandbox %+v: %v", contConfig, s, err)
|
|
|
|
assert.Equal(t, len(s.config.Containers), 1, "Container config list length from sandbox structure should be 1")
|
|
|
|
_, err = s.CreateContainer(context.Background(), contConfig)
|
|
assert.NotNil(t, err, "Should failed to create a duplicated container")
|
|
assert.Equal(t, len(s.config.Containers), 1, "Container config list length from sandbox structure should be 1")
|
|
}
|
|
|
|
func TestDeleteContainer(t *testing.T) {
|
|
s, err := testCreateSandbox(t, testSandboxID, MockHypervisor, newHypervisorConfig(nil, nil), NetworkConfig{}, nil, nil)
|
|
assert.Nil(t, err, "VirtContainers should not allow empty sandboxes")
|
|
defer cleanUp()
|
|
|
|
contID := "999"
|
|
_, err = s.DeleteContainer(context.Background(), contID)
|
|
assert.NotNil(t, err, "Deletng non-existing container should fail")
|
|
|
|
contConfig := newTestContainerConfigNoop(contID)
|
|
_, err = s.CreateContainer(context.Background(), contConfig)
|
|
assert.Nil(t, err, "Failed to create container %+v in sandbox %+v: %v", contConfig, s, err)
|
|
|
|
_, err = s.DeleteContainer(context.Background(), contID)
|
|
assert.Nil(t, err, "Failed to delete container %s in sandbox %s: %v", contID, s.ID(), err)
|
|
}
|
|
|
|
func TestStartContainer(t *testing.T) {
|
|
s, err := testCreateSandbox(t, testSandboxID, MockHypervisor, newHypervisorConfig(nil, nil), NetworkConfig{}, nil, nil)
|
|
assert.Nil(t, err, "VirtContainers should not allow empty sandboxes")
|
|
defer cleanUp()
|
|
|
|
contID := "999"
|
|
_, err = s.StartContainer(context.Background(), contID)
|
|
assert.NotNil(t, err, "Starting non-existing container should fail")
|
|
|
|
err = s.Start(context.Background())
|
|
assert.Nil(t, err, "Failed to start sandbox: %v", err)
|
|
|
|
contConfig := newTestContainerConfigNoop(contID)
|
|
_, err = s.CreateContainer(context.Background(), contConfig)
|
|
assert.Nil(t, err, "Failed to create container %+v in sandbox %+v: %v", contConfig, s, err)
|
|
|
|
_, err = s.StartContainer(context.Background(), contID)
|
|
assert.Nil(t, err, "Start container failed: %v", err)
|
|
}
|
|
|
|
func TestStatusContainer(t *testing.T) {
|
|
s, err := testCreateSandbox(t, testSandboxID, MockHypervisor, newHypervisorConfig(nil, nil), NetworkConfig{}, nil, nil)
|
|
assert.Nil(t, err, "VirtContainers should not allow empty sandboxes")
|
|
defer cleanUp()
|
|
|
|
contID := "999"
|
|
_, err = s.StatusContainer(contID)
|
|
assert.NotNil(t, err, "Status non-existing container should fail")
|
|
|
|
contConfig := newTestContainerConfigNoop(contID)
|
|
_, err = s.CreateContainer(context.Background(), contConfig)
|
|
assert.Nil(t, err, "Failed to create container %+v in sandbox %+v: %v", contConfig, s, err)
|
|
|
|
_, err = s.StatusContainer(contID)
|
|
assert.Nil(t, err, "Status container failed: %v", err)
|
|
|
|
_, err = s.DeleteContainer(context.Background(), contID)
|
|
assert.Nil(t, err, "Failed to delete container %s in sandbox %s: %v", contID, s.ID(), err)
|
|
}
|
|
|
|
func TestStatusSandbox(t *testing.T) {
|
|
s, err := testCreateSandbox(t, testSandboxID, MockHypervisor, newHypervisorConfig(nil, nil), NetworkConfig{}, nil, nil)
|
|
assert.Nil(t, err, "VirtContainers should not allow empty sandboxes")
|
|
defer cleanUp()
|
|
|
|
s.Status()
|
|
}
|
|
|
|
func TestEnterContainer(t *testing.T) {
|
|
s, err := testCreateSandbox(t, testSandboxID, MockHypervisor, newHypervisorConfig(nil, nil), NetworkConfig{}, nil, nil)
|
|
assert.Nil(t, err, "VirtContainers should not allow empty sandboxes")
|
|
defer cleanUp()
|
|
|
|
contID := "999"
|
|
cmd := types.Cmd{}
|
|
_, _, err = s.EnterContainer(context.Background(), contID, cmd)
|
|
assert.NotNil(t, err, "Entering non-existing container should fail")
|
|
|
|
contConfig := newTestContainerConfigNoop(contID)
|
|
_, err = s.CreateContainer(context.Background(), contConfig)
|
|
assert.Nil(t, err, "Failed to create container %+v in sandbox %+v: %v", contConfig, s, err)
|
|
|
|
_, _, err = s.EnterContainer(context.Background(), contID, cmd)
|
|
assert.NotNil(t, err, "Entering non-running container should fail")
|
|
|
|
err = s.Start(context.Background())
|
|
assert.Nil(t, err, "Failed to start sandbox: %v", err)
|
|
|
|
_, _, err = s.EnterContainer(context.Background(), contID, cmd)
|
|
assert.Nil(t, err, "Enter container failed: %v", err)
|
|
}
|
|
|
|
func TestDeleteStoreWhenNewContainerFail(t *testing.T) {
|
|
hConfig := newHypervisorConfig(nil, nil)
|
|
p, err := testCreateSandbox(t, testSandboxID, MockHypervisor, hConfig, NetworkConfig{}, nil, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer cleanUp()
|
|
|
|
contID := "999"
|
|
contConfig := newTestContainerConfigNoop(contID)
|
|
contConfig.DeviceInfos = []config.DeviceInfo{
|
|
{
|
|
ContainerPath: "",
|
|
DevType: "",
|
|
},
|
|
}
|
|
_, err = newContainer(context.Background(), p, &contConfig)
|
|
assert.NotNil(t, err, "New container with invalid device info should fail")
|
|
storePath := filepath.Join(p.store.RunStoragePath(), testSandboxID, contID)
|
|
_, err = os.Stat(storePath)
|
|
assert.NotNil(t, err, "Should delete configuration root after failed to create a container")
|
|
}
|
|
|
|
func TestMonitor(t *testing.T) {
|
|
s, err := testCreateSandbox(t, testSandboxID, MockHypervisor, newHypervisorConfig(nil, nil), NetworkConfig{}, nil, nil)
|
|
assert.Nil(t, err, "VirtContainers should not allow empty sandboxes")
|
|
defer cleanUp()
|
|
|
|
_, err = s.Monitor(context.Background())
|
|
assert.NotNil(t, err, "Monitoring non-running container should fail")
|
|
|
|
err = s.Start(context.Background())
|
|
assert.Nil(t, err, "Failed to start sandbox: %v", err)
|
|
|
|
_, err = s.Monitor(context.Background())
|
|
assert.Nil(t, err, "Monitor sandbox failed: %v", err)
|
|
|
|
_, err = s.Monitor(context.Background())
|
|
assert.Nil(t, err, "Monitor sandbox again failed: %v", err)
|
|
|
|
s.monitor.stop()
|
|
}
|
|
|
|
func TestWaitProcess(t *testing.T) {
|
|
s, err := testCreateSandbox(t, testSandboxID, MockHypervisor, newHypervisorConfig(nil, nil), NetworkConfig{}, nil, nil)
|
|
assert.Nil(t, err, "VirtContainers should not allow empty sandboxes")
|
|
defer cleanUp()
|
|
|
|
contID := "foo"
|
|
execID := "bar"
|
|
_, err = s.WaitProcess(context.Background(), contID, execID)
|
|
assert.NotNil(t, err, "Wait process in stopped sandbox should fail")
|
|
|
|
err = s.Start(context.Background())
|
|
assert.Nil(t, err, "Failed to start sandbox: %v", err)
|
|
|
|
_, err = s.WaitProcess(context.Background(), contID, execID)
|
|
assert.NotNil(t, err, "Wait process in non-existing container should fail")
|
|
|
|
contConfig := newTestContainerConfigNoop(contID)
|
|
_, err = s.CreateContainer(context.Background(), contConfig)
|
|
assert.Nil(t, err, "Failed to create container %+v in sandbox %+v: %v", contConfig, s, err)
|
|
|
|
_, err = s.WaitProcess(context.Background(), contID, execID)
|
|
assert.Nil(t, err, "Wait process in ready container failed: %v", err)
|
|
|
|
_, err = s.StartContainer(context.Background(), contID)
|
|
assert.Nil(t, err, "Start container failed: %v", err)
|
|
|
|
_, err = s.WaitProcess(context.Background(), contID, execID)
|
|
assert.Nil(t, err, "Wait process failed: %v", err)
|
|
}
|
|
|
|
func TestSignalProcess(t *testing.T) {
|
|
s, err := testCreateSandbox(t, testSandboxID, MockHypervisor, newHypervisorConfig(nil, nil), NetworkConfig{}, nil, nil)
|
|
assert.Nil(t, err, "VirtContainers should not allow empty sandboxes")
|
|
defer cleanUp()
|
|
|
|
contID := "foo"
|
|
execID := "bar"
|
|
err = s.SignalProcess(context.Background(), contID, execID, syscall.SIGKILL, true)
|
|
assert.NotNil(t, err, "Wait process in stopped sandbox should fail")
|
|
|
|
err = s.Start(context.Background())
|
|
assert.Nil(t, err, "Failed to start sandbox: %v", err)
|
|
|
|
err = s.SignalProcess(context.Background(), contID, execID, syscall.SIGKILL, false)
|
|
assert.NotNil(t, err, "Wait process in non-existing container should fail")
|
|
|
|
contConfig := newTestContainerConfigNoop(contID)
|
|
_, err = s.CreateContainer(context.Background(), contConfig)
|
|
assert.Nil(t, err, "Failed to create container %+v in sandbox %+v: %v", contConfig, s, err)
|
|
|
|
err = s.SignalProcess(context.Background(), contID, execID, syscall.SIGKILL, true)
|
|
assert.Nil(t, err, "Wait process in ready container failed: %v", err)
|
|
|
|
_, err = s.StartContainer(context.Background(), contID)
|
|
assert.Nil(t, err, "Start container failed: %v", err)
|
|
|
|
err = s.SignalProcess(context.Background(), contID, execID, syscall.SIGKILL, false)
|
|
assert.Nil(t, err, "Wait process failed: %v", err)
|
|
}
|
|
|
|
func TestWinsizeProcess(t *testing.T) {
|
|
s, err := testCreateSandbox(t, testSandboxID, MockHypervisor, newHypervisorConfig(nil, nil), NetworkConfig{}, nil, nil)
|
|
assert.Nil(t, err, "VirtContainers should not allow empty sandboxes")
|
|
defer cleanUp()
|
|
|
|
contID := "foo"
|
|
execID := "bar"
|
|
err = s.WinsizeProcess(context.Background(), contID, execID, 100, 200)
|
|
assert.NotNil(t, err, "Winsize process in stopped sandbox should fail")
|
|
|
|
err = s.Start(context.Background())
|
|
assert.Nil(t, err, "Failed to start sandbox: %v", err)
|
|
|
|
err = s.WinsizeProcess(context.Background(), contID, execID, 100, 200)
|
|
assert.NotNil(t, err, "Winsize process in non-existing container should fail")
|
|
|
|
contConfig := newTestContainerConfigNoop(contID)
|
|
_, err = s.CreateContainer(context.Background(), contConfig)
|
|
assert.Nil(t, err, "Failed to create container %+v in sandbox %+v: %v", contConfig, s, err)
|
|
|
|
err = s.WinsizeProcess(context.Background(), contID, execID, 100, 200)
|
|
assert.Nil(t, err, "Winsize process in ready container failed: %v", err)
|
|
|
|
_, err = s.StartContainer(context.Background(), contID)
|
|
assert.Nil(t, err, "Start container failed: %v", err)
|
|
|
|
err = s.WinsizeProcess(context.Background(), contID, execID, 100, 200)
|
|
assert.Nil(t, err, "Winsize process failed: %v", err)
|
|
}
|
|
|
|
func TestContainerProcessIOStream(t *testing.T) {
|
|
s, err := testCreateSandbox(t, testSandboxID, MockHypervisor, newHypervisorConfig(nil, nil), NetworkConfig{}, nil, nil)
|
|
assert.Nil(t, err, "VirtContainers should not allow empty sandboxes")
|
|
defer cleanUp()
|
|
|
|
contID := "foo"
|
|
execID := "bar"
|
|
_, _, _, err = s.IOStream(contID, execID)
|
|
assert.NotNil(t, err, "Winsize process in stopped sandbox should fail")
|
|
|
|
err = s.Start(context.Background())
|
|
assert.Nil(t, err, "Failed to start sandbox: %v", err)
|
|
|
|
_, _, _, err = s.IOStream(contID, execID)
|
|
assert.NotNil(t, err, "Winsize process in non-existing container should fail")
|
|
|
|
contConfig := newTestContainerConfigNoop(contID)
|
|
_, err = s.CreateContainer(context.Background(), contConfig)
|
|
assert.Nil(t, err, "Failed to create container %+v in sandbox %+v: %v", contConfig, s, err)
|
|
|
|
_, _, _, err = s.IOStream(contID, execID)
|
|
assert.Nil(t, err, "Winsize process in ready container failed: %v", err)
|
|
|
|
_, err = s.StartContainer(context.Background(), contID)
|
|
assert.Nil(t, err, "Start container failed: %v", err)
|
|
|
|
_, _, _, err = s.IOStream(contID, execID)
|
|
assert.Nil(t, err, "Winsize process failed: %v", err)
|
|
}
|
|
|
|
func TestAttachBlockDevice(t *testing.T) {
|
|
hypervisor := &mockHypervisor{}
|
|
|
|
hConfig := HypervisorConfig{
|
|
BlockDeviceDriver: config.VirtioBlock,
|
|
}
|
|
|
|
sconfig := &SandboxConfig{
|
|
HypervisorConfig: hConfig,
|
|
}
|
|
|
|
sandbox := &Sandbox{
|
|
id: testSandboxID,
|
|
hypervisor: hypervisor,
|
|
config: sconfig,
|
|
ctx: context.Background(),
|
|
state: types.SandboxState{BlockIndexMap: make(map[int]struct{})},
|
|
}
|
|
|
|
contID := "100"
|
|
container := Container{
|
|
sandbox: sandbox,
|
|
id: contID,
|
|
}
|
|
|
|
// create state file
|
|
path := filepath.Join(fs.MockRunStoragePath(), testSandboxID, container.ID())
|
|
err := os.MkdirAll(path, DirMode)
|
|
assert.NoError(t, err)
|
|
|
|
defer os.RemoveAll(path)
|
|
|
|
path = "/dev/hda"
|
|
deviceInfo := config.DeviceInfo{
|
|
HostPath: path,
|
|
ContainerPath: path,
|
|
DevType: "b",
|
|
}
|
|
|
|
dm := manager.NewDeviceManager(config.VirtioBlock, false, "", nil)
|
|
device, err := dm.NewDevice(deviceInfo)
|
|
assert.Nil(t, err)
|
|
_, ok := device.(*drivers.BlockDevice)
|
|
assert.True(t, ok)
|
|
|
|
container.state.State = ""
|
|
index, err := sandbox.getAndSetSandboxBlockIndex()
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, index, 0)
|
|
|
|
err = device.Attach(context.Background(), sandbox)
|
|
assert.Nil(t, err)
|
|
index, err = sandbox.getAndSetSandboxBlockIndex()
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, index, 2)
|
|
|
|
err = device.Detach(context.Background(), sandbox)
|
|
assert.Nil(t, err)
|
|
index, err = sandbox.getAndSetSandboxBlockIndex()
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, index, 1)
|
|
|
|
container.state.State = types.StateReady
|
|
err = device.Attach(context.Background(), sandbox)
|
|
assert.Nil(t, err)
|
|
|
|
err = device.Detach(context.Background(), sandbox)
|
|
assert.Nil(t, err)
|
|
|
|
container.sandbox.config.HypervisorConfig.BlockDeviceDriver = config.VirtioSCSI
|
|
err = device.Attach(context.Background(), sandbox)
|
|
assert.Nil(t, err)
|
|
|
|
err = device.Detach(context.Background(), sandbox)
|
|
assert.Nil(t, err)
|
|
|
|
container.state.State = types.StateReady
|
|
err = device.Attach(context.Background(), sandbox)
|
|
assert.Nil(t, err)
|
|
|
|
err = device.Detach(context.Background(), sandbox)
|
|
assert.Nil(t, err)
|
|
}
|
|
|
|
func TestPreAddDevice(t *testing.T) {
|
|
hypervisor := &mockHypervisor{}
|
|
|
|
hConfig := HypervisorConfig{
|
|
BlockDeviceDriver: config.VirtioBlock,
|
|
}
|
|
|
|
sconfig := &SandboxConfig{
|
|
HypervisorConfig: hConfig,
|
|
}
|
|
|
|
dm := manager.NewDeviceManager(config.VirtioBlock, false, "", nil)
|
|
// create a sandbox first
|
|
sandbox := &Sandbox{
|
|
id: testSandboxID,
|
|
hypervisor: hypervisor,
|
|
config: sconfig,
|
|
devManager: dm,
|
|
ctx: context.Background(),
|
|
state: types.SandboxState{BlockIndexMap: make(map[int]struct{})},
|
|
}
|
|
|
|
contID := "100"
|
|
container := Container{
|
|
sandbox: sandbox,
|
|
id: contID,
|
|
sandboxID: testSandboxID,
|
|
}
|
|
container.state.State = types.StateReady
|
|
|
|
// create state file
|
|
path := filepath.Join(fs.MockRunStoragePath(), testSandboxID, container.ID())
|
|
err := os.MkdirAll(path, DirMode)
|
|
assert.NoError(t, err)
|
|
|
|
defer os.RemoveAll(path)
|
|
|
|
path = "/dev/hda"
|
|
deviceInfo := config.DeviceInfo{
|
|
HostPath: path,
|
|
ContainerPath: path,
|
|
DevType: "b",
|
|
}
|
|
|
|
// Add a mount device for a mountpoint before container's creation
|
|
dev, err := sandbox.AddDevice(context.Background(), deviceInfo)
|
|
assert.Nil(t, err)
|
|
|
|
// in Frakti use case, here we will create and start the container
|
|
// which will attach same device twice
|
|
container.mounts = []Mount{
|
|
{
|
|
Destination: path,
|
|
Source: path,
|
|
Type: "bind",
|
|
BlockDeviceID: dev.DeviceID(),
|
|
},
|
|
}
|
|
|
|
mounts := make(map[string]Mount)
|
|
ignoreMounts := make(map[string]Mount)
|
|
_, err = container.mountSharedDirMounts(context.Background(), mounts, ignoreMounts)
|
|
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, len(mounts), 0,
|
|
"mounts should contain nothing because it only contains a block device")
|
|
assert.Equal(t, len(ignoreMounts), 0,
|
|
"ignoreMounts should contain nothing because it only contains a block device")
|
|
}
|
|
|
|
func TestGetNetNs(t *testing.T) {
|
|
s := Sandbox{}
|
|
|
|
expected := "/foo/bar/ns/net"
|
|
network, err := NewNetwork(&NetworkConfig{NetworkID: expected})
|
|
assert.Nil(t, err)
|
|
|
|
s.network = network
|
|
netNs := s.GetNetNs()
|
|
assert.Equal(t, netNs, expected)
|
|
}
|
|
|
|
func TestSandboxStopStopped(t *testing.T) {
|
|
s := &Sandbox{
|
|
ctx: context.Background(),
|
|
state: types.SandboxState{State: types.StateStopped},
|
|
}
|
|
err := s.Stop(context.Background(), false)
|
|
|
|
assert.Nil(t, err)
|
|
}
|
|
|
|
func checkDirNotExist(path string) error {
|
|
if _, err := os.Stat(path); os.IsExist(err) {
|
|
return fmt.Errorf("%s is still exists", path)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func checkSandboxRemains() error {
|
|
var err error
|
|
if err = checkDirNotExist(sandboxDirState); err != nil {
|
|
return fmt.Errorf("%s still exists", sandboxDirState)
|
|
}
|
|
if err = checkDirNotExist(path.Join(kataHostSharedDir(), testSandboxID)); err != nil {
|
|
return fmt.Errorf("%s still exists", path.Join(kataHostSharedDir(), testSandboxID))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func TestSandboxCreationFromConfigRollbackFromCreateSandbox(t *testing.T) {
|
|
defer cleanUp()
|
|
assert := assert.New(t)
|
|
ctx := context.Background()
|
|
hConf := newHypervisorConfig(nil, nil)
|
|
sConf := SandboxConfig{
|
|
ID: testSandboxID,
|
|
HypervisorType: QemuHypervisor,
|
|
HypervisorConfig: hConf,
|
|
NetworkConfig: NetworkConfig{},
|
|
Volumes: nil,
|
|
Containers: nil,
|
|
}
|
|
|
|
// Ensure hypervisor doesn't exist
|
|
assert.NoError(os.Remove(hConf.HypervisorPath))
|
|
|
|
_, err := createSandboxFromConfig(ctx, sConf, nil)
|
|
// Fail at createSandbox: QEMU path does not exist, it is expected. Then rollback is called
|
|
assert.Error(err)
|
|
|
|
// Check dirs
|
|
err = checkSandboxRemains()
|
|
assert.NoError(err)
|
|
}
|
|
|
|
func TestSandboxUpdateResources(t *testing.T) {
|
|
contConfig1 := newTestContainerConfigNoop("cont-00001")
|
|
contConfig2 := newTestContainerConfigNoop("cont-00002")
|
|
hConfig := newHypervisorConfig(nil, nil)
|
|
|
|
defer cleanUp()
|
|
// create a sandbox
|
|
s, err := testCreateSandbox(t,
|
|
testSandboxID,
|
|
MockHypervisor,
|
|
hConfig,
|
|
NetworkConfig{},
|
|
[]ContainerConfig{contConfig1, contConfig2},
|
|
nil)
|
|
|
|
assert.NoError(t, err)
|
|
err = s.updateResources(context.Background())
|
|
assert.NoError(t, err)
|
|
|
|
containerMemLimit := int64(1000)
|
|
containerCPUPeriod := uint64(1000)
|
|
containerCPUQouta := int64(5)
|
|
for _, c := range s.config.Containers {
|
|
c.Resources.Memory = &specs.LinuxMemory{
|
|
Limit: new(int64),
|
|
}
|
|
c.Resources.CPU = &specs.LinuxCPU{
|
|
Period: new(uint64),
|
|
Quota: new(int64),
|
|
}
|
|
c.Resources.Memory.Limit = &containerMemLimit
|
|
c.Resources.CPU.Period = &containerCPUPeriod
|
|
c.Resources.CPU.Quota = &containerCPUQouta
|
|
}
|
|
err = s.updateResources(context.Background())
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestSandboxExperimentalFeature(t *testing.T) {
|
|
testFeature := exp.Feature{
|
|
Name: "mock",
|
|
Description: "exp feature for test",
|
|
ExpRelease: "1.8.0",
|
|
}
|
|
sconfig := SandboxConfig{
|
|
ID: testSandboxID,
|
|
Experimental: []exp.Feature{testFeature},
|
|
}
|
|
|
|
assert.Nil(t, exp.Get(testFeature.Name))
|
|
assert.False(t, sconfig.valid())
|
|
|
|
exp.Register(testFeature)
|
|
assert.NotNil(t, exp.Get(testFeature.Name))
|
|
assert.True(t, sconfig.valid())
|
|
}
|
|
|
|
func TestSandbox_Cgroups(t *testing.T) {
|
|
sandboxContainer := ContainerConfig{}
|
|
sandboxContainer.Annotations = make(map[string]string)
|
|
sandboxContainer.Annotations[annotations.ContainerTypeKey] = string(PodSandbox)
|
|
|
|
emptyJSONLinux := ContainerConfig{
|
|
CustomSpec: newEmptySpec(),
|
|
}
|
|
emptyJSONLinux.Annotations = make(map[string]string)
|
|
emptyJSONLinux.Annotations[annotations.ContainerTypeKey] = string(PodSandbox)
|
|
|
|
cloneSpec1 := newEmptySpec()
|
|
cloneSpec1.Linux.CgroupsPath = "/myRuntime/myContainer"
|
|
successfulContainer := ContainerConfig{
|
|
CustomSpec: cloneSpec1,
|
|
}
|
|
successfulContainer.Annotations = make(map[string]string)
|
|
successfulContainer.Annotations[annotations.ContainerTypeKey] = string(PodSandbox)
|
|
|
|
// nolint: govet
|
|
tests := []struct {
|
|
name string
|
|
s *Sandbox
|
|
wantErr bool
|
|
needRoot bool
|
|
}{
|
|
{
|
|
"New sandbox",
|
|
&Sandbox{},
|
|
true,
|
|
false,
|
|
},
|
|
{
|
|
"New sandbox, new config",
|
|
&Sandbox{config: &SandboxConfig{}},
|
|
false,
|
|
true,
|
|
},
|
|
{
|
|
"sandbox, container no sandbox type",
|
|
&Sandbox{
|
|
config: &SandboxConfig{Containers: []ContainerConfig{
|
|
{},
|
|
}}},
|
|
false,
|
|
true,
|
|
},
|
|
{
|
|
"sandbox, container sandbox type",
|
|
&Sandbox{
|
|
config: &SandboxConfig{Containers: []ContainerConfig{
|
|
sandboxContainer,
|
|
}}},
|
|
false,
|
|
true,
|
|
},
|
|
{
|
|
"sandbox, empty linux json",
|
|
&Sandbox{
|
|
config: &SandboxConfig{Containers: []ContainerConfig{
|
|
emptyJSONLinux,
|
|
}}},
|
|
false,
|
|
true,
|
|
},
|
|
{
|
|
"sandbox, successful config",
|
|
&Sandbox{
|
|
config: &SandboxConfig{Containers: []ContainerConfig{
|
|
successfulContainer,
|
|
}}},
|
|
false,
|
|
true,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
if tt.needRoot && os.Getuid() != 0 {
|
|
t.Skip(tt.name + "needs root")
|
|
}
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := tt.s.createCgroups()
|
|
t.Logf("create groups error %v", err)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("Sandbox.CreateCgroups() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
|
|
if err == nil {
|
|
if err := tt.s.setupCgroups(); (err != nil) != tt.wantErr {
|
|
t.Errorf("Sandbox.SetupCgroups() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func getContainerConfigWithCPUSet(cpuset, memset string) ContainerConfig {
|
|
return ContainerConfig{
|
|
Resources: specs.LinuxResources{
|
|
CPU: &specs.LinuxCPU{
|
|
Cpus: cpuset,
|
|
Mems: memset,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func getSimpleSandbox(cpusets, memsets [3]string) *Sandbox {
|
|
sandbox := Sandbox{}
|
|
|
|
sandbox.config = &SandboxConfig{
|
|
Containers: []ContainerConfig{
|
|
getContainerConfigWithCPUSet(cpusets[0], memsets[0]),
|
|
getContainerConfigWithCPUSet(cpusets[1], memsets[1]),
|
|
getContainerConfigWithCPUSet(cpusets[2], memsets[2]),
|
|
},
|
|
}
|
|
|
|
return &sandbox
|
|
}
|
|
|
|
func TestGetSandboxCpuSet(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
name string
|
|
cpusets [3]string
|
|
memsets [3]string
|
|
cpuResult string
|
|
memResult string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
"single, no cpuset",
|
|
[3]string{"", "", ""},
|
|
[3]string{"", "", ""},
|
|
"",
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
"single cpuset",
|
|
[3]string{"0", "", ""},
|
|
[3]string{"", "", ""},
|
|
"0",
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
"two duplicate cpuset",
|
|
[3]string{"0", "0", ""},
|
|
[3]string{"", "", ""},
|
|
"0",
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
"3 cpusets",
|
|
[3]string{"0-3", "5-7", "1"},
|
|
[3]string{"", "", ""},
|
|
"0-3,5-7",
|
|
"",
|
|
false,
|
|
},
|
|
|
|
{
|
|
"weird, but should be okay",
|
|
[3]string{"0-3", "99999", ""},
|
|
[3]string{"", "", ""},
|
|
"0-3,99999",
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
"two, overlapping cpuset",
|
|
[3]string{"0-3", "1-2", ""},
|
|
[3]string{"", "", ""},
|
|
"0-3",
|
|
"",
|
|
false,
|
|
},
|
|
{
|
|
"garbage, should fail",
|
|
[3]string{"7 beard-seconds", "Audrey + 7", "Elliott - 17"},
|
|
[3]string{"", "", ""},
|
|
"",
|
|
"",
|
|
true,
|
|
},
|
|
{
|
|
"cpuset and memset",
|
|
[3]string{"0-3", "1-2", ""},
|
|
[3]string{"0", "1", "0-1"},
|
|
"0-3",
|
|
"0-1",
|
|
false,
|
|
},
|
|
{
|
|
"memset",
|
|
[3]string{"0-3", "1-2", ""},
|
|
[3]string{"0", "3", ""},
|
|
"0-3",
|
|
"0,3",
|
|
false,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s := getSimpleSandbox(tt.cpusets, tt.memsets)
|
|
res, _, err := s.getSandboxCPUSet()
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("getSandboxCPUSet() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
if res != tt.cpuResult {
|
|
t.Errorf("getSandboxCPUSet() result = %s, wanted result %s", res, tt.cpuResult)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSandboxHugepageLimit(t *testing.T) {
|
|
contConfig1 := newTestContainerConfigNoop("cont-00001")
|
|
contConfig2 := newTestContainerConfigNoop("cont-00002")
|
|
limit := int64(4000)
|
|
contConfig1.Resources.Memory = &specs.LinuxMemory{Limit: &limit}
|
|
contConfig2.Resources.Memory = &specs.LinuxMemory{Limit: &limit}
|
|
hConfig := newHypervisorConfig(nil, nil)
|
|
|
|
defer cleanUp()
|
|
// create a sandbox
|
|
s, err := testCreateSandbox(t,
|
|
testSandboxID,
|
|
MockHypervisor,
|
|
hConfig,
|
|
NetworkConfig{},
|
|
[]ContainerConfig{contConfig1, contConfig2},
|
|
nil)
|
|
|
|
assert.NoError(t, err)
|
|
|
|
hugepageLimits := []specs.LinuxHugepageLimit{
|
|
{
|
|
Pagesize: "1GB",
|
|
Limit: 322122547,
|
|
},
|
|
{
|
|
Pagesize: "2MB",
|
|
Limit: 134217728,
|
|
},
|
|
}
|
|
|
|
for i := range s.config.Containers {
|
|
s.config.Containers[i].Resources.HugepageLimits = hugepageLimits
|
|
}
|
|
err = s.updateResources(context.Background())
|
|
assert.NoError(t, err)
|
|
}
|