mirror of
https://github.com/aljazceru/kata-containers.git
synced 2026-01-11 02:14:20 +01:00
Firecracker's revertBytes function, now called "RevertBytes", can be exposed as part of the virtcontainers' utils file, as this function will be reused by Cloud Hypervisor, when adding the rate limiter logic there. Signed-off-by: Fabiano Fidêncio <fabiano.fidencio@intel.com>
583 lines
12 KiB
Go
583 lines
12 KiB
Go
// Copyright (c) 2017 Intel Corporation
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
package utils
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"syscall"
|
|
"testing"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
const waitLocalProcessTimeoutSecs = 3
|
|
|
|
func TestFileCopySuccessful(t *testing.T) {
|
|
assert := assert.New(t)
|
|
fileContent := "testContent"
|
|
|
|
srcFile, err := os.CreateTemp("", "test_src_copy")
|
|
assert.NoError(err)
|
|
defer os.Remove(srcFile.Name())
|
|
defer srcFile.Close()
|
|
|
|
dstFile, err := os.CreateTemp("", "test_dst_copy")
|
|
assert.NoError(err)
|
|
defer os.Remove(dstFile.Name())
|
|
|
|
dstPath := dstFile.Name()
|
|
|
|
assert.NoError(dstFile.Close())
|
|
|
|
_, err = srcFile.WriteString(fileContent)
|
|
assert.NoError(err)
|
|
|
|
err = FileCopy(srcFile.Name(), dstPath)
|
|
assert.NoError(err)
|
|
|
|
dstContent, err := os.ReadFile(dstPath)
|
|
assert.NoError(err)
|
|
assert.Equal(string(dstContent), fileContent)
|
|
|
|
srcInfo, err := srcFile.Stat()
|
|
assert.NoError(err)
|
|
|
|
dstInfo, err := os.Stat(dstPath)
|
|
assert.NoError(err)
|
|
|
|
assert.Equal(dstInfo.Mode(), srcInfo.Mode())
|
|
assert.Equal(dstInfo.IsDir(), srcInfo.IsDir())
|
|
assert.Equal(dstInfo.Size(), srcInfo.Size())
|
|
}
|
|
|
|
func TestFileCopySourceEmptyFailure(t *testing.T) {
|
|
assert := assert.New(t)
|
|
err := FileCopy("", "testDst")
|
|
assert.Error(err)
|
|
}
|
|
|
|
func TestFileCopyDestinationEmptyFailure(t *testing.T) {
|
|
assert := assert.New(t)
|
|
err := FileCopy("testSrc", "")
|
|
assert.Error(err)
|
|
}
|
|
|
|
func TestFileCopySourceNotExistFailure(t *testing.T) {
|
|
assert := assert.New(t)
|
|
srcFile, err := os.CreateTemp("", "test_src_copy")
|
|
assert.NoError(err)
|
|
|
|
srcPath := srcFile.Name()
|
|
assert.NoError(srcFile.Close())
|
|
assert.NoError(os.Remove(srcPath))
|
|
|
|
err = FileCopy(srcPath, "testDest")
|
|
assert.Error(err)
|
|
}
|
|
|
|
func TestGenerateRandomBytes(t *testing.T) {
|
|
assert := assert.New(t)
|
|
bytesNeeded := 8
|
|
randBytes, err := GenerateRandomBytes(bytesNeeded)
|
|
assert.NoError(err)
|
|
assert.Equal(len(randBytes), bytesNeeded)
|
|
}
|
|
|
|
func TestRevereString(t *testing.T) {
|
|
assert := assert.New(t)
|
|
str := "Teststr"
|
|
reversed := reverseString(str)
|
|
assert.Equal(reversed, "rtstseT")
|
|
}
|
|
|
|
func TestCleanupFds(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
tmpFile, err := os.CreateTemp("", "testFds1")
|
|
assert.NoError(err)
|
|
filename := tmpFile.Name()
|
|
defer os.Remove(filename)
|
|
|
|
numFds := 1
|
|
fds := make([]*os.File, numFds)
|
|
assert.NotNil(fds)
|
|
assert.Equal(len(fds), 1)
|
|
|
|
fds[0] = tmpFile
|
|
|
|
CleanupFds(fds, 0)
|
|
CleanupFds(fds, 1)
|
|
|
|
err = tmpFile.Close()
|
|
assert.Error(err)
|
|
}
|
|
|
|
func TestWriteToFile(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
err := WriteToFile("/file-does-not-exist", []byte("test-data"))
|
|
assert.NotNil(err)
|
|
|
|
tmpFile, err := os.CreateTemp("", "test_append_file")
|
|
assert.NoError(err)
|
|
|
|
filename := tmpFile.Name()
|
|
defer os.Remove(filename)
|
|
|
|
tmpFile.Close()
|
|
|
|
testData := []byte("test-data")
|
|
err = WriteToFile(filename, testData)
|
|
assert.NoError(err)
|
|
|
|
data, err := os.ReadFile(filename)
|
|
assert.NoError(err)
|
|
|
|
assert.True(reflect.DeepEqual(testData, data))
|
|
}
|
|
|
|
func TestCalculateMilliCPUs(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
n := CalculateMilliCPUs(1, 1)
|
|
expected := uint32(1000)
|
|
assert.Equal(n, expected)
|
|
|
|
n = CalculateMilliCPUs(1, 0)
|
|
expected = uint32(0)
|
|
assert.Equal(n, expected)
|
|
|
|
n = CalculateMilliCPUs(-1, 1)
|
|
assert.Equal(n, expected)
|
|
}
|
|
|
|
func TestCaluclateVCpusFromMilliCpus(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
n := CalculateVCpusFromMilliCpus(1)
|
|
expected := uint32(1)
|
|
assert.Equal(n, expected)
|
|
}
|
|
|
|
func TestGetVirtDriveNameInvalidIndex(t *testing.T) {
|
|
assert := assert.New(t)
|
|
_, err := GetVirtDriveName(-1)
|
|
assert.Error(err)
|
|
}
|
|
|
|
func TestGetVirtDriveName(t *testing.T) {
|
|
assert := assert.New(t)
|
|
// nolint: govet
|
|
tests := []struct {
|
|
index int
|
|
expectedDrive string
|
|
}{
|
|
{0, "vda"},
|
|
{25, "vdz"},
|
|
{27, "vdab"},
|
|
{704, "vdaac"},
|
|
{18277, "vdzzz"},
|
|
}
|
|
|
|
for i, test := range tests {
|
|
msg := fmt.Sprintf("test[%d]: %+v", i, test)
|
|
driveName, err := GetVirtDriveName(test.index)
|
|
assert.NoError(err, msg)
|
|
assert.Equal(driveName, test.expectedDrive, msg)
|
|
}
|
|
}
|
|
|
|
func TestGetSCSIIdLun(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
tests := []struct {
|
|
index int
|
|
expectedScsiID int
|
|
expectedLun int
|
|
}{
|
|
{0, 0, 0},
|
|
{1, 0, 1},
|
|
{2, 0, 2},
|
|
{255, 0, 255},
|
|
{256, 1, 0},
|
|
{257, 1, 1},
|
|
{258, 1, 2},
|
|
{512, 2, 0},
|
|
{513, 2, 1},
|
|
}
|
|
|
|
for i, test := range tests {
|
|
msg := fmt.Sprintf("test[%d]: %+v", i, test)
|
|
scsiID, lun, err := GetSCSIIdLun(test.index)
|
|
assert.NoError(err, msg)
|
|
assert.Equal(scsiID, test.expectedScsiID, msg)
|
|
assert.Equal(lun, test.expectedLun, msg)
|
|
}
|
|
|
|
_, _, err := GetSCSIIdLun(-1)
|
|
assert.Error(err)
|
|
_, _, err = GetSCSIIdLun(maxSCSIDevices + 1)
|
|
assert.Error(err)
|
|
}
|
|
|
|
func TestGetSCSIAddress(t *testing.T) {
|
|
assert := assert.New(t)
|
|
// nolint: govet
|
|
tests := []struct {
|
|
index int
|
|
expectedSCSIAddress string
|
|
}{
|
|
{0, "0:0"},
|
|
{200, "0:200"},
|
|
{255, "0:255"},
|
|
{258, "1:2"},
|
|
{512, "2:0"},
|
|
}
|
|
|
|
for i, test := range tests {
|
|
msg := fmt.Sprintf("test[%d]: %+v", i, test)
|
|
scsiAddr, err := GetSCSIAddress(test.index)
|
|
assert.NoError(err, msg)
|
|
assert.Equal(scsiAddr, test.expectedSCSIAddress, msg)
|
|
}
|
|
|
|
_, err := GetSCSIAddress(-1)
|
|
assert.Error(err)
|
|
}
|
|
|
|
func TestMakeNameID(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
nameID := MakeNameID("testType", "testID", 14)
|
|
expected := "testType-testI"
|
|
assert.Equal(expected, nameID)
|
|
}
|
|
|
|
func TestBuildSocketPath(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
// nolint: govet
|
|
type testData struct {
|
|
elems []string
|
|
valid bool
|
|
expected string
|
|
}
|
|
|
|
longPath := strings.Repeat("/a", 106/2)
|
|
longestPath := longPath + "a"
|
|
pathTooLong := filepath.Join(longestPath, "x")
|
|
|
|
data := []testData{
|
|
{[]string{""}, false, ""},
|
|
|
|
{[]string{"a"}, true, "a"},
|
|
{[]string{"/a"}, true, "/a"},
|
|
{[]string{"a", "b", "c"}, true, "a/b/c"},
|
|
{[]string{"a", "/b", "c"}, true, "a/b/c"},
|
|
{[]string{"/a", "b", "c"}, true, "/a/b/c"},
|
|
{[]string{"/a", "/b", "/c"}, true, "/a/b/c"},
|
|
|
|
{[]string{longPath}, true, longPath},
|
|
{[]string{longestPath}, true, longestPath},
|
|
{[]string{pathTooLong}, false, ""},
|
|
}
|
|
|
|
for i, d := range data {
|
|
result, err := BuildSocketPath(d.elems...)
|
|
msg := fmt.Sprintf("test[%d]: %+v", i, d)
|
|
|
|
if d.valid {
|
|
assert.NoError(err, msg)
|
|
} else {
|
|
assert.Error(err, msg)
|
|
}
|
|
|
|
assert.NotNil(result, msg)
|
|
assert.Equal(d.expected, result, msg)
|
|
}
|
|
}
|
|
|
|
func TestSupportsVsocks(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
orgVHostVSockDevicePath := VHostVSockDevicePath
|
|
defer func() {
|
|
VHostVSockDevicePath = orgVHostVSockDevicePath
|
|
}()
|
|
|
|
VHostVSockDevicePath = "/abc/xyz/123"
|
|
assert.False(SupportsVsocks())
|
|
|
|
vHostVSockFile, err := os.CreateTemp("", "vhost-vsock")
|
|
assert.NoError(err)
|
|
defer os.Remove(vHostVSockFile.Name())
|
|
defer vHostVSockFile.Close()
|
|
VHostVSockDevicePath = vHostVSockFile.Name()
|
|
|
|
assert.True(SupportsVsocks())
|
|
}
|
|
|
|
func TestAlignMem(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
memSize := MemUnit(1024) * MiB
|
|
blockSize := MemUnit(512) * MiB
|
|
resultMem := memSize.AlignMem(blockSize)
|
|
expected := memSize
|
|
assert.Equal(expected, resultMem)
|
|
|
|
memSize = MemUnit(512) * MiB
|
|
blockSize = MemUnit(1024) * MiB
|
|
resultMem = memSize.AlignMem(blockSize)
|
|
expected = blockSize
|
|
assert.Equal(expected, resultMem)
|
|
|
|
memSize = MemUnit(1024) * MiB
|
|
blockSize = MemUnit(50) * MiB
|
|
resultMem = memSize.AlignMem(blockSize)
|
|
expected = memSize + (blockSize - (memSize % blockSize))
|
|
assert.Equal(expected, resultMem)
|
|
}
|
|
|
|
func TestToMiB(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
memSize := MemUnit(1) * GiB
|
|
result := memSize.ToMiB()
|
|
expected := uint64(1024)
|
|
assert.Equal(expected, result)
|
|
}
|
|
|
|
func TestToBytes(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
memSize := MemUnit(1) * GiB
|
|
result := memSize.ToBytes()
|
|
expected := uint64(1073741824)
|
|
assert.Equal(expected, result)
|
|
}
|
|
|
|
func TestWaitLocalProcess(t *testing.T) {
|
|
cfg := []struct {
|
|
command string
|
|
args []string
|
|
timeout uint
|
|
signal syscall.Signal
|
|
}{
|
|
{
|
|
"true",
|
|
[]string{},
|
|
waitLocalProcessTimeoutSecs,
|
|
syscall.SIGKILL,
|
|
},
|
|
{
|
|
"sleep",
|
|
[]string{"999"},
|
|
waitLocalProcessTimeoutSecs,
|
|
syscall.SIGKILL,
|
|
},
|
|
{
|
|
"sleep",
|
|
[]string{"999"},
|
|
1,
|
|
syscall.SIGKILL,
|
|
},
|
|
}
|
|
|
|
logger := logrus.WithField("foo", "bar")
|
|
|
|
for _, opts := range cfg {
|
|
assert := assert.New(t)
|
|
|
|
cmd := exec.Command(opts.command, opts.args...)
|
|
err := cmd.Start()
|
|
assert.NoError(err)
|
|
|
|
pid := cmd.Process.Pid
|
|
|
|
err = WaitLocalProcess(pid, opts.timeout, opts.signal, logger)
|
|
assert.NoError(err)
|
|
|
|
_ = cmd.Wait()
|
|
}
|
|
}
|
|
|
|
func TestWaitLocalProcessInvalidSignal(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
const invalidSignal = syscall.Signal(999)
|
|
|
|
cmd := exec.Command("sleep", "999")
|
|
err := cmd.Start()
|
|
assert.NoError(err)
|
|
|
|
pid := cmd.Process.Pid
|
|
|
|
logger := logrus.WithField("foo", "bar")
|
|
|
|
err = WaitLocalProcess(pid, waitLocalProcessTimeoutSecs, invalidSignal, logger)
|
|
assert.Error(err)
|
|
|
|
err = syscall.Kill(pid, syscall.SIGTERM)
|
|
assert.NoError(err)
|
|
|
|
err = cmd.Wait()
|
|
|
|
// This will error because we killed the process without the knowledge
|
|
// of exec.Command.
|
|
assert.Error(err)
|
|
}
|
|
|
|
func TestWaitLocalProcessInvalidPid(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
invalidPids := []int{-999, -173, -3, -2, -1, 0}
|
|
|
|
logger := logrus.WithField("foo", "bar")
|
|
|
|
for i, pid := range invalidPids {
|
|
msg := fmt.Sprintf("test[%d]: %v", i, pid)
|
|
|
|
err := WaitLocalProcess(pid, waitLocalProcessTimeoutSecs, syscall.Signal(0), logger)
|
|
assert.Error(err, msg)
|
|
}
|
|
}
|
|
|
|
func TestMkdirAllWithInheritedOwnerSuccessful(t *testing.T) {
|
|
if os.Getuid() != 0 {
|
|
t.Skip("Test disabled as requires root user")
|
|
}
|
|
assert := assert.New(t)
|
|
tmpDir1 := t.TempDir()
|
|
tmpDir2 := t.TempDir()
|
|
|
|
testCases := []struct {
|
|
before func(rootDir string, uid, gid int)
|
|
rootDir string
|
|
targetDir string
|
|
uid int
|
|
gid int
|
|
}{
|
|
{
|
|
before: func(rootDir string, uid, gid int) {
|
|
err := syscall.Chown(rootDir, uid, gid)
|
|
assert.NoError(err)
|
|
},
|
|
rootDir: tmpDir1,
|
|
targetDir: path.Join(tmpDir1, "foo", "bar"),
|
|
uid: 1234,
|
|
gid: 5678,
|
|
},
|
|
{
|
|
before: func(rootDir string, uid, gid int) {
|
|
// remove the tmpDir2 so the MkdirAllWithInheritedOwner() call creates them from /tmp
|
|
err := os.RemoveAll(tmpDir2)
|
|
assert.NoError(err)
|
|
},
|
|
rootDir: tmpDir2,
|
|
targetDir: path.Join(tmpDir2, "foo", "bar"),
|
|
uid: 0,
|
|
gid: 0,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
if tc.before != nil {
|
|
tc.before(tc.rootDir, tc.uid, tc.gid)
|
|
}
|
|
|
|
err := MkdirAllWithInheritedOwner(tc.targetDir, 0700)
|
|
assert.NoError(err)
|
|
// tmpDir1: /tmp/TestMkdirAllWithInheritedOwnerSuccessful/001
|
|
// tmpDir2: /tmp/TestMkdirAllWithInheritedOwnerSuccessful/002
|
|
// remove the first two parent "/tmp/TestMkdirAllWithInheritedOwnerSuccessful" from the assertion as it's owned by root
|
|
for _, p := range getAllParentPaths(tc.targetDir)[2:] {
|
|
info, err := os.Stat(p)
|
|
assert.NoError(err)
|
|
assert.True(info.IsDir())
|
|
stat, ok := info.Sys().(*syscall.Stat_t)
|
|
assert.True(ok)
|
|
assert.Equal(tc.uid, int(stat.Uid))
|
|
assert.Equal(tc.gid, int(stat.Gid))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestChownToParent(t *testing.T) {
|
|
if os.Getuid() != 0 {
|
|
t.Skip("Test disabled as requires root user")
|
|
}
|
|
assert := assert.New(t)
|
|
rootDir := t.TempDir()
|
|
uid := 1234
|
|
gid := 5678
|
|
err := syscall.Chown(rootDir, uid, gid)
|
|
assert.NoError(err)
|
|
|
|
targetDir := path.Join(rootDir, "foo")
|
|
|
|
err = os.MkdirAll(targetDir, 0700)
|
|
assert.NoError(err)
|
|
|
|
err = ChownToParent(targetDir)
|
|
assert.NoError(err)
|
|
|
|
info, err := os.Stat(targetDir)
|
|
assert.NoError(err)
|
|
stat, ok := info.Sys().(*syscall.Stat_t)
|
|
assert.True(ok)
|
|
assert.Equal(uid, int(stat.Uid))
|
|
assert.Equal(gid, int(stat.Gid))
|
|
}
|
|
|
|
func TestGetAllParentPaths(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
testCases := []struct {
|
|
targetPath string
|
|
parents []string
|
|
}{
|
|
{
|
|
targetPath: "/",
|
|
parents: []string{},
|
|
},
|
|
{
|
|
targetPath: ".",
|
|
parents: []string{},
|
|
},
|
|
{
|
|
targetPath: "foo",
|
|
parents: []string{"foo"},
|
|
},
|
|
{
|
|
targetPath: "/tmp/bar",
|
|
parents: []string{"/tmp", "/tmp/bar"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
assert.Equal(tc.parents, getAllParentPaths(tc.targetPath))
|
|
}
|
|
}
|
|
|
|
func TestRevertBytes(t *testing.T) {
|
|
assert := assert.New(t)
|
|
|
|
//10MB
|
|
testNum := uint64(10000000)
|
|
expectedNum := uint64(10485760)
|
|
|
|
num := RevertBytes(testNum)
|
|
assert.Equal(expectedNum, num)
|
|
}
|