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>
This commit is contained in:
bin 2021-09-29 07:32:50 +08:00
parent 41c49a7bf5
commit 273a1a9ac6
13 changed files with 269 additions and 940 deletions

View File

@ -30,9 +30,6 @@ import (
"github.com/urfave/cli"
)
// specConfig is the name of the file holding the containers configuration
const specConfig = "config.json"
// arch is the architecture for the running program
const arch = goruntime.GOARCH

View File

@ -13,7 +13,6 @@ import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
@ -22,9 +21,7 @@ import (
ktu "github.com/kata-containers/kata-containers/src/runtime/pkg/katatestutils"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
"github.com/kata-containers/kata-containers/src/runtime/pkg/utils"
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"
specs "github.com/opencontainers/runtime-spec/specs-go"
@ -38,19 +35,12 @@ const (
testFileMode = os.FileMode(0640)
testExeFileMode = os.FileMode(0750)
// small docker image used to create root filesystems from
testDockerImage = "busybox"
testBundle = "bundle"
testConsole = "/dev/pts/999"
)
var (
// package variables set by calling TestMain()
testDir = ""
testBundleDir = ""
tc ktu.TestConstraint
ctrEngine = katautils.CtrEngine{}
)
// testingImpl is a concrete mock RVC implementation used for testing
@ -79,57 +69,6 @@ func init() {
fmt.Printf("INFO: switching to fake virtcontainers implementation for testing\n")
vci = testingImpl
var err error
fmt.Printf("INFO: creating test directory\n")
testDir, err = ioutil.TempDir("", fmt.Sprintf("%s-", katautils.NAME))
if err != nil {
panic(fmt.Sprintf("ERROR: failed to create test directory: %v", err))
}
fmt.Printf("INFO: test directory is %v\n", testDir)
var output string
for _, name := range katautils.DockerLikeCtrEngines {
fmt.Printf("INFO: checking for container engine: %s\n", name)
output, err = ctrEngine.Init(name)
if err == nil {
break
}
}
if ctrEngine.Name == "" {
panic(fmt.Sprintf("ERROR: Docker-like container engine not accessible to current user: %v (error %v)",
output, err))
}
// Do this now to avoid hitting the test timeout value due to
// slow network response.
fmt.Printf("INFO: ensuring required container image (%v) is available\n", testDockerImage)
// Only hit the network if the image doesn't exist locally
_, err = ctrEngine.Inspect(testDockerImage)
if err == nil {
fmt.Printf("INFO: container image %v already exists locally\n", testDockerImage)
} else {
fmt.Printf("INFO: pulling container image %v\n", testDockerImage)
_, err = ctrEngine.Pull(testDockerImage)
if err != nil {
panic(err)
}
}
testBundleDir = filepath.Join(testDir, testBundle)
err = os.MkdirAll(testBundleDir, testDirMode)
if err != nil {
panic(fmt.Sprintf("ERROR: failed to create bundle directory %v: %v", testBundleDir, err))
}
fmt.Printf("INFO: creating OCI bundle in %v for tests to use\n", testBundleDir)
err = realMakeOCIBundle(testBundleDir)
if err != nil {
panic(fmt.Sprintf("ERROR: failed to create OCI bundle: %v", err))
}
tc = ktu.NewTestConstraint(false)
}
@ -143,8 +82,6 @@ func resetCLIGlobals() {
func runUnitTests(m *testing.M) {
ret := m.Run()
os.RemoveAll(testDir)
os.Exit(ret)
}
@ -232,146 +169,6 @@ func newTestRuntimeConfig(dir, consolePath string, create bool) (oci.RuntimeConf
}, nil
}
// createOCIConfig creates an OCI configuration (spec) file in
// the bundle directory specified (which must exist).
func createOCIConfig(bundleDir string) error {
if bundleDir == "" {
return errors.New("BUG: Need bundle directory")
}
if !katautils.FileExists(bundleDir) {
return fmt.Errorf("BUG: Bundle directory %s does not exist", bundleDir)
}
var configCmd string
// Search for a suitable version of runc to use to generate
// the OCI config file.
for _, cmd := range []string{"docker-runc", "runc"} {
fullPath, err := exec.LookPath(cmd)
if err == nil {
configCmd = fullPath
break
}
}
if configCmd == "" {
return errors.New("Cannot find command to generate OCI config file")
}
_, err := utils.RunCommand([]string{configCmd, "spec", "--bundle", bundleDir})
if err != nil {
return err
}
specFile := filepath.Join(bundleDir, specConfig)
if !katautils.FileExists(specFile) {
return fmt.Errorf("generated OCI config file does not exist: %v", specFile)
}
return nil
}
// createRootfs creates a minimal root filesystem below the specified
// directory.
func createRootfs(dir string) error {
err := os.MkdirAll(dir, testDirMode)
if err != nil {
return err
}
container, err := ctrEngine.Create(testDockerImage)
if err != nil {
return err
}
err = ctrEngine.GetRootfs(container, dir)
if err != nil {
return err
}
// Clean up
_, err = ctrEngine.Rm(container)
if err != nil {
return err
}
return nil
}
// realMakeOCIBundle will create an OCI bundle (including the "config.json"
// config file) in the directory specified (which must already exist).
//
// XXX: Note that tests should *NOT* call this function - they should
// XXX: instead call makeOCIBundle().
func realMakeOCIBundle(bundleDir string) error {
if bundleDir == "" {
return errors.New("BUG: Need bundle directory")
}
if !katautils.FileExists(bundleDir) {
return fmt.Errorf("BUG: Bundle directory %v does not exist", bundleDir)
}
err := createOCIConfig(bundleDir)
if err != nil {
return err
}
// Note the unusual parameter (a directory, not the config
// file to parse!)
spec, err := compatoci.ParseConfigJSON(bundleDir)
if err != nil {
return err
}
// Determine the rootfs directory name the OCI config refers to
ociRootPath := spec.Root.Path
rootfsDir := filepath.Join(bundleDir, ociRootPath)
if strings.HasPrefix(ociRootPath, "/") {
return fmt.Errorf("Cannot handle absolute rootfs as bundle must be unique to each test")
}
err = createRootfs(rootfsDir)
if err != nil {
return err
}
return nil
}
// Create an OCI bundle in the specified directory.
//
// Note that the directory will be created, but it's parent is expected to exist.
//
// This function works by copying the already-created test bundle. Ideally,
// the bundle would be recreated for each test, but createRootfs() uses
// docker which on some systems is too slow, resulting in the tests timing
// out.
func makeOCIBundle(bundleDir string) error {
from := testBundleDir
to := bundleDir
// only the basename of bundleDir needs to exist as bundleDir
// will get created by cp(1).
base := filepath.Dir(bundleDir)
for _, dir := range []string{from, base} {
if !katautils.FileExists(dir) {
return fmt.Errorf("BUG: directory %v should exist", dir)
}
}
output, err := utils.RunCommandFull([]string{"cp", "-a", from, to}, true)
if err != nil {
return fmt.Errorf("failed to copy test OCI bundle from %v to %v: %v (output: %v)", from, to, err, output)
}
return nil
}
func createCLIContextWithApp(flagSet *flag.FlagSet, app *cli.App) *cli.Context {
ctx := cli.NewContext(app, flagSet, nil)
@ -390,69 +187,6 @@ func createCLIContext(flagset *flag.FlagSet) *cli.Context {
return createCLIContextWithApp(flagset, cli.NewApp())
}
func TestMakeOCIBundle(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir(testDir, "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
bundleDir := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundleDir)
assert.NoError(err)
specFile := filepath.Join(bundleDir, specConfig)
assert.True(katautils.FileExists(specFile))
}
func TestCreateOCIConfig(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir(testDir, "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
bundleDir := filepath.Join(tmpdir, "bundle")
err = createOCIConfig(bundleDir)
// ENOENT
assert.Error(err)
err = os.MkdirAll(bundleDir, testDirMode)
assert.NoError(err)
err = createOCIConfig(bundleDir)
assert.NoError(err)
specFile := filepath.Join(bundleDir, specConfig)
assert.True(katautils.FileExists(specFile))
}
func TestCreateRootfs(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir(testDir, "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
rootfsDir := filepath.Join(tmpdir, "rootfs")
assert.False(katautils.FileExists(rootfsDir))
err = createRootfs(rootfsDir)
assert.NoError(err)
// non-comprehensive list of expected directories
expectedDirs := []string{"bin", "dev", "etc", "usr", "var"}
assert.True(katautils.FileExists(rootfsDir))
for _, dir := range expectedDirs {
dirPath := filepath.Join(rootfsDir, dir)
assert.True(katautils.FileExists(dirPath))
}
}
func TestMainUserWantsUsage(t *testing.T) {
assert := assert.New(t)
@ -525,7 +259,7 @@ func TestMainBeforeSubCommands(t *testing.T) {
func TestMainBeforeSubCommandsInvalidLogFile(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir(testDir, "")
tmpdir, err := ioutil.TempDir("", "katatest")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
@ -548,7 +282,7 @@ func TestMainBeforeSubCommandsInvalidLogFile(t *testing.T) {
func TestMainBeforeSubCommandsInvalidLogFormat(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir(testDir, "")
tmpdir, err := ioutil.TempDir("", "katatest")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
@ -577,7 +311,7 @@ func TestMainBeforeSubCommandsInvalidLogFormat(t *testing.T) {
func TestMainBeforeSubCommandsLoadConfigurationFail(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir(testDir, "")
tmpdir, err := ioutil.TempDir("", "katatest")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
@ -612,7 +346,7 @@ func TestMainBeforeSubCommandsLoadConfigurationFail(t *testing.T) {
func TestMainBeforeSubCommandsShowCCConfigPaths(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir(testDir, "")
tmpdir, err := ioutil.TempDir("", "katatest")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
@ -676,7 +410,7 @@ func TestMainBeforeSubCommandsShowCCConfigPaths(t *testing.T) {
func TestMainFatal(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir(testDir, "")
tmpdir, err := ioutil.TempDir("", "katatest")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
@ -900,7 +634,7 @@ func TestMainCreateRuntime(t *testing.T) {
func TestMainVersionPrinter(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
tmpdir, err := ioutil.TempDir("", "katatest")
assert.NoError(err)
defer os.RemoveAll(tmpdir)

View File

@ -18,7 +18,7 @@ import (
)
func TestFileExists(t *testing.T) {
dir, err := ioutil.TempDir(testDir, "")
dir, err := ioutil.TempDir("", "katatest")
if err != nil {
t.Fatal(err)
}

View File

@ -12,7 +12,6 @@ import (
"io/ioutil"
"os"
"path"
"path/filepath"
"testing"
"github.com/containerd/containerd/namespaces"
@ -23,7 +22,6 @@ import (
"github.com/stretchr/testify/assert"
ktu "github.com/kata-containers/kata-containers/src/runtime/pkg/katatestutils"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
vc "github.com/kata-containers/kata-containers/src/runtime/virtcontainers"
vcAnnotations "github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/annotations"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/compatoci"
@ -52,21 +50,12 @@ func TestCreateSandboxSuccess(t *testing.T) {
testingImpl.CreateSandboxFunc = nil
}()
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
defer os.RemoveAll(tmpdir)
tmpdir, bundlePath, ociConfigFile := ktu.SetupOCIConfigFile(t)
// defer os.RemoveAll(tmpdir)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(katautils.FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
@ -82,7 +71,7 @@ func TestCreateSandboxSuccess(t *testing.T) {
}
// Rewrite the file
err = writeOCIConfigFile(spec, ociConfigFile)
err = ktu.WriteOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
s := &service{
@ -110,25 +99,16 @@ func TestCreateSandboxFail(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
tmpdir, bundlePath, ociConfigFile := ktu.SetupOCIConfigFile(t)
defer os.RemoveAll(tmpdir)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(katautils.FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
err = writeOCIConfigFile(spec, ociConfigFile)
err = ktu.WriteOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
s := &service{
@ -157,21 +137,12 @@ func TestCreateSandboxConfigFail(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
tmpdir, bundlePath, _ := ktu.SetupOCIConfigFile(t)
defer os.RemoveAll(tmpdir)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(katautils.FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
@ -216,21 +187,12 @@ func TestCreateContainerSuccess(t *testing.T) {
},
}
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
tmpdir, bundlePath, ociConfigFile := ktu.SetupOCIConfigFile(t)
defer os.RemoveAll(tmpdir)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(katautils.FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
@ -240,7 +202,7 @@ func TestCreateContainerSuccess(t *testing.T) {
spec.Annotations[testSandboxIDAnnotation] = testSandboxID
// rewrite file
err = writeOCIConfigFile(spec, ociConfigFile)
err = ktu.WriteOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
s := &service{
@ -265,21 +227,12 @@ func TestCreateContainerSuccess(t *testing.T) {
func TestCreateContainerFail(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
tmpdir, bundlePath, ociConfigFile := ktu.SetupOCIConfigFile(t)
defer os.RemoveAll(tmpdir)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(katautils.FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
@ -287,7 +240,7 @@ func TestCreateContainerFail(t *testing.T) {
spec.Annotations[testContainerTypeAnnotation] = testContainerTypeContainer
spec.Annotations[testSandboxIDAnnotation] = testSandboxID
err = writeOCIConfigFile(spec, ociConfigFile)
err = ktu.WriteOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
// doesn't create sandbox first
@ -325,21 +278,12 @@ func TestCreateContainerConfigFail(t *testing.T) {
sandbox.CreateContainerFunc = nil
}()
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
tmpdir, bundlePath, ociConfigFile := ktu.SetupOCIConfigFile(t)
defer os.RemoveAll(tmpdir)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(katautils.FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
@ -348,7 +292,7 @@ func TestCreateContainerConfigFail(t *testing.T) {
spec.Annotations[testContainerTypeAnnotation] = "errorType"
spec.Annotations[testSandboxIDAnnotation] = testSandboxID
err = writeOCIConfigFile(spec, ociConfigFile)
err = ktu.WriteOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
s := &service{

View File

@ -7,14 +7,13 @@
package containerdshim
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
taskAPI "github.com/containerd/containerd/runtime/v2/task"
"github.com/stretchr/testify/assert"
ktu "github.com/kata-containers/kata-containers/src/runtime/pkg/katatestutils"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/compatoci"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/vcmock"
)
@ -26,7 +25,7 @@ func TestDeleteContainerSuccessAndFail(t *testing.T) {
MockID: testSandboxID,
}
rootPath, bundlePath := testConfigSetup(t)
rootPath, bundlePath, _ := ktu.SetupOCIConfigFile(t)
defer os.RemoveAll(rootPath)
_, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
@ -43,19 +42,3 @@ func TestDeleteContainerSuccessAndFail(t *testing.T) {
s.containers[testContainerID], err = newContainer(s, reqCreate, "", nil, true)
assert.NoError(err)
}
func testConfigSetup(t *testing.T) (rootPath string, bundlePath string) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
bundlePath = filepath.Join(tmpdir, "bundle")
err = os.MkdirAll(bundlePath, testDirMode)
assert.NoError(err)
err = createOCIConfig(bundlePath)
assert.NoError(err)
return tmpdir, bundlePath
}

View File

@ -8,9 +8,7 @@ package containerdshim
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
@ -43,13 +41,9 @@ func TestServiceCreate(t *testing.T) {
assert := assert.New(t)
tmpdir, _ := ioutil.TempDir("", "")
tmpdir, bundleDir, _ := ktu.SetupOCIConfigFile(t)
defer os.RemoveAll(tmpdir)
bundleDir := filepath.Join(tmpdir, "bundle")
err := makeOCIBundle(bundleDir)
assert.NoError(err)
ctx := context.Background()
s, err := newService("foo")

View File

@ -9,6 +9,7 @@ import (
"context"
"io"
"io/ioutil"
"os"
"path/filepath"
"syscall"
"testing"
@ -24,6 +25,11 @@ func TestNewTtyIOFifoReopen(t *testing.T) {
var tty *ttyIO
assert := assert.New(t)
ctx := context.TODO()
testDir, err := ioutil.TempDir("", "kata-")
assert.NoError(err)
defer os.RemoveAll(testDir)
fifoPath, err := ioutil.TempDir(testDir, "fifo-path-")
assert.NoError(err)
stdout := filepath.Join(fifoPath, "stdout")
@ -100,6 +106,10 @@ func TestIoCopy(t *testing.T) {
testBytes2 := []byte("Test2")
testBytes3 := []byte("Test3")
testDir, err := ioutil.TempDir("", "kata-")
assert.NoError(err)
defer os.RemoveAll(testDir)
fifoPath, err := ioutil.TempDir(testDir, "fifo-path-")
assert.NoError(err)
dstStdoutPath := filepath.Join(fifoPath, "dststdout")

View File

@ -7,46 +7,28 @@
package containerdshim
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
sysExec "os/exec"
"path"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/opencontainers/runtime-spec/specs-go"
ktu "github.com/kata-containers/kata-containers/src/runtime/pkg/katatestutils"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils"
"github.com/kata-containers/kata-containers/src/runtime/pkg/utils"
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/pkg/errors"
)
const (
// specConf is the name of the file holding the containers configuration
specConf = "config.json"
TestID = "container_test"
testDirMode = os.FileMode(0750)
testFileMode = os.FileMode(0640)
// testExeFileMode = os.FileMode(0750)
// small docker image used to create root filesystems from
testDockerImage = "busybox"
testSandboxID = "777-77-77777777"
testContainerID = "42"
testBundle = "bundle"
testConsole = "/dev/pts/888"
testContainerTypeAnnotation = "io.kubernetes.cri.container-type"
@ -57,10 +39,7 @@ const (
var (
// package variables set by calling TestMain()
testDir = ""
testBundleDir = ""
tc ktu.TestConstraint
ctrEngine = katautils.CtrEngine{}
)
// testingImpl is a concrete mock RVC implementation used for testing
@ -73,57 +52,6 @@ func init() {
fmt.Printf("INFO: switching to fake virtcontainers implementation for testing\n")
vci = testingImpl
var err error
fmt.Printf("INFO: creating test directory\n")
testDir, err = ioutil.TempDir("", "shimV2-")
if err != nil {
panic(fmt.Sprintf("ERROR: failed to create test directory: %v", err))
}
fmt.Printf("INFO: test directory is %v\n", testDir)
var output string
for _, name := range katautils.DockerLikeCtrEngines {
fmt.Printf("INFO: checking for container engine: %s\n", name)
output, err = ctrEngine.Init(name)
if err == nil {
break
}
}
if ctrEngine.Name == "" {
panic(fmt.Sprintf("ERROR: Docker-like container engine not accessible to current user: %v (error %v)",
output, err))
}
// Do this now to avoid hitting the test timeout value due to
// slow network response.
fmt.Printf("INFO: ensuring required container image (%v) is available\n", testDockerImage)
// Only hit the network if the image doesn't exist locally
_, err = ctrEngine.Inspect(testDockerImage)
if err == nil {
fmt.Printf("INFO: container image %v already exists locally\n", testDockerImage)
} else {
fmt.Printf("INFO: pulling container image %v\n", testDockerImage)
_, err = ctrEngine.Pull(testDockerImage)
if err != nil {
panic(err)
}
}
testBundleDir = filepath.Join(testDir, testBundle)
err = os.MkdirAll(testBundleDir, testDirMode)
if err != nil {
panic(fmt.Sprintf("ERROR: failed to create bundle directory %v: %v", testBundleDir, err))
}
fmt.Printf("INFO: creating OCI bundle in %v for tests to use\n", testBundleDir)
err = realMakeOCIBundle(testBundleDir)
if err != nil {
panic(fmt.Sprintf("ERROR: failed to create OCI bundle: %v", err))
}
tc = ktu.NewTestConstraint(false)
// disable shim management server.
@ -131,46 +59,6 @@ func init() {
defaultStartManagementServerFunc = nil
}
// createOCIConfig creates an OCI configuration (spec) file in
// the bundle directory specified (which must exist).
func createOCIConfig(bundleDir string) error {
if bundleDir == "" {
return errors.New("BUG: Need bundle directory")
}
if !katautils.FileExists(bundleDir) {
return fmt.Errorf("BUG: Bundle directory %s does not exist", bundleDir)
}
var configCmd string
// Search for a suitable version of runc to use to generate
// the OCI config file.
for _, cmd := range []string{"docker-runc", "runc"} {
fullPath, err := sysExec.LookPath(cmd)
if err == nil {
configCmd = fullPath
break
}
}
if configCmd == "" {
return errors.New("Cannot find command to generate OCI config file")
}
_, err := utils.RunCommand([]string{configCmd, "spec", "--bundle", bundleDir})
if err != nil {
return err
}
specFile := filepath.Join(bundleDir, specConf)
if !katautils.FileExists(specFile) {
return fmt.Errorf("generated OCI config file does not exist: %v", specFile)
}
return nil
}
func createEmptyFile(path string) (err error) {
return ioutil.WriteFile(path, []byte(""), testFileMode)
}
@ -221,119 +109,6 @@ func newTestRuntimeConfig(dir, consolePath string, create bool) (oci.RuntimeConf
}, nil
}
// realMakeOCIBundle will create an OCI bundle (including the "config.json"
// config file) in the directory specified (which must already exist).
//
// XXX: Note that tests should *NOT* call this function - they should
// XXX: instead call makeOCIBundle().
func realMakeOCIBundle(bundleDir string) error {
if bundleDir == "" {
return errors.New("BUG: Need bundle directory")
}
if !katautils.FileExists(bundleDir) {
return fmt.Errorf("BUG: Bundle directory %v does not exist", bundleDir)
}
err := createOCIConfig(bundleDir)
if err != nil {
return err
}
// Note the unusual parameter (a directory, not the config
// file to parse!)
spec, err := compatoci.ParseConfigJSON(bundleDir)
if err != nil {
return err
}
// Determine the rootfs directory name the OCI config refers to
ociRootPath := spec.Root.Path
rootfsDir := filepath.Join(bundleDir, ociRootPath)
if strings.HasPrefix(ociRootPath, "/") {
return fmt.Errorf("Cannot handle absolute rootfs as bundle must be unique to each test")
}
err = createRootfs(rootfsDir)
if err != nil {
return err
}
return nil
}
// Create an OCI bundle in the specified directory.
//
// Note that the directory will be created, but it's parent is expected to exist.
//
// This function works by copying the already-created test bundle. Ideally,
// the bundle would be recreated for each test, but createRootfs() uses
// docker which on some systems is too slow, resulting in the tests timing
// out.
func makeOCIBundle(bundleDir string) error {
from := testBundleDir
to := bundleDir
// only the basename of bundleDir needs to exist as bundleDir
// will get created by cp(1).
base := filepath.Dir(bundleDir)
for _, dir := range []string{from, base} {
if !katautils.FileExists(dir) {
return fmt.Errorf("BUG: directory %v should exist", dir)
}
}
output, err := utils.RunCommandFull([]string{"cp", "-a", from, to}, true)
if err != nil {
return fmt.Errorf("failed to copy test OCI bundle from %v to %v: %v (output: %v)", from, to, err, output)
}
return nil
}
// createRootfs creates a minimal root filesystem below the specified
// directory.
func createRootfs(dir string) error {
err := os.MkdirAll(dir, testDirMode)
if err != nil {
return err
}
container, err := ctrEngine.Create(testDockerImage)
if err != nil {
return err
}
err = ctrEngine.GetRootfs(container, dir)
if err != nil {
return err
}
// Clean up
_, err = ctrEngine.Rm(container)
if err != nil {
return err
}
return nil
}
func writeOCIConfigFile(spec specs.Spec, configPath string) error {
if configPath == "" {
return errors.New("BUG: need config file path")
}
bytes, err := json.MarshalIndent(spec, "", "\t")
if err != nil {
return err
}
return ioutil.WriteFile(configPath, bytes, testFileMode)
}
func TestNoNeedForOutput(t *testing.T) {
assert := assert.New(t)

View File

@ -21,7 +21,6 @@ import (
)
const (
testFileMode = os.FileMode(0640)
invalidOperator = 1234
skipUnknownDistroName = "skipping test as cannot determine distro name"

View File

@ -7,8 +7,203 @@
package katatestutils
import (
"encoding/json"
"errors"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"testing"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
)
const (
testDirMode = os.FileMode(0750)
testFileMode = os.FileMode(0640)
busyboxConfigJson = `
{
"ociVersion": "1.0.1-dev",
"process": {
"terminal": true,
"user": {
"uid": 0,
"gid": 0
},
"args": [
"sh"
],
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TERM=xterm"
],
"cwd": "/",
"capabilities": {
"bounding": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"effective": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"inheritable": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"permitted": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
],
"ambient": [
"CAP_AUDIT_WRITE",
"CAP_KILL",
"CAP_NET_BIND_SERVICE"
]
},
"rlimits": [
{
"type": "RLIMIT_NOFILE",
"hard": 1024,
"soft": 1024
}
],
"noNewPrivileges": true
},
"root": {
"path": "rootfs",
"readonly": true
},
"hostname": "runc",
"mounts": [
{
"destination": "/proc",
"type": "proc",
"source": "proc"
},
{
"destination": "/dev",
"type": "tmpfs",
"source": "tmpfs",
"options": [
"nosuid",
"strictatime",
"mode=755",
"size=65536k"
]
},
{
"destination": "/dev/pts",
"type": "devpts",
"source": "devpts",
"options": [
"nosuid",
"noexec",
"newinstance",
"ptmxmode=0666",
"mode=0620",
"gid=5"
]
},
{
"destination": "/dev/shm",
"type": "tmpfs",
"source": "shm",
"options": [
"nosuid",
"noexec",
"nodev",
"mode=1777",
"size=65536k"
]
},
{
"destination": "/dev/mqueue",
"type": "mqueue",
"source": "mqueue",
"options": [
"nosuid",
"noexec",
"nodev"
]
},
{
"destination": "/sys",
"type": "sysfs",
"source": "sysfs",
"options": [
"nosuid",
"noexec",
"nodev",
"ro"
]
},
{
"destination": "/sys/fs/cgroup",
"type": "cgroup",
"source": "cgroup",
"options": [
"nosuid",
"noexec",
"nodev",
"relatime",
"ro"
]
}
],
"linux": {
"resources": {
"devices": [
{
"allow": false,
"access": "rwm"
}
]
},
"namespaces": [
{
"type": "pid"
},
{
"type": "network"
},
{
"type": "ipc"
},
{
"type": "uts"
},
{
"type": "mount"
}
],
"maskedPaths": [
"/proc/acpi",
"/proc/asound",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/sys/firmware",
"/proc/scsi"
],
"readonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
}
}`
)
type RuntimeConfigOptions struct {
@ -154,3 +349,34 @@ func IsInGitHubActions() bool {
// https://docs.github.com/en/actions/reference/environment-variables#default-environment-variables
return os.Getenv("GITHUB_ACTIONS") == "true"
}
func SetupOCIConfigFile(t *testing.T) (rootPath string, bundlePath, ociConfigFile string) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "katatest-")
assert.NoError(err)
bundlePath = filepath.Join(tmpdir, "bundle")
err = os.MkdirAll(bundlePath, testDirMode)
assert.NoError(err)
ociConfigFile = filepath.Join(bundlePath, "config.json")
err = ioutil.WriteFile(ociConfigFile, []byte(busyboxConfigJson), testFileMode)
assert.NoError(err)
return tmpdir, bundlePath, ociConfigFile
}
// WriteOCIConfigFile using spec to update OCI config file by path configPath
func WriteOCIConfigFile(spec specs.Spec, configPath string) error {
if configPath == "" {
return errors.New("BUG: need config file path")
}
bytes, err := json.MarshalIndent(spec, "", "\t")
if err != nil {
return err
}
return ioutil.WriteFile(configPath, bytes, testFileMode)
}

View File

@ -1,77 +0,0 @@
// Copyright (c) 2019 SUSE LLC
//
// SPDX-License-Identifier: Apache-2.0
//
package katautils
import (
"os/exec"
"github.com/kata-containers/kata-containers/src/runtime/pkg/utils"
)
type CtrEngine struct {
Name string
}
var (
DockerLikeCtrEngines = []string{"docker", "podman"}
)
func (e *CtrEngine) Init(name string) (string, error) {
var out string
out, err := utils.RunCommandFull([]string{name, "version"}, true)
if err != nil {
return out, err
}
e.Name = name
return out, nil
}
func (e *CtrEngine) Inspect(image string) (string, error) {
// Only hit the network if the image doesn't exist locally
return utils.RunCommand([]string{e.Name, "inspect", "--type=image", image})
}
func (e *CtrEngine) Pull(image string) (string, error) {
return utils.RunCommand([]string{e.Name, "pull", image})
}
func (e *CtrEngine) Create(image string) (string, error) {
return utils.RunCommand([]string{e.Name, "create", image})
}
func (e *CtrEngine) Rm(ctrID string) (string, error) {
return utils.RunCommand([]string{e.Name, "rm", ctrID})
}
func (e *CtrEngine) GetRootfs(ctrID string, dir string) error {
cmd1 := exec.Command(e.Name, "export", ctrID)
cmd2 := exec.Command("tar", "-C", dir, "-xvf", "-")
cmd1Stdout, err := cmd1.StdoutPipe()
if err != nil {
return err
}
cmd2.Stdin = cmd1Stdout
err = cmd2.Start()
if err != nil {
return err
}
err = cmd1.Run()
if err != nil {
return err
}
err = cmd2.Wait()
if err != nil {
return err
}
return nil
}

View File

@ -8,7 +8,6 @@ package katautils
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
@ -20,7 +19,6 @@ import (
"testing"
ktu "github.com/kata-containers/kata-containers/src/runtime/pkg/katatestutils"
"github.com/kata-containers/kata-containers/src/runtime/pkg/utils"
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"
@ -37,8 +35,6 @@ const (
)
var (
testBundleDir = ""
// testingImpl is a concrete mock RVC implementation used for testing
testingImpl = &vcmock.VCMock{}
// mock sandbox
@ -53,49 +49,6 @@ func init() {
tc = ktu.NewTestConstraint(false)
}
func writeOCIConfigFile(spec specs.Spec, configPath string) error {
if configPath == "" {
return errors.New("BUG: need config file path")
}
bytes, err := json.MarshalIndent(spec, "", "\t")
if err != nil {
return err
}
return ioutil.WriteFile(configPath, bytes, testFileMode)
}
// Create an OCI bundle in the specified directory.
//
// Note that the directory will be created, but it's parent is expected to exist.
//
// This function works by copying the already-created test bundle. Ideally,
// the bundle would be recreated for each test, but createRootfs() uses
// docker which on some systems is too slow, resulting in the tests timing
// out.
func makeOCIBundle(bundleDir string) error {
from := testBundleDir
to := bundleDir
// only the basename of bundleDir needs to exist as bundleDir
// will get created by cp(1).
base := filepath.Dir(bundleDir)
for _, dir := range []string{from, base} {
if !FileExists(dir) {
return fmt.Errorf("BUG: directory %v should exist", dir)
}
}
output, err := utils.RunCommandFull([]string{"cp", "-a", from, to}, true)
if err != nil {
return fmt.Errorf("failed to copy test OCI bundle from %v to %v: %v (output: %v)", from, to, err, output)
}
return nil
}
// newTestRuntimeConfig creates a new RuntimeConfig
func newTestRuntimeConfig(dir, consolePath string, create bool) (oci.RuntimeConfig, error) {
if dir == "" {
@ -262,21 +215,12 @@ func TestSetKernelParamsUserOptionTakesPriority(t *testing.T) {
func TestCreateSandboxConfigFail(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
tmpdir, bundlePath, _ := ktu.SetupOCIConfigFile(t)
defer os.RemoveAll(tmpdir)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
@ -305,21 +249,12 @@ func TestCreateSandboxFail(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
tmpdir, bundlePath, _ := ktu.SetupOCIConfigFile(t)
defer os.RemoveAll(tmpdir)
runtimeConfig, err := newTestRuntimeConfig(tmpdir, testConsole, true)
assert.NoError(err)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
@ -375,18 +310,9 @@ func TestCheckForFips(t *testing.T) {
func TestCreateContainerContainerConfigFail(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
tmpdir, bundlePath, ociConfigFile := ktu.SetupOCIConfigFile(t)
defer os.RemoveAll(tmpdir)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
@ -396,7 +322,7 @@ func TestCreateContainerContainerConfigFail(t *testing.T) {
spec.Annotations[testContainerTypeAnnotation] = containerType
// rewrite file
err = writeOCIConfigFile(spec, ociConfigFile)
err = ktu.WriteOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
rootFs := vc.RootFs{Mounted: true}
@ -412,18 +338,9 @@ func TestCreateContainerContainerConfigFail(t *testing.T) {
func TestCreateContainerFail(t *testing.T) {
assert := assert.New(t)
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
tmpdir, bundlePath, ociConfigFile := ktu.SetupOCIConfigFile(t)
defer os.RemoveAll(tmpdir)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
@ -433,7 +350,7 @@ func TestCreateContainerFail(t *testing.T) {
spec.Annotations[testSandboxIDAnnotation] = testSandboxID
// rewrite file
err = writeOCIConfigFile(spec, ociConfigFile)
err = ktu.WriteOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
rootFs := vc.RootFs{Mounted: true}
@ -456,18 +373,9 @@ func TestCreateContainer(t *testing.T) {
mockSandbox.CreateContainerFunc = nil
}()
tmpdir, err := ioutil.TempDir("", "")
assert.NoError(err)
tmpdir, bundlePath, ociConfigFile := ktu.SetupOCIConfigFile(t)
defer os.RemoveAll(tmpdir)
bundlePath := filepath.Join(tmpdir, "bundle")
err = makeOCIBundle(bundlePath)
assert.NoError(err)
ociConfigFile := filepath.Join(bundlePath, "config.json")
assert.True(FileExists(ociConfigFile))
spec, err := compatoci.ParseConfigJSON(bundlePath)
assert.NoError(err)
@ -477,7 +385,7 @@ func TestCreateContainer(t *testing.T) {
spec.Annotations[testSandboxIDAnnotation] = testSandboxID
// rewrite file
err = writeOCIConfigFile(spec, ociConfigFile)
err = ktu.WriteOCIConfigFile(spec, ociConfigFile)
assert.NoError(err)
rootFs := vc.RootFs{Mounted: true}

View File

@ -7,20 +7,15 @@
package katautils
import (
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"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/utils"
"github.com/kata-containers/kata-containers/src/runtime/virtcontainers/pkg/compatoci"
"github.com/stretchr/testify/assert"
)
@ -28,173 +23,14 @@ const (
testDirMode = os.FileMode(0750)
testFileMode = os.FileMode(0640)
// small docker image used to create root filesystems from
testDockerImage = "busybox"
testSandboxID = "99999999-9999-9999-99999999999999999"
testContainerID = "1"
testBundle = "bundle"
specConfig = "config.json"
)
var (
testDir = ""
)
func init() {
var err error
fmt.Printf("INFO: creating test directory\n")
testDir, err = ioutil.TempDir("", fmt.Sprintf("%s-", NAME))
if err != nil {
panic(fmt.Sprintf("ERROR: failed to create test directory: %v", err))
}
fmt.Printf("INFO: test directory is %v\n", testDir)
testBundleDir = filepath.Join(testDir, testBundle)
err = os.MkdirAll(testBundleDir, testDirMode)
if err != nil {
panic(fmt.Sprintf("ERROR: failed to create bundle directory %v: %v", testBundleDir, err))
}
fmt.Printf("INFO: creating OCI bundle in %v for tests to use\n", testBundleDir)
err = realMakeOCIBundle(testBundleDir)
if err != nil {
panic(fmt.Sprintf("ERROR: failed to create OCI bundle: %v", err))
}
}
// createOCIConfig creates an OCI configuration (spec) file in
// the bundle directory specified (which must exist).
func createOCIConfig(bundleDir string) error {
if bundleDir == "" {
return errors.New("BUG: Need bundle directory")
}
if !FileExists(bundleDir) {
return fmt.Errorf("BUG: Bundle directory %s does not exist", bundleDir)
}
var configCmd string
// Search for a suitable version of runc to use to generate
// the OCI config file.
for _, cmd := range []string{"docker-runc", "runc"} {
fullPath, err := exec.LookPath(cmd)
if err == nil {
configCmd = fullPath
break
}
}
if configCmd == "" {
return errors.New("Cannot find command to generate OCI config file")
}
_, err := utils.RunCommand([]string{configCmd, "spec", "--bundle", bundleDir})
if err != nil {
return err
}
specFile := filepath.Join(bundleDir, specConfig)
if !FileExists(specFile) {
return fmt.Errorf("generated OCI config file does not exist: %v", specFile)
}
return nil
}
// realMakeOCIBundle will create an OCI bundle (including the "config.json"
// config file) in the directory specified (which must already exist).
//
// XXX: Note that tests should *NOT* call this function - they should
// XXX: instead call makeOCIBundle().
func realMakeOCIBundle(bundleDir string) error {
if bundleDir == "" {
return errors.New("BUG: Need bundle directory")
}
if !FileExists(bundleDir) {
return fmt.Errorf("BUG: Bundle directory %v does not exist", bundleDir)
}
err := createOCIConfig(bundleDir)
if err != nil {
return err
}
// Note the unusual parameter (a directory, not the config
// file to parse!)
spec, err := compatoci.ParseConfigJSON(bundleDir)
if err != nil {
return err
}
// Determine the rootfs directory name the OCI config refers to
ociRootPath := spec.Root.Path
rootfsDir := filepath.Join(bundleDir, ociRootPath)
if strings.HasPrefix(ociRootPath, "/") {
return fmt.Errorf("Cannot handle absolute rootfs as bundle must be unique to each test")
}
err = createRootfs(rootfsDir)
if err != nil {
return err
}
return nil
}
// createRootfs creates a minimal root filesystem below the specified
// directory.
func createRootfs(dir string) error {
var (
output string
err error
)
ctrEngine := CtrEngine{}
for _, name := range DockerLikeCtrEngines {
fmt.Printf("INFO: checking for container engine: %s\n", name)
output, err = ctrEngine.Init(name)
if err == nil {
break
}
}
if ctrEngine.Name == "" {
panic(fmt.Sprintf("ERROR: Docker-like container engine not accessible to current user: %v (error %v)",
output, err))
}
err = os.MkdirAll(dir, testDirMode)
if err != nil {
return err
}
container, err := ctrEngine.Create(testDockerImage)
if err != nil {
return err
}
err = ctrEngine.GetRootfs(container, dir)
if err != nil {
return err
}
// Clean up
_, err = ctrEngine.Rm(container)
if err != nil {
return err
}
return nil
}
func createFile(file, contents string) error {
return ioutil.WriteFile(file, []byte(contents), testFileMode)
}