Files
kata-containers/src/runtime/pkg/katautils/create_test.go
David Gibson 90b2f5b776 runtime: Make SetupOCIConfigFile clean up after itself
SetupOCIConfigFile creates a temporary directory with os.MkDirTemp().  This
means the callers need to register a deferred function to remove it again.
At least one of them was commented out meaning that a /temp/katatest-
directory was leftover after the unit tests ran.

Change to using t.TempDir() which as well as better matching other parts of
the tests means the testing framework will handle cleaning it up.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
2022-04-22 14:14:52 +10:00

424 lines
11 KiB
Go

// Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc.
// Copyright (c) 2021 Adobe Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
package katautils
import (
"context"
"errors"
"fmt"
"os"
"path"
"path/filepath"
"strings"
"syscall"
"testing"
ktu "github.com/kata-containers/kata-containers/src/runtime/pkg/katatestutils"
"github.com/kata-containers/kata-containers/src/runtime/pkg/oci"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/compatoci"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
)
const (
testConsole = "/dev/pts/999"
testContainerTypeAnnotation = "io.kubernetes.cri-o.ContainerType"
testSandboxIDAnnotation = "io.kubernetes.cri-o.SandboxID"
testContainerTypeContainer = "container"
)
var (
// testingImpl is a concrete mock RVC implementation used for testing
testingImpl = &vcmock.VCMock{}
// mock sandbox
mockSandbox = &vcmock.Sandbox{
MockID: testSandboxID,
}
tc ktu.TestConstraint
)
func init() {
tc = ktu.NewTestConstraint(false)
}
// newTestRuntimeConfig creates a new RuntimeConfig
func newTestRuntimeConfig(dir, consolePath string, create bool) (oci.RuntimeConfig, error) {
if dir == "" {
return oci.RuntimeConfig{}, errors.New("BUG: need directory")
}
hypervisorConfig, err := newTestHypervisorConfig(dir, create)
if err != nil {
return oci.RuntimeConfig{}, err
}
return oci.RuntimeConfig{
HypervisorType: vc.QemuHypervisor,
HypervisorConfig: hypervisorConfig,
Console: consolePath,
}, nil
}
// newTestHypervisorConfig creaets a new virtcontainers
// HypervisorConfig, ensuring that the required resources are also
// created.
//
// Note: no parameter validation in case caller wishes to create an invalid
// object.
func newTestHypervisorConfig(dir string, create bool) (vc.HypervisorConfig, error) {
kernelPath := path.Join(dir, "kernel")
imagePath := path.Join(dir, "image")
hypervisorPath := path.Join(dir, "hypervisor")
if create {
for _, file := range []string{kernelPath, imagePath, hypervisorPath} {
err := createEmptyFile(file)
if err != nil {
return vc.HypervisorConfig{}, err
}
}
}
return vc.HypervisorConfig{
KernelPath: kernelPath,
ImagePath: imagePath,
HypervisorPath: hypervisorPath,
HypervisorMachineType: "q35",
}, nil
}
// return the value of the *last* param with the specified key
func findLastParam(key string, params []vc.Param) (string, error) {
if key == "" {
return "", errors.New("ERROR: need non-nil key")
}
l := len(params)
if l == 0 {
return "", errors.New("ERROR: no params")
}
for i := l - 1; i >= 0; i-- {
p := params[i]
if key == p.Key {
return p.Value, nil
}
}
return "", fmt.Errorf("no param called %q found", NAME)
}
func TestSetEphemeralStorageType(t *testing.T) {
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(ktu.TestDisabledNeedRoot)
}
assert := assert.New(t)
dir := t.TempDir()
ephePath := filepath.Join(dir, vc.K8sEmptyDir, "tmp-volume")
err := os.MkdirAll(ephePath, testDirMode)
assert.Nil(err)
err = syscall.Mount("tmpfs", ephePath, "tmpfs", 0, "")
assert.Nil(err)
defer syscall.Unmount(ephePath, 0)
ociSpec := specs.Spec{}
var ociMounts []specs.Mount
mount := specs.Mount{
Source: ephePath,
}
ociMounts = append(ociMounts, mount)
ociSpec.Mounts = ociMounts
ociSpec = SetEphemeralStorageType(ociSpec, false)
mountType := ociSpec.Mounts[0].Type
assert.Equal(mountType, "ephemeral",
"Unexpected mount type, got %s expected ephemeral", mountType)
}
func TestSetKernelParams(t *testing.T) {
assert := assert.New(t)
config := oci.RuntimeConfig{}
assert.Empty(config.HypervisorConfig.KernelParams)
err := SetKernelParams(&config)
assert.NoError(err)
config.HypervisorConfig.BlockDeviceDriver = "virtio-scsi"
err = SetKernelParams(&config)
assert.NoError(err)
if needSystemd(config.HypervisorConfig) {
assert.NotEmpty(config.HypervisorConfig.KernelParams)
}
}
func TestSetKernelParamsUserOptionTakesPriority(t *testing.T) {
assert := assert.New(t)
initName := "init"
initValue := "/sbin/myinit"
ipName := "ip"
ipValue := "127.0.0.1"
params := []vc.Param{
{Key: initName, Value: initValue},
{Key: ipName, Value: ipValue},
}
hypervisorConfig := vc.HypervisorConfig{
KernelParams: params,
}
// Config containing user-specified kernel parameters
config := oci.RuntimeConfig{
HypervisorConfig: hypervisorConfig,
}
assert.NotEmpty(config.HypervisorConfig.KernelParams)
err := SetKernelParams(&config)
assert.NoError(err)
kernelParams := config.HypervisorConfig.KernelParams
init, err := findLastParam(initName, kernelParams)
assert.NoError(err)
assert.Equal(initValue, init)
ip, err := findLastParam(ipName, kernelParams)
assert.NoError(err)
assert.Equal(ipValue, ip)
}
func TestCreateSandboxConfigFail(t *testing.T) {
assert := assert.New(t)
tmpdir, bundlePath, _ := ktu.SetupOCIConfigFile(t)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
quota := int64(0)
limit := int64(0)
spec.Linux.Resources.Memory = &specs.LinuxMemory{
Limit: &limit,
}
spec.Linux.Resources.CPU = &specs.LinuxCPU{
// specify an invalid value
Quota: &quota,
}
rootFs := vc.RootFs{Mounted: true}
_, _, err = CreateSandbox(context.Background(), testingImpl, spec, runtimeConfig, rootFs, testContainerID, bundlePath, testConsole, true, true)
assert.Error(err)
}
func TestCreateSandboxFail(t *testing.T) {
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(ktu.TestDisabledNeedRoot)
}
assert := assert.New(t)
tmpdir, bundlePath, _ := ktu.SetupOCIConfigFile(t)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
rootFs := vc.RootFs{Mounted: true}
_, _, err = CreateSandbox(context.Background(), testingImpl, spec, runtimeConfig, rootFs, testContainerID, bundlePath, testConsole, true, true)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
}
func TestCreateSandboxAnnotations(t *testing.T) {
if tc.NotValid(ktu.NeedRoot()) {
t.Skip(ktu.TestDisabledNeedRoot)
}
assert := assert.New(t)
tmpdir, bundlePath, _ := ktu.SetupOCIConfigFile(t)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
rootFs := vc.RootFs{Mounted: true}
testingImpl.CreateSandboxFunc = func(ctx context.Context, sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error) {
return &vcmock.Sandbox{
MockID: testSandboxID,
MockContainers: []*vcmock.Container{
{MockID: testContainerID},
},
MockAnnotations: sandboxConfig.Annotations,
}, nil
}
defer func() {
testingImpl.CreateSandboxFunc = nil
}()
sandbox, _, err := CreateSandbox(context.Background(), testingImpl, spec, runtimeConfig, rootFs, testContainerID, bundlePath, testConsole, true, true)
assert.NoError(err)
netNsPath, err := sandbox.Annotations("nerdctl/network-namespace")
assert.NoError(err)
assert.Equal(path.Dir(netNsPath), "/var/run/netns")
}
func TestCheckForFips(t *testing.T) {
assert := assert.New(t)
val := procFIPS
procFIPS = filepath.Join(t.TempDir(), "fips-enabled")
defer func() {
procFIPS = val
}()
err := os.WriteFile(procFIPS, []byte("1"), 0644)
assert.NoError(err)
hconfig := vc.HypervisorConfig{
KernelParams: []vc.Param{
{Key: "init", Value: "/sys/init"},
},
}
config := vc.SandboxConfig{
HypervisorConfig: hconfig,
}
assert.NoError(checkForFIPS(&config))
params := config.HypervisorConfig.KernelParams
assert.Equal(len(params), 2)
assert.Equal(params[1].Key, "fips")
assert.Equal(params[1].Value, "1")
config.HypervisorConfig = hconfig
err = os.WriteFile(procFIPS, []byte("unexpected contents"), 0644)
assert.NoError(err)
assert.NoError(checkForFIPS(&config))
assert.Equal(config.HypervisorConfig, hconfig)
assert.NoError(os.Remove(procFIPS))
assert.NoError(checkForFIPS(&config))
assert.Equal(config.HypervisorConfig, hconfig)
}
func TestCreateContainerContainerConfigFail(t *testing.T) {
assert := assert.New(t)
_, bundlePath, ociConfigFile := ktu.SetupOCIConfigFile(t)
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
// Set invalid container type
containerType := "你好,世界"
spec.Annotations = make(map[string]string)
spec.Annotations[testContainerTypeAnnotation] = containerType
// rewrite file
err = ktu.WriteOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
rootFs := vc.RootFs{Mounted: true}
for _, disableOutput := range []bool{true, false} {
_, err = CreateContainer(context.Background(), mockSandbox, spec, rootFs, testContainerID, bundlePath, testConsole, disableOutput, false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
assert.True(strings.Contains(err.Error(), containerType))
}
}
func TestCreateContainerFail(t *testing.T) {
assert := assert.New(t)
_, bundlePath, ociConfigFile := ktu.SetupOCIConfigFile(t)
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
// set expected container type and sandboxID
spec.Annotations = make(map[string]string)
spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer
spec.Annotations[testSandboxIDAnnotation] = testSandboxID
// rewrite file
err = ktu.WriteOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
rootFs := vc.RootFs{Mounted: true}
for _, disableOutput := range []bool{true, false} {
_, err = CreateContainer(context.Background(), mockSandbox, spec, rootFs, testContainerID, bundlePath, testConsole, disableOutput, false)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
}
}
func TestCreateContainer(t *testing.T) {
assert := assert.New(t)
mockSandbox.CreateContainerFunc = func(containerConfig vc.ContainerConfig) (vc.VCContainer, error) {
return &vcmock.Container{}, nil
}
defer func() {
mockSandbox.CreateContainerFunc = nil
}()
_, bundlePath, ociConfigFile := ktu.SetupOCIConfigFile(t)
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
// set expected container type and sandboxID
spec.Annotations = make(map[string]string)
spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer
spec.Annotations[testSandboxIDAnnotation] = testSandboxID
// rewrite file
err = ktu.WriteOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
rootFs := vc.RootFs{Mounted: true}
for _, disableOutput := range []bool{true, false} {
_, err = CreateContainer(context.Background(), mockSandbox, spec, rootFs, testContainerID, bundlePath, testConsole, disableOutput, false)
assert.NoError(err)
}
}