clh: test: add unit test

Add unit test for clh.

- Check endpoint has valid values for CH.
- Add unit tests
- Add force flag to ignore cleanup errors.
- Add unit tests.
- Fail if hypervisor ID is empty.
- Add createSandbox uni test

Signed-off-by: Jose Carlos Venegas Munoz <jose.carlos.venegas.munoz@intel.com>
This commit is contained in:
Jose Carlos Venegas Munoz 2019-12-16 20:23:49 +00:00
parent 8a439eab9d
commit 6a10cd960d
3 changed files with 219 additions and 12 deletions

View File

@ -446,7 +446,9 @@ func (clh *cloudHypervisor) addDevice(devInfo interface{}, devType deviceType) e
switch v := devInfo.(type) {
case Endpoint:
clh.addNet(v)
if err := clh.addNet(v); err != nil {
return err
}
case types.HybridVSock:
clh.addVSock(defaultGuestVSockCID, v.UdsPath)
case types.Volume:
@ -522,7 +524,7 @@ func (clh *cloudHypervisor) terminate() (err error) {
}
}
clh.cleanupVM()
_ = clh.cleanupVM(true)
}()
pid := clh.state.PID
@ -1049,16 +1051,29 @@ func (clh *cloudHypervisor) addVSock(cid int64, path string) {
clh.vmconfig.Vsock = append(clh.vmconfig.Vsock, chclient.VsockConfig{Cid: cid, Sock: path})
}
func (clh *cloudHypervisor) addNet(e Endpoint) {
func (clh *cloudHypervisor) addNet(e Endpoint) error {
clh.Logger().WithField("endpoint-type", e).Debugf("Adding Endpoint of type %v", e)
mac := e.HardwareAddr()
tapPath := e.NetworkPair().TapInterface.TAPIface.Name
netPair := e.NetworkPair()
if netPair == nil {
return errors.New("net Pair to be added is nil, needed to get TAP path")
}
tapPath := netPair.TapInterface.TAPIface.Name
if tapPath == "" {
return errors.New("TAP path in network pair is empty")
}
clh.Logger().WithFields(log.Fields{
"mac": mac,
"tap": tapPath,
}).Info("Adding Net")
clh.vmconfig.Net = append(clh.vmconfig.Net, chclient.NetConfig{Mac: mac, Tap: tapPath})
return nil
}
// Add shared Volume using virtiofs
@ -1095,25 +1110,37 @@ func (clh *cloudHypervisor) addVolume(volume types.Volume) error {
}
// cleanupVM will remove generated files and directories related with the virtual machine
func (clh *cloudHypervisor) cleanupVM() error {
func (clh *cloudHypervisor) cleanupVM(force bool) error {
if clh.id == "" {
return errors.New("Hypervisor ID is empty")
}
// cleanup vm path
dir := filepath.Join(store.RunVMStoragePath(), clh.id)
// If it's a symlink, remove both dir and the target.
// This can happen when vm template links a sandbox to a vm.
link, err := filepath.EvalSymlinks(dir)
if err != nil {
// Well, it's just cleanup failure. Let's ignore it.
clh.Logger().WithError(err).WithField("dir", dir).Warn("failed to resolve vm path")
}
clh.Logger().WithField("link", link).WithField("dir", dir).Infof("cleanup vm path")
clh.Logger().WithFields(log.Fields{
"link": link,
"dir": dir,
}).Infof("cleanup vm path")
if err := os.RemoveAll(dir); err != nil {
if !force {
return err
}
clh.Logger().WithError(err).Warnf("failed to remove vm path %s", dir)
}
if link != dir && link != "" {
if err := os.RemoveAll(link); err != nil {
if !force {
return err
}
clh.Logger().WithError(err).WithField("link", link).Warn("failed to remove resolved vm path")
}
}
@ -1121,10 +1148,16 @@ func (clh *cloudHypervisor) cleanupVM() error {
if clh.config.VMid != "" {
dir = store.SandboxConfigurationRootPath(clh.config.VMid)
if err := os.RemoveAll(dir); err != nil {
if !force {
return err
}
clh.Logger().WithError(err).WithField("path", dir).Warnf("failed to remove vm path")
}
dir = store.SandboxRuntimeRootPath(clh.config.VMid)
if err := os.RemoveAll(dir); err != nil {
if !force {
return err
}
clh.Logger().WithError(err).WithField("path", dir).Warnf("failed to remove vm path")
}
}

View File

@ -8,12 +8,52 @@ package virtcontainers
import (
"context"
"net/http"
"os"
"path/filepath"
"testing"
"github.com/kata-containers/runtime/virtcontainers/device/config"
chclient "github.com/kata-containers/runtime/virtcontainers/pkg/cloud-hypervisor/client"
"github.com/kata-containers/runtime/virtcontainers/store"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
func newClhConfig() (HypervisorConfig, error) {
setupClh()
if testClhPath == "" {
return HypervisorConfig{}, errors.New("hypervisor fake path is empty")
}
if testVirtiofsdPath == "" {
return HypervisorConfig{}, errors.New("hypervisor fake path is empty")
}
if _, err := os.Stat(testClhPath); os.IsNotExist(err) {
return HypervisorConfig{}, err
}
if _, err := os.Stat(testVirtiofsdPath); os.IsNotExist(err) {
return HypervisorConfig{}, err
}
return HypervisorConfig{
KernelPath: testClhKernelPath,
ImagePath: testClhImagePath,
HypervisorPath: testClhPath,
NumVCPUs: defaultVCPUs,
BlockDeviceDriver: config.VirtioBlock,
MemorySize: defaultMemSzMiB,
DefaultBridges: defaultBridges,
DefaultMaxVCPUs: MaxClhVCPUs(),
SharedFS: config.VirtioFS,
VirtioFSCache: virtioFsCacheAlways,
VirtioFSDaemon: testVirtiofsdPath,
}, nil
}
type clhClientMock struct {
vmInfo chclient.VmInfo
}
@ -54,6 +94,74 @@ func TestCloudHypervisorAddVSock(t *testing.T) {
assert.Equal(clh.vmconfig.Vsock[1].Sock, "path2")
}
// Check addNet appends to the network config list new configurations.
// Check that the elements in the list has the correct values
func TestCloudHypervisorAddNetCheckNetConfigListValues(t *testing.T) {
macTest := "00:00:00:00:00"
tapPath := "/path/to/tap"
assert := assert.New(t)
clh := cloudHypervisor{}
e := &VethEndpoint{}
e.NetPair.TAPIface.HardAddr = macTest
e.NetPair.TapInterface.TAPIface.Name = tapPath
err := clh.addNet(e)
assert.Nil(err)
assert.Equal(len(clh.vmconfig.Net), 1)
if err == nil {
assert.Equal(clh.vmconfig.Net[0].Mac, macTest)
assert.Equal(clh.vmconfig.Net[0].Tap, tapPath)
}
err = clh.addNet(e)
assert.Nil(err)
assert.Equal(len(clh.vmconfig.Net), 2)
if err == nil {
assert.Equal(clh.vmconfig.Net[1].Mac, macTest)
assert.Equal(clh.vmconfig.Net[1].Tap, tapPath)
}
}
// Check addNet with valid values, and fail with invalid values
// For Cloud Hypervisor only tap is be required
func TestCloudHypervisorAddNetCheckEnpointTypes(t *testing.T) {
assert := assert.New(t)
tapPath := "/path/to/tap"
validVeth := &VethEndpoint{}
validVeth.NetPair.TapInterface.TAPIface.Name = tapPath
type args struct {
e Endpoint
}
tests := []struct {
name string
args args
wantErr bool
}{
{"TapEndpoint", args{e: &TapEndpoint{}}, true},
{"Empty VethEndpoint", args{e: &VethEndpoint{}}, true},
{"Valid VethEndpoint", args{e: validVeth}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
clh := &cloudHypervisor{}
if err := clh.addNet(tt.args.e); (err != nil) != tt.wantErr {
t.Errorf("cloudHypervisor.addNet() error = %v, wantErr %v", err, tt.wantErr)
} else if err == nil {
assert.Equal(clh.vmconfig.Net[0].Tap, tapPath)
}
})
}
}
func TestCloudHypervisorBootVM(t *testing.T) {
clh := &cloudHypervisor{}
clh.APIClient = &clhClientMock{}
@ -62,3 +170,66 @@ func TestCloudHypervisorBootVM(t *testing.T) {
t.Errorf("cloudHypervisor.bootVM() error = %v", err)
}
}
func TestCloudHypervisorCleanupVM(t *testing.T) {
clh := &cloudHypervisor{}
if err := clh.cleanupVM(true); err == nil {
t.Errorf("cloudHypervisor.cleanupVM() expected error != %v", err)
}
clh.id = "cleanVMID"
if err := clh.cleanupVM(true); err != nil {
t.Errorf("cloudHypervisor.cleanupVM() expected error != %v", err)
}
dir := filepath.Join(store.RunVMStoragePath(), clh.id)
os.MkdirAll(dir, os.ModePerm)
if err := clh.cleanupVM(false); err != nil {
t.Errorf("cloudHypervisor.cleanupVM() expected error != %v", err)
}
_, err := os.Stat(dir)
if err == nil {
t.Errorf("dir should not exist %s", dir)
}
if !os.IsNotExist(err) {
t.Errorf("Unexpected error = %v", err)
}
}
func TestClhCreateSandbox(t *testing.T) {
assert := assert.New(t)
clhConfig, err := newClhConfig()
assert.NoError(err)
clh := &cloudHypervisor{
config: clhConfig,
}
sandbox := &Sandbox{
ctx: context.Background(),
id: "testSandbox",
config: &SandboxConfig{
HypervisorConfig: clhConfig,
},
}
vcStore, err := store.NewVCSandboxStore(sandbox.ctx, sandbox.id)
assert.NoError(err)
sandbox.store = vcStore
// Create parent dir path for hypervisor.json
parentDir := store.SandboxConfigurationRootPath(sandbox.id)
assert.NoError(os.MkdirAll(parentDir, store.DirMode))
err = clh.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, sandbox.store, false)
assert.NoError(err)
assert.NoError(os.RemoveAll(parentDir))
assert.Exactly(clhConfig, clh.config)
}

View File

@ -25,6 +25,7 @@ const testKernel = "kernel"
const testInitrd = "initrd"
const testImage = "image"
const testHypervisor = "hypervisor"
const testVirtiofsd = "virtiofsd"
const testHypervisorCtl = "hypervisorctl"
const testBundle = "bundle"
@ -49,6 +50,7 @@ var testAcrnKernelPath = ""
var testAcrnImagePath = ""
var testAcrnPath = ""
var testAcrnCtlPath = ""
var testVirtiofsdPath = ""
var testHyperstartCtlSocket = ""
var testHyperstartTtySocket = ""
@ -91,7 +93,7 @@ func setupAcrn() {
func setupClh() {
os.Mkdir(filepath.Join(testDir, testBundle), store.DirMode)
for _, filename := range []string{testClhKernelPath, testClhImagePath, testClhPath} {
for _, filename := range []string{testClhKernelPath, testClhImagePath, testClhPath, testVirtiofsdPath} {
_, err := os.Create(filename)
if err != nil {
fmt.Printf("Could not recreate %s:%v", filename, err)
@ -142,9 +144,10 @@ func TestMain(m *testing.M) {
setupAcrn()
testClhKernelPath = filepath.Join(testDir, testKernel)
testClhImagePath = filepath.Join(testDir, testImage)
testClhPath = filepath.Join(testDir, testHypervisor)
testVirtiofsdPath = filepath.Join(testDir, testBundle, testVirtiofsd)
testClhKernelPath = filepath.Join(testDir, testBundle, testKernel)
testClhImagePath = filepath.Join(testDir, testBundle, testImage)
testClhPath = filepath.Join(testDir, testBundle, testHypervisor)
setupClh()