Files
kata-containers/src/runtime/pkg/katautils/create_test.go
bin 273a1a9ac6 runtime: optimize test code
This PR includes these optimize changes:

- Remove the dependency on the container engine.
  The old code uses runc to generate config.json and
  Docker to export rootfs, that will be heavy and need
  additional dependency.
  Using a fixed config for busybox image can avoid
  the heavy processing above.

- Moved duplicate code to pkg/katatestutils package

Fixes: #2752

Signed-off-by: bin <bin@hyper.sh>
2021-10-19 09:54:49 +08:00

398 lines
10 KiB
Go

// Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
package katautils
import (
"context"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"syscall"
"testing"
ktu "github.com/kata-containers/kata-containers/src/runtime/pkg/katatestutils"
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/oci"
"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, err := ioutil.TempDir(testDir, "foo")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
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)
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)
defer os.RemoveAll(tmpdir)
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)
defer os.RemoveAll(tmpdir)
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 TestCheckForFips(t *testing.T) {
assert := assert.New(t)
path, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(path)
val := procFIPS
procFIPS = filepath.Join(path, "fips-enabled")
defer func() {
procFIPS = val
}()
err = ioutil.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 = ioutil.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)
tmpdir, bundlePath, ociConfigFile := ktu.SetupOCIConfigFile(t)
defer os.RemoveAll(tmpdir)
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)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
assert.True(strings.Contains(err.Error(), containerType))
}
}
func TestCreateContainerFail(t *testing.T) {
assert := assert.New(t)
tmpdir, bundlePath, ociConfigFile := ktu.SetupOCIConfigFile(t)
defer os.RemoveAll(tmpdir)
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)
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
}()
tmpdir, bundlePath, ociConfigFile := ktu.SetupOCIConfigFile(t)
defer os.RemoveAll(tmpdir)
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)
assert.NoError(err)
}
}