Files
kata-containers/virtcontainers/api_test.go
gabrielle beyer e08f13ea31 vc: error handling for bindUnmount functionalities
Add error handling surrounding the syscall of unmounting the
container rootfs. Include a unit test to check that missing
files are not considered errors when attempting to unmount.

Fixes: #164

Signed-off-by: gabrielle beyer <gabrielle.n.beyer@intel.com>
2019-06-11 19:35:59 +00:00

1863 lines
40 KiB
Go

// Copyright (c) 2016 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package virtcontainers
import (
"context"
"fmt"
"os"
"path/filepath"
"reflect"
"strings"
"syscall"
"testing"
ktu "github.com/kata-containers/runtime/pkg/katatestutils"
"github.com/kata-containers/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/runtime/virtcontainers/pkg/mock"
vcTypes "github.com/kata-containers/runtime/virtcontainers/pkg/types"
"github.com/kata-containers/runtime/virtcontainers/store"
"github.com/kata-containers/runtime/virtcontainers/types"
"github.com/kata-containers/runtime/virtcontainers/utils"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
)
const (
containerID = "1"
)
var sandboxAnnotations = map[string]string{
"sandbox.foo": "sandbox.bar",
"sandbox.hello": "sandbox.world",
annotations.ConfigJSONKey: `{"linux":{"resources":{}}}`,
}
var containerAnnotations = map[string]string{
"container.foo": "container.bar",
"container.hello": "container.world",
annotations.ConfigJSONKey: `{"linux":{"resources":{}}}`,
}
func newBasicTestCmd() types.Cmd {
envs := []types.EnvVar{
{
Var: "PATH",
Value: "/bin:/usr/bin:/sbin:/usr/sbin",
},
}
cmd := types.Cmd{
Args: strings.Split("/bin/sh", " "),
Envs: envs,
WorkDir: "/",
}
return cmd
}
func newTestSandboxConfigNoop() SandboxConfig {
// Define the container command and bundle.
container := ContainerConfig{
ID: containerID,
RootFs: RootFs{Target: filepath.Join(testDir, testBundle), Mounted: true},
Cmd: newBasicTestCmd(),
Annotations: containerAnnotations,
}
// Sets the hypervisor configuration.
hypervisorConfig := HypervisorConfig{
KernelPath: filepath.Join(testDir, testKernel),
ImagePath: filepath.Join(testDir, testImage),
HypervisorPath: filepath.Join(testDir, testHypervisor),
}
sandboxConfig := SandboxConfig{
ID: testSandboxID,
HypervisorType: MockHypervisor,
HypervisorConfig: hypervisorConfig,
AgentType: NoopAgentType,
Containers: []ContainerConfig{container},
Annotations: sandboxAnnotations,
ProxyType: NoopProxyType,
}
return sandboxConfig
}
func newTestSandboxConfigKataAgent() SandboxConfig {
// Sets the hypervisor configuration.
hypervisorConfig := HypervisorConfig{
KernelPath: filepath.Join(testDir, testKernel),
ImagePath: filepath.Join(testDir, testImage),
HypervisorPath: filepath.Join(testDir, testHypervisor),
}
sandboxConfig := SandboxConfig{
ID: testSandboxID,
HypervisorType: MockHypervisor,
HypervisorConfig: hypervisorConfig,
AgentType: KataContainersAgent,
Annotations: sandboxAnnotations,
ProxyType: NoopProxyType,
}
return sandboxConfig
}
func TestCreateSandboxNoopAgentSuccessful(t *testing.T) {
defer cleanUp()
config := newTestSandboxConfigNoop()
p, err := CreateSandbox(context.Background(), config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
}
func TestCreateSandboxKataAgentSuccessful(t *testing.T) {
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(testDisabledAsNonRoot)
}
defer cleanUp()
config := newTestSandboxConfigKataAgent()
sockDir, err := testGenerateKataProxySockDir()
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(sockDir)
testKataProxyURL := fmt.Sprintf(testKataProxyURLTempl, sockDir)
noopProxyURL = testKataProxyURL
impl := &gRPCProxy{}
kataProxyMock := mock.ProxyGRPCMock{
GRPCImplementer: impl,
GRPCRegister: gRPCRegister,
}
if err := kataProxyMock.Start(testKataProxyURL); err != nil {
t.Fatal(err)
}
defer kataProxyMock.Stop()
p, err := CreateSandbox(context.Background(), config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
}
func TestCreateSandboxFailing(t *testing.T) {
defer cleanUp()
config := SandboxConfig{}
p, err := CreateSandbox(context.Background(), config, nil)
if p.(*Sandbox) != nil || err == nil {
t.Fatal()
}
}
func TestDeleteSandboxNoopAgentSuccessful(t *testing.T) {
defer cleanUp()
ctx := context.Background()
config := newTestSandboxConfigNoop()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
p, err = DeleteSandbox(ctx, p.ID())
if p == nil || err != nil {
t.Fatal(err)
}
_, err = os.Stat(sandboxDir)
if err == nil {
t.Fatal()
}
}
func TestDeleteSandboxKataAgentSuccessful(t *testing.T) {
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(testDisabledAsNonRoot)
}
defer cleanUp()
config := newTestSandboxConfigKataAgent()
sockDir, err := testGenerateKataProxySockDir()
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(sockDir)
testKataProxyURL := fmt.Sprintf(testKataProxyURLTempl, sockDir)
noopProxyURL = testKataProxyURL
impl := &gRPCProxy{}
kataProxyMock := mock.ProxyGRPCMock{
GRPCImplementer: impl,
GRPCRegister: gRPCRegister,
}
if err := kataProxyMock.Start(testKataProxyURL); err != nil {
t.Fatal(err)
}
defer kataProxyMock.Stop()
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
p, err = DeleteSandbox(ctx, p.ID())
if p == nil || err != nil {
t.Fatal(err)
}
_, err = os.Stat(sandboxDir)
if err == nil {
t.Fatal(err)
}
}
func TestDeleteSandboxFailing(t *testing.T) {
defer cleanUp()
sandboxDir := store.SandboxConfigurationRootPath(testSandboxID)
os.Remove(sandboxDir)
p, err := DeleteSandbox(context.Background(), testSandboxID)
if p != nil || err == nil {
t.Fatal()
}
}
func TestStartSandboxNoopAgentSuccessful(t *testing.T) {
defer cleanUp()
config := newTestSandboxConfigNoop()
p, _, err := createAndStartSandbox(context.Background(), config)
if p == nil || err != nil {
t.Fatal(err)
}
}
func TestStartSandboxKataAgentSuccessful(t *testing.T) {
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(testDisabledAsNonRoot)
}
defer cleanUp()
config := newTestSandboxConfigKataAgent()
sockDir, err := testGenerateKataProxySockDir()
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(sockDir)
testKataProxyURL := fmt.Sprintf(testKataProxyURLTempl, sockDir)
noopProxyURL = testKataProxyURL
impl := &gRPCProxy{}
kataProxyMock := mock.ProxyGRPCMock{
GRPCImplementer: impl,
GRPCRegister: gRPCRegister,
}
if err := kataProxyMock.Start(testKataProxyURL); err != nil {
t.Fatal(err)
}
defer kataProxyMock.Stop()
ctx := context.Background()
p, _, err := createAndStartSandbox(ctx, config)
if p == nil || err != nil {
t.Fatal(err)
}
pImpl, ok := p.(*Sandbox)
assert.True(t, ok)
// TODO: defaultSharedDir is a hyper var = /run/hyper/shared/sandboxes
// do we need to unmount sandboxes and containers?
if err := bindUnmountAllRootfs(ctx, testDir, pImpl); err != nil {
t.Fatal(err)
}
}
func TestStartSandboxFailing(t *testing.T) {
defer cleanUp()
sandboxDir := store.SandboxConfigurationRootPath(testSandboxID)
os.Remove(sandboxDir)
p, err := StartSandbox(context.Background(), testSandboxID)
if p != nil || err == nil {
t.Fatal()
}
}
func TestStopSandboxNoopAgentSuccessful(t *testing.T) {
defer cleanUp()
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, _, err := createAndStartSandbox(ctx, config)
if p == nil || err != nil {
t.Fatal(err)
}
vp, err := StopSandbox(ctx, p.ID())
if vp == nil || err != nil {
t.Fatal(err)
}
}
func TestPauseThenResumeSandboxNoopAgentSuccessful(t *testing.T) {
defer cleanUp()
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, _, err := createAndStartSandbox(ctx, config)
if p == nil || err != nil {
t.Fatal(err)
}
contID := "100"
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, p.ID(), contConfig)
if c == nil || err != nil {
t.Fatal(err)
}
p, err = PauseSandbox(ctx, p.ID())
if p == nil || err != nil {
t.Fatal(err)
}
pImpl, ok := p.(*Sandbox)
assert.True(t, ok)
expectedState := types.StatePaused
assert.Equal(t, pImpl.state.State, expectedState, "unexpected paused sandbox state")
for i, c := range p.GetAllContainers() {
cImpl, ok := c.(*Container)
assert.True(t, ok)
assert.Equal(t, expectedState, cImpl.state.State,
fmt.Sprintf("paused container %d has unexpected state", i))
}
p, err = ResumeSandbox(ctx, p.ID())
if p == nil || err != nil {
t.Fatal(err)
}
pImpl, ok = p.(*Sandbox)
assert.True(t, ok)
expectedState = types.StateRunning
assert.Equal(t, pImpl.state.State, expectedState, "unexpected resumed sandbox state")
for i, c := range p.GetAllContainers() {
cImpl, ok := c.(*Container)
assert.True(t, ok)
assert.Equal(t, cImpl.state.State, expectedState,
fmt.Sprintf("resumed container %d has unexpected state", i))
}
}
func TestStopSandboxKataAgentSuccessful(t *testing.T) {
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(testDisabledAsNonRoot)
}
defer cleanUp()
config := newTestSandboxConfigKataAgent()
sockDir, err := testGenerateKataProxySockDir()
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(sockDir)
testKataProxyURL := fmt.Sprintf(testKataProxyURLTempl, sockDir)
noopProxyURL = testKataProxyURL
impl := &gRPCProxy{}
kataProxyMock := mock.ProxyGRPCMock{
GRPCImplementer: impl,
GRPCRegister: gRPCRegister,
}
if err := kataProxyMock.Start(testKataProxyURL); err != nil {
t.Fatal(err)
}
defer kataProxyMock.Stop()
ctx := context.Background()
p, _, err := createAndStartSandbox(ctx, config)
if p == nil || err != nil {
t.Fatal(err)
}
p, err = StopSandbox(ctx, p.ID())
if p == nil || err != nil {
t.Fatal(err)
}
}
func TestStopSandboxFailing(t *testing.T) {
defer cleanUp()
sandboxDir := store.SandboxConfigurationRootPath(testSandboxID)
os.Remove(sandboxDir)
p, err := StopSandbox(context.Background(), testSandboxID)
if p != nil || err == nil {
t.Fatal()
}
}
func TestRunSandboxNoopAgentSuccessful(t *testing.T) {
defer cleanUp()
config := newTestSandboxConfigNoop()
p, err := RunSandbox(context.Background(), config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
}
func TestRunSandboxKataAgentSuccessful(t *testing.T) {
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(testDisabledAsNonRoot)
}
defer cleanUp()
config := newTestSandboxConfigKataAgent()
sockDir, err := testGenerateKataProxySockDir()
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(sockDir)
testKataProxyURL := fmt.Sprintf(testKataProxyURLTempl, sockDir)
noopProxyURL = testKataProxyURL
impl := &gRPCProxy{}
kataProxyMock := mock.ProxyGRPCMock{
GRPCImplementer: impl,
GRPCRegister: gRPCRegister,
}
if err := kataProxyMock.Start(testKataProxyURL); err != nil {
t.Fatal(err)
}
defer kataProxyMock.Stop()
ctx := context.Background()
p, err := RunSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
pImpl, ok := p.(*Sandbox)
assert.True(t, ok)
if err := bindUnmountAllRootfs(ctx, testDir, pImpl); err != nil {
t.Fatal(err)
}
}
func TestRunSandboxFailing(t *testing.T) {
defer cleanUp()
config := SandboxConfig{}
p, err := RunSandbox(context.Background(), config, nil)
if p != nil || err == nil {
t.Fatal()
}
}
func TestListSandboxSuccessful(t *testing.T) {
defer cleanUp()
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
_, err = ListSandbox(ctx)
if err != nil {
t.Fatal(err)
}
}
func TestListSandboxNoSandboxDirectory(t *testing.T) {
defer cleanUp()
_, err := ListSandbox(context.Background())
if err != nil {
t.Fatal(fmt.Sprintf("unexpected ListSandbox error from non-existent sandbox directory: %v", err))
}
}
func TestStatusSandboxSuccessfulStateReady(t *testing.T) {
defer cleanUp()
config := newTestSandboxConfigNoop()
cgroupPath, err := renameCgroupPath(utils.DefaultCgroupPath)
if err != nil {
t.Fatal(err)
}
hypervisorConfig := HypervisorConfig{
KernelPath: filepath.Join(testDir, testKernel),
ImagePath: filepath.Join(testDir, testImage),
HypervisorPath: filepath.Join(testDir, testHypervisor),
NumVCPUs: defaultVCPUs,
MemorySize: defaultMemSzMiB,
DefaultBridges: defaultBridges,
BlockDeviceDriver: defaultBlockDriver,
DefaultMaxVCPUs: defaultMaxQemuVCPUs,
Msize9p: defaultMsize9p,
}
expectedStatus := SandboxStatus{
ID: testSandboxID,
State: types.SandboxState{
State: types.StateReady,
},
Hypervisor: MockHypervisor,
HypervisorConfig: hypervisorConfig,
Agent: NoopAgentType,
Annotations: sandboxAnnotations,
ContainersStatus: []ContainerStatus{
{
ID: containerID,
State: types.ContainerState{
State: types.StateReady,
CgroupPath: cgroupPath,
},
PID: 0,
RootFs: filepath.Join(testDir, testBundle),
Annotations: containerAnnotations,
},
},
}
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
status, err := StatusSandbox(ctx, p.ID())
if err != nil {
t.Fatal(err)
}
// Copy the start time as we can't pretend we know what that
// value will be.
expectedStatus.ContainersStatus[0].StartTime = status.ContainersStatus[0].StartTime
if reflect.DeepEqual(status, expectedStatus) == false {
t.Fatalf("Got sandbox status %v\n expecting %v", status, expectedStatus)
}
}
func TestStatusSandboxSuccessfulStateRunning(t *testing.T) {
defer cleanUp()
config := newTestSandboxConfigNoop()
cgroupPath, err := renameCgroupPath(utils.DefaultCgroupPath)
if err != nil {
t.Fatal(err)
}
hypervisorConfig := HypervisorConfig{
KernelPath: filepath.Join(testDir, testKernel),
ImagePath: filepath.Join(testDir, testImage),
HypervisorPath: filepath.Join(testDir, testHypervisor),
NumVCPUs: defaultVCPUs,
MemorySize: defaultMemSzMiB,
DefaultBridges: defaultBridges,
BlockDeviceDriver: defaultBlockDriver,
DefaultMaxVCPUs: defaultMaxQemuVCPUs,
Msize9p: defaultMsize9p,
}
expectedStatus := SandboxStatus{
ID: testSandboxID,
State: types.SandboxState{
State: types.StateRunning,
},
Hypervisor: MockHypervisor,
HypervisorConfig: hypervisorConfig,
Agent: NoopAgentType,
Annotations: sandboxAnnotations,
ContainersStatus: []ContainerStatus{
{
ID: containerID,
State: types.ContainerState{
State: types.StateRunning,
CgroupPath: cgroupPath,
},
PID: 0,
RootFs: filepath.Join(testDir, testBundle),
Annotations: containerAnnotations,
},
},
}
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
p, err = StartSandbox(ctx, p.ID())
if p == nil || err != nil {
t.Fatal(err)
}
status, err := StatusSandbox(ctx, p.ID())
if err != nil {
t.Fatal(err)
}
// Copy the start time as we can't pretend we know what that
// value will be.
expectedStatus.ContainersStatus[0].StartTime = status.ContainersStatus[0].StartTime
if reflect.DeepEqual(status, expectedStatus) == false {
t.Fatalf("Got sandbox status %v\n expecting %v", status, expectedStatus)
}
}
func TestStatusSandboxFailingFetchSandboxConfig(t *testing.T) {
defer cleanUp()
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
store.DeleteAll()
globalSandboxList.removeSandbox(p.ID())
_, err = StatusSandbox(ctx, p.ID())
if err == nil {
t.Fatal()
}
}
func TestStatusPodSandboxFailingFetchSandboxState(t *testing.T) {
defer cleanUp()
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
store.DeleteAll()
globalSandboxList.removeSandbox(p.ID())
_, err = StatusSandbox(ctx, p.ID())
if err == nil {
t.Fatal()
}
}
func newTestContainerConfigNoop(contID string) ContainerConfig {
// Define the container command and bundle.
container := ContainerConfig{
ID: contID,
RootFs: RootFs{Target: filepath.Join(testDir, testBundle), Mounted: true},
Cmd: newBasicTestCmd(),
Annotations: containerAnnotations,
}
return container
}
func TestCreateContainerSuccessful(t *testing.T) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, p.ID(), contConfig)
if c == nil || err != nil {
t.Fatal(err)
}
contDir := filepath.Join(sandboxDir, contID)
_, err = os.Stat(contDir)
if err != nil {
t.Fatal(err)
}
}
func TestCreateContainerFailingNoSandbox(t *testing.T) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
p, err = DeleteSandbox(ctx, p.ID())
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err == nil {
t.Fatal()
}
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, p.ID(), contConfig)
if c != nil || err == nil {
t.Fatal(err)
}
}
func TestDeleteContainerSuccessful(t *testing.T) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, p.ID(), contConfig)
if c == nil || err != nil {
t.Fatal(err)
}
contDir := filepath.Join(sandboxDir, contID)
_, err = os.Stat(contDir)
if err != nil {
t.Fatal(err)
}
c, err = DeleteContainer(ctx, p.ID(), contID)
if c == nil || err != nil {
t.Fatal(err)
}
_, err = os.Stat(contDir)
if err == nil {
t.Fatal()
}
}
func TestDeleteContainerFailingNoSandbox(t *testing.T) {
defer cleanUp()
contID := "100"
c, err := DeleteContainer(context.Background(), testSandboxID, contID)
if c != nil || err == nil {
t.Fatal()
}
}
func TestDeleteContainerFailingNoContainer(t *testing.T) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
c, err := DeleteContainer(ctx, p.ID(), contID)
if c != nil || err == nil {
t.Fatal()
}
}
func TestStartContainerNoopAgentSuccessful(t *testing.T) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, sandboxDir, err := createAndStartSandbox(ctx, config)
if p == nil || err != nil {
t.Fatal(err)
}
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, p.ID(), contConfig)
if c == nil || err != nil {
t.Fatal(err)
}
contDir := filepath.Join(sandboxDir, contID)
_, err = os.Stat(contDir)
if err != nil {
t.Fatal(err)
}
c, err = StartContainer(ctx, p.ID(), contID)
if c == nil || err != nil {
t.Fatal(err)
}
}
func TestStartContainerFailingNoSandbox(t *testing.T) {
defer cleanUp()
contID := "100"
c, err := StartContainer(context.Background(), testSandboxID, contID)
if c != nil || err == nil {
t.Fatal()
}
}
func TestStartContainerFailingNoContainer(t *testing.T) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
c, err := StartContainer(ctx, p.ID(), contID)
if c != nil || err == nil {
t.Fatal()
}
}
func TestStartContainerFailingSandboxNotStarted(t *testing.T) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, p.ID(), contConfig)
if c == nil || err != nil {
t.Fatal(err)
}
contDir := filepath.Join(sandboxDir, contID)
_, err = os.Stat(contDir)
if err != nil {
t.Fatal(err)
}
_, err = StartContainer(ctx, p.ID(), contID)
if err == nil {
t.Fatal("Function should have failed")
}
}
func TestStopContainerNoopAgentSuccessful(t *testing.T) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, sandboxDir, err := createAndStartSandbox(ctx, config)
if p == nil || err != nil {
t.Fatal(err)
}
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, p.ID(), contConfig)
if c == nil || err != nil {
t.Fatal(err)
}
contDir := filepath.Join(sandboxDir, contID)
_, err = os.Stat(contDir)
if err != nil {
t.Fatal(err)
}
c, err = StartContainer(ctx, p.ID(), contID)
if c == nil || err != nil {
t.Fatal(err)
}
c, err = StopContainer(ctx, p.ID(), contID)
if c == nil || err != nil {
t.Fatal(err)
}
}
func TestStopContainerFailingNoSandbox(t *testing.T) {
defer cleanUp()
contID := "100"
c, err := StopContainer(context.Background(), testSandboxID, contID)
if c != nil || err == nil {
t.Fatal()
}
}
func TestStopContainerFailingNoContainer(t *testing.T) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
c, err := StopContainer(ctx, p.ID(), contID)
if c != nil || err == nil {
t.Fatal()
}
}
func testKillContainerFromContReadySuccessful(t *testing.T, signal syscall.Signal) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, sandboxDir, err := createAndStartSandbox(ctx, config)
if p == nil || err != nil {
t.Fatal(err)
}
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, p.ID(), contConfig)
if c == nil || err != nil {
t.Fatal(err)
}
contDir := filepath.Join(sandboxDir, contID)
_, err = os.Stat(contDir)
if err != nil {
t.Fatal(err)
}
if err := KillContainer(ctx, p.ID(), contID, signal, false); err != nil {
t.Fatal()
}
}
func TestKillContainerFromContReadySuccessful(t *testing.T) {
// SIGUSR1
testKillContainerFromContReadySuccessful(t, syscall.SIGUSR1)
// SIGUSR2
testKillContainerFromContReadySuccessful(t, syscall.SIGUSR2)
// SIGKILL
testKillContainerFromContReadySuccessful(t, syscall.SIGKILL)
// SIGTERM
testKillContainerFromContReadySuccessful(t, syscall.SIGTERM)
}
func TestEnterContainerNoopAgentSuccessful(t *testing.T) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, sandboxDir, err := createAndStartSandbox(ctx, config)
if p == nil || err != nil {
t.Fatal(err)
}
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, p.ID(), contConfig)
if c == nil || err != nil {
t.Fatal(err)
}
contDir := filepath.Join(sandboxDir, contID)
_, err = os.Stat(contDir)
if err != nil {
t.Fatal(err)
}
c, err = StartContainer(ctx, p.ID(), contID)
if c == nil || err != nil {
t.Fatal(err)
}
cmd := newBasicTestCmd()
_, c, _, err = EnterContainer(ctx, p.ID(), contID, cmd)
if c == nil || err != nil {
t.Fatal(err)
}
}
func TestEnterContainerFailingNoSandbox(t *testing.T) {
defer cleanUp()
contID := "100"
cmd := newBasicTestCmd()
_, c, _, err := EnterContainer(context.Background(), testSandboxID, contID, cmd)
if c != nil || err == nil {
t.Fatal()
}
}
func TestEnterContainerFailingNoContainer(t *testing.T) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
cmd := newBasicTestCmd()
_, c, _, err := EnterContainer(ctx, p.ID(), contID, cmd)
if c != nil || err == nil {
t.Fatal()
}
}
func TestEnterContainerFailingContNotStarted(t *testing.T) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, sandboxDir, err := createAndStartSandbox(ctx, config)
if p == nil || err != nil {
t.Fatal(err)
}
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, p.ID(), contConfig)
if c == nil || err != nil {
t.Fatal(err)
}
contDir := filepath.Join(sandboxDir, contID)
_, err = os.Stat(contDir)
if err != nil {
t.Fatal(err)
}
cmd := newBasicTestCmd()
_, c, _, err = EnterContainer(ctx, p.ID(), contID, cmd)
if c == nil || err != nil {
t.Fatal()
}
}
func TestStatusContainerSuccessful(t *testing.T) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, p.ID(), contConfig)
if c == nil || err != nil {
t.Fatal(err)
}
contDir := filepath.Join(sandboxDir, contID)
_, err = os.Stat(contDir)
if err != nil {
t.Fatal(err)
}
status, err := StatusContainer(ctx, p.ID(), contID)
if err != nil {
t.Fatal(err)
}
pImpl, ok := p.(*Sandbox)
assert.True(t, ok)
cImpl, ok := c.(*Container)
assert.True(t, ok)
if status.StartTime.Equal(cImpl.process.StartTime) == false {
t.Fatalf("Got container start time %v, expecting %v", status.StartTime, cImpl.process.StartTime)
}
if reflect.DeepEqual(pImpl.config.Containers[0].Annotations, status.Annotations) == false {
t.Fatalf("Got annotations %v\n expecting %v", status.Annotations, pImpl.config.Containers[0].Annotations)
}
}
func TestStatusContainerStateReady(t *testing.T) {
defer cleanUp()
// (homage to a great album! ;)
contID := "101"
config := newTestSandboxConfigNoop()
cgroupPath, err := renameCgroupPath(utils.DefaultCgroupPath)
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, p.ID(), contConfig)
if c == nil || err != nil {
t.Fatal(err)
}
contDir := filepath.Join(sandboxDir, contID)
_, err = os.Stat(contDir)
if err != nil {
t.Fatal(err)
}
// fresh lookup
p2, err := fetchSandbox(ctx, p.ID())
if err != nil {
t.Fatal(err)
}
defer p2.releaseStatelessSandbox()
expectedStatus := ContainerStatus{
ID: contID,
State: types.ContainerState{
State: types.StateReady,
CgroupPath: cgroupPath,
},
PID: 0,
RootFs: filepath.Join(testDir, testBundle),
Annotations: containerAnnotations,
}
defer p2.wg.Wait()
status, err := statusContainer(p2, contID)
if err != nil {
t.Fatal(err)
}
// Copy the start time as we can't pretend we know what that
// value will be.
expectedStatus.StartTime = status.StartTime
if reflect.DeepEqual(status, expectedStatus) == false {
t.Fatalf("Got container status %v, expected %v", status, expectedStatus)
}
}
func TestStatusContainerStateRunning(t *testing.T) {
defer cleanUp()
// (homage to a great album! ;)
contID := "101"
config := newTestSandboxConfigNoop()
cgroupPath, err := renameCgroupPath(utils.DefaultCgroupPath)
if err != nil {
t.Fatal(err)
}
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
p, err = StartSandbox(ctx, p.ID())
if p == nil || err != nil {
t.Fatal(err)
}
sandboxDir := store.SandboxConfigurationRootPath(p.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
t.Fatal(err)
}
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, p.ID(), contConfig)
if c == nil || err != nil {
t.Fatal(err)
}
c, err = StartContainer(ctx, p.ID(), c.ID())
if c == nil || err != nil {
t.Fatal(err)
}
contDir := filepath.Join(sandboxDir, contID)
_, err = os.Stat(contDir)
if err != nil {
t.Fatal(err)
}
// fresh lookup
p2, err := fetchSandbox(ctx, p.ID())
if err != nil {
t.Fatal(err)
}
defer p2.releaseStatelessSandbox()
expectedStatus := ContainerStatus{
ID: contID,
State: types.ContainerState{
State: types.StateRunning,
CgroupPath: cgroupPath,
},
PID: 0,
RootFs: filepath.Join(testDir, testBundle),
Annotations: containerAnnotations,
}
defer p2.wg.Wait()
status, err := statusContainer(p2, contID)
if err != nil {
t.Fatal(err)
}
// Copy the start time as we can't pretend we know what that
// value will be.
expectedStatus.StartTime = status.StartTime
if reflect.DeepEqual(status, expectedStatus) == false {
t.Fatalf("Got container status %v, expected %v", status, expectedStatus)
}
}
func TestStatusContainerFailing(t *testing.T) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
store.DeleteAll()
globalSandboxList.removeSandbox(p.ID())
_, err = StatusContainer(ctx, p.ID(), contID)
if err == nil {
t.Fatal()
}
}
func TestStatsContainerFailing(t *testing.T) {
defer cleanUp()
contID := "100"
config := newTestSandboxConfigNoop()
ctx := context.Background()
p, err := CreateSandbox(ctx, config, nil)
if p == nil || err != nil {
t.Fatal(err)
}
store.DeleteAll()
globalSandboxList.removeSandbox(p.ID())
_, err = StatsContainer(ctx, p.ID(), contID)
if err == nil {
t.Fatal()
}
}
func TestStatsContainer(t *testing.T) {
defer cleanUp()
assert := assert.New(t)
contID := "100"
ctx := context.Background()
_, err := StatsContainer(ctx, "", "")
assert.Error(err)
_, err = StatsContainer(ctx, "abc", "")
assert.Error(err)
_, err = StatsContainer(ctx, "abc", "abc")
assert.Error(err)
config := newTestSandboxConfigNoop()
p, err := CreateSandbox(ctx, config, nil)
assert.NoError(err)
assert.NotNil(p)
p, err = StartSandbox(ctx, p.ID())
if p == nil || err != nil {
t.Fatal(err)
}
pImpl, ok := p.(*Sandbox)
assert.True(ok)
defer store.DeleteAll()
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, p.ID(), contConfig)
assert.NoError(err)
assert.NotNil(c)
_, err = StatsContainer(ctx, pImpl.id, "xyz")
assert.Error(err)
_, err = StatsContainer(ctx, "xyz", contID)
assert.Error(err)
stats, err := StatsContainer(ctx, pImpl.id, contID)
assert.NoError(err)
assert.Equal(stats, ContainerStats{})
}
func TestProcessListContainer(t *testing.T) {
defer cleanUp()
assert := assert.New(t)
contID := "abc"
options := ProcessListOptions{
Format: "json",
Args: []string{"-ef"},
}
ctx := context.Background()
_, err := ProcessListContainer(ctx, "", "", options)
assert.Error(err)
_, err = ProcessListContainer(ctx, "xyz", "", options)
assert.Error(err)
_, err = ProcessListContainer(ctx, "xyz", "xyz", options)
assert.Error(err)
config := newTestSandboxConfigNoop()
p, err := CreateSandbox(ctx, config, nil)
assert.NoError(err)
assert.NotNil(p)
pImpl, ok := p.(*Sandbox)
assert.True(ok)
defer store.DeleteAll()
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, p.ID(), contConfig)
assert.NoError(err)
assert.NotNil(c)
_, err = ProcessListContainer(ctx, pImpl.id, "xyz", options)
assert.Error(err)
_, err = ProcessListContainer(ctx, "xyz", contID, options)
assert.Error(err)
_, err = ProcessListContainer(ctx, pImpl.id, contID, options)
// Sandbox not running, impossible to ps the container
assert.Error(err)
}
/*
* Benchmarks
*/
func createNewSandboxConfig(hType HypervisorType, aType AgentType, aConfig interface{}) SandboxConfig {
hypervisorConfig := HypervisorConfig{
KernelPath: "/usr/share/kata-containers/vmlinux.container",
ImagePath: "/usr/share/kata-containers/kata-containers.img",
HypervisorPath: "/usr/bin/qemu-system-x86_64",
}
netConfig := NetworkConfig{}
return SandboxConfig{
ID: testSandboxID,
HypervisorType: hType,
HypervisorConfig: hypervisorConfig,
AgentType: aType,
AgentConfig: aConfig,
NetworkConfig: netConfig,
ProxyType: NoopProxyType,
}
}
// createAndStartSandbox handles the common test operation of creating and
// starting a sandbox.
func createAndStartSandbox(ctx context.Context, config SandboxConfig) (sandbox VCSandbox, sandboxDir string,
err error) {
// Create sandbox
sandbox, err = CreateSandbox(ctx, config, nil)
if sandbox == nil || err != nil {
return nil, "", err
}
sandboxDir = store.SandboxConfigurationRootPath(sandbox.ID())
_, err = os.Stat(sandboxDir)
if err != nil {
return nil, "", err
}
// Start sandbox
sandbox, err = StartSandbox(ctx, sandbox.ID())
if sandbox == nil || err != nil {
return nil, "", err
}
return sandbox, sandboxDir, nil
}
func createStartStopDeleteSandbox(b *testing.B, sandboxConfig SandboxConfig) {
ctx := context.Background()
p, _, err := createAndStartSandbox(ctx, sandboxConfig)
if p == nil || err != nil {
b.Fatalf("Could not create and start sandbox: %s", err)
}
// Stop sandbox
_, err = StopSandbox(ctx, p.ID())
if err != nil {
b.Fatalf("Could not stop sandbox: %s", err)
}
// Delete sandbox
_, err = DeleteSandbox(ctx, p.ID())
if err != nil {
b.Fatalf("Could not delete sandbox: %s", err)
}
}
func BenchmarkCreateStartStopDeleteSandboxQemuHypervisorNoopAgentNetworkNoop(b *testing.B) {
for i := 0; i < b.N; i++ {
sandboxConfig := createNewSandboxConfig(QemuHypervisor, NoopAgentType, nil)
createStartStopDeleteSandbox(b, sandboxConfig)
}
}
func BenchmarkCreateStartStopDeleteSandboxMockHypervisorNoopAgentNetworkNoop(b *testing.B) {
for i := 0; i < b.N; i++ {
sandboxConfig := createNewSandboxConfig(MockHypervisor, NoopAgentType, nil)
createStartStopDeleteSandbox(b, sandboxConfig)
}
}
func TestFetchSandbox(t *testing.T) {
defer cleanUp()
config := newTestSandboxConfigNoop()
ctx := context.Background()
s, err := CreateSandbox(ctx, config, nil)
if s == nil || err != nil {
t.Fatal(err)
}
fetched, err := FetchSandbox(ctx, s.ID())
assert.Nil(t, err, "%v", err)
assert.True(t, fetched != s, "fetched stateless sandboxes should not match")
}
func TestFetchStatefulSandbox(t *testing.T) {
defer cleanUp()
config := newTestSandboxConfigNoop()
config.Stateful = true
ctx := context.Background()
s, err := CreateSandbox(ctx, config, nil)
if s == nil || err != nil {
t.Fatal(err)
}
fetched, err := FetchSandbox(ctx, s.ID())
assert.Nil(t, err, "%v", err)
assert.Equal(t, fetched, s, "fetched stateful sandboxed should match")
}
func TestFetchNonExistingSandbox(t *testing.T) {
defer cleanUp()
_, err := FetchSandbox(context.Background(), "some-non-existing-sandbox-name")
assert.NotNil(t, err, "fetch non-existing sandbox should fail")
}
func TestReleaseSandbox(t *testing.T) {
defer cleanUp()
config := newTestSandboxConfigNoop()
s, err := CreateSandbox(context.Background(), config, nil)
if s == nil || err != nil {
t.Fatal(err)
}
err = s.Release()
assert.Nil(t, err, "sandbox release failed: %v", err)
}
func TestUpdateContainer(t *testing.T) {
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(testDisabledAsNonRoot)
}
defer cleanUp()
ctx := context.Background()
period := uint64(1000)
quota := int64(2000)
memoryLimit := int64(1073741824)
memorySwap := int64(1073741824)
assert := assert.New(t)
resources := specs.LinuxResources{
CPU: &specs.LinuxCPU{
Period: &period,
Quota: &quota,
},
Memory: &specs.LinuxMemory{
Limit: &memoryLimit,
Swap: &memorySwap,
},
}
err := UpdateContainer(ctx, "", "", resources)
assert.Error(err)
err = UpdateContainer(ctx, "abc", "", resources)
assert.Error(err)
contID := "100"
config := newTestSandboxConfigNoop()
s, sandboxDir, err := createAndStartSandbox(ctx, config)
assert.NoError(err)
assert.NotNil(s)
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, s.ID(), contConfig)
assert.NoError(err)
assert.NotNil(c)
contDir := filepath.Join(sandboxDir, contID)
_, err = os.Stat(contDir)
assert.NoError(err)
_, err = StartContainer(ctx, s.ID(), contID)
assert.NoError(err)
err = UpdateContainer(ctx, s.ID(), contID, resources)
assert.NoError(err)
}
func TestPauseResumeContainer(t *testing.T) {
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(testDisabledAsNonRoot)
}
defer cleanUp()
ctx := context.Background()
assert := assert.New(t)
err := PauseContainer(ctx, "", "")
assert.Error(err)
err = PauseContainer(ctx, "abc", "")
assert.Error(err)
contID := "100"
config := newTestSandboxConfigNoop()
s, sandboxDir, err := createAndStartSandbox(ctx, config)
assert.NoError(err)
assert.NotNil(s)
contConfig := newTestContainerConfigNoop(contID)
_, c, err := CreateContainer(ctx, s.ID(), contConfig)
assert.NoError(err)
assert.NotNil(c)
contDir := filepath.Join(sandboxDir, contID)
_, err = os.Stat(contDir)
assert.NoError(err)
_, err = StartContainer(ctx, s.ID(), contID)
assert.NoError(err)
err = PauseContainer(ctx, s.ID(), contID)
assert.NoError(err)
err = ResumeContainer(ctx, s.ID(), contID)
assert.NoError(err)
}
func TestNetworkOperation(t *testing.T) {
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(testDisabledAsNonRoot)
}
defer cleanUp()
assert := assert.New(t)
inf := &vcTypes.Interface{
Name: "eno1",
Mtu: 1500,
HwAddr: "02:00:ca:fe:00:48",
}
ip := vcTypes.IPAddress{
Family: 0,
Address: "192.168.0.101",
Mask: "24",
}
inf.IPAddresses = append(inf.IPAddresses, &ip)
ctx := context.Background()
_, err := AddInterface(ctx, "", inf)
assert.Error(err)
_, err = AddInterface(ctx, "abc", inf)
assert.Error(err)
netNSPath, err := createNetNS()
assert.NoError(err)
defer deleteNetNS(netNSPath)
config := newTestSandboxConfigNoop()
config.NetworkConfig = NetworkConfig{
NetNSPath: netNSPath,
}
s, _, err := createAndStartSandbox(ctx, config)
assert.NoError(err)
assert.NotNil(s)
_, err = AddInterface(ctx, s.ID(), inf)
assert.Error(err)
_, err = RemoveInterface(ctx, s.ID(), inf)
assert.NoError(err)
_, err = ListInterfaces(ctx, s.ID())
assert.NoError(err)
_, err = UpdateRoutes(ctx, s.ID(), nil)
assert.NoError(err)
_, err = ListRoutes(ctx, s.ID())
assert.NoError(err)
}