gitee.com/leisunstar/runtime@v0.0.0-20200521203717-5cef3e7b53f9/virtcontainers/clh_test.go (about)

     1  // Copyright (c) 2019 Ericsson Eurolab Deutschland G.m.b.H.
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  //
     5  
     6  package virtcontainers
     7  
     8  import (
     9  	"context"
    10  	"fmt"
    11  	"net/http"
    12  	"os"
    13  	"path/filepath"
    14  	"reflect"
    15  	"testing"
    16  
    17  	"github.com/kata-containers/runtime/virtcontainers/device/config"
    18  	"github.com/kata-containers/runtime/virtcontainers/persist"
    19  	chclient "github.com/kata-containers/runtime/virtcontainers/pkg/cloud-hypervisor/client"
    20  	"github.com/kata-containers/runtime/virtcontainers/utils"
    21  	"github.com/pkg/errors"
    22  	"github.com/stretchr/testify/assert"
    23  )
    24  
    25  const (
    26  	FAIL = true
    27  	PASS = !FAIL
    28  )
    29  
    30  func newClhConfig() (HypervisorConfig, error) {
    31  
    32  	setupClh()
    33  
    34  	if testClhPath == "" {
    35  		return HypervisorConfig{}, errors.New("hypervisor fake path is empty")
    36  	}
    37  
    38  	if testVirtiofsdPath == "" {
    39  		return HypervisorConfig{}, errors.New("hypervisor fake path is empty")
    40  	}
    41  
    42  	if _, err := os.Stat(testClhPath); os.IsNotExist(err) {
    43  		return HypervisorConfig{}, err
    44  	}
    45  
    46  	if _, err := os.Stat(testVirtiofsdPath); os.IsNotExist(err) {
    47  		return HypervisorConfig{}, err
    48  	}
    49  
    50  	return HypervisorConfig{
    51  		KernelPath:        testClhKernelPath,
    52  		ImagePath:         testClhImagePath,
    53  		HypervisorPath:    testClhPath,
    54  		NumVCPUs:          defaultVCPUs,
    55  		BlockDeviceDriver: config.VirtioBlock,
    56  		MemorySize:        defaultMemSzMiB,
    57  		DefaultBridges:    defaultBridges,
    58  		DefaultMaxVCPUs:   uint32(64),
    59  		SharedFS:          config.VirtioFS,
    60  		VirtioFSCache:     virtioFsCacheAlways,
    61  		VirtioFSDaemon:    testVirtiofsdPath,
    62  	}, nil
    63  }
    64  
    65  type clhClientMock struct {
    66  	vmInfo chclient.VmInfo
    67  }
    68  
    69  func (c *clhClientMock) VmmPingGet(ctx context.Context) (chclient.VmmPingResponse, *http.Response, error) {
    70  	return chclient.VmmPingResponse{}, nil, nil
    71  }
    72  
    73  func (c *clhClientMock) ShutdownVMM(ctx context.Context) (*http.Response, error) {
    74  	return nil, nil
    75  }
    76  
    77  func (c *clhClientMock) CreateVM(ctx context.Context, vmConfig chclient.VmConfig) (*http.Response, error) {
    78  	c.vmInfo.State = clhStateCreated
    79  	return nil, nil
    80  }
    81  
    82  //nolint:golint
    83  func (c *clhClientMock) VmInfoGet(ctx context.Context) (chclient.VmInfo, *http.Response, error) {
    84  	return c.vmInfo, nil, nil
    85  }
    86  
    87  func (c *clhClientMock) BootVM(ctx context.Context) (*http.Response, error) {
    88  	c.vmInfo.State = clhStateRunning
    89  	return nil, nil
    90  }
    91  
    92  //nolint:golint
    93  func (c *clhClientMock) VmResizePut(ctx context.Context, vmResize chclient.VmResize) (*http.Response, error) {
    94  	return nil, nil
    95  }
    96  
    97  //nolint:golint
    98  func (c *clhClientMock) VmAddDevicePut(ctx context.Context, vmAddDevice chclient.VmAddDevice) (*http.Response, error) {
    99  	return nil, nil
   100  }
   101  
   102  //nolint:golint
   103  func (c *clhClientMock) VmAddDiskPut(ctx context.Context, diskConfig chclient.DiskConfig) (*http.Response, error) {
   104  	return nil, nil
   105  }
   106  
   107  func TestCloudHypervisorAddVSock(t *testing.T) {
   108  	assert := assert.New(t)
   109  	clh := cloudHypervisor{}
   110  
   111  	clh.addVSock(1, "path")
   112  	assert.Equal(clh.vmconfig.Vsock.Cid, int64(1))
   113  	assert.Equal(clh.vmconfig.Vsock.Sock, "path")
   114  }
   115  
   116  // Check addNet appends to the network config list new configurations.
   117  // Check that the elements in the list has the correct values
   118  func TestCloudHypervisorAddNetCheckNetConfigListValues(t *testing.T) {
   119  	macTest := "00:00:00:00:00"
   120  	tapPath := "/path/to/tap"
   121  
   122  	assert := assert.New(t)
   123  
   124  	clh := cloudHypervisor{}
   125  
   126  	e := &VethEndpoint{}
   127  	e.NetPair.TAPIface.HardAddr = macTest
   128  	e.NetPair.TapInterface.TAPIface.Name = tapPath
   129  
   130  	err := clh.addNet(e)
   131  	assert.Nil(err)
   132  
   133  	assert.Equal(len(clh.vmconfig.Net), 1)
   134  	if err == nil {
   135  		assert.Equal(clh.vmconfig.Net[0].Mac, macTest)
   136  		assert.Equal(clh.vmconfig.Net[0].Tap, tapPath)
   137  	}
   138  
   139  	err = clh.addNet(e)
   140  	assert.Nil(err)
   141  
   142  	assert.Equal(len(clh.vmconfig.Net), 2)
   143  	if err == nil {
   144  		assert.Equal(clh.vmconfig.Net[1].Mac, macTest)
   145  		assert.Equal(clh.vmconfig.Net[1].Tap, tapPath)
   146  	}
   147  }
   148  
   149  // Check addNet with valid values, and fail with invalid values
   150  // For Cloud Hypervisor only tap is be required
   151  func TestCloudHypervisorAddNetCheckEnpointTypes(t *testing.T) {
   152  	assert := assert.New(t)
   153  
   154  	tapPath := "/path/to/tap"
   155  
   156  	validVeth := &VethEndpoint{}
   157  	validVeth.NetPair.TapInterface.TAPIface.Name = tapPath
   158  
   159  	type args struct {
   160  		e Endpoint
   161  	}
   162  	tests := []struct {
   163  		name    string
   164  		args    args
   165  		wantErr bool
   166  	}{
   167  		{"TapEndpoint", args{e: &TapEndpoint{}}, true},
   168  		{"Empty VethEndpoint", args{e: &VethEndpoint{}}, true},
   169  		{"Valid VethEndpoint", args{e: validVeth}, false},
   170  	}
   171  	for _, tt := range tests {
   172  		t.Run(tt.name, func(t *testing.T) {
   173  			clh := &cloudHypervisor{}
   174  			if err := clh.addNet(tt.args.e); (err != nil) != tt.wantErr {
   175  				t.Errorf("cloudHypervisor.addNet() error = %v, wantErr %v", err, tt.wantErr)
   176  
   177  			} else if err == nil {
   178  				assert.Equal(clh.vmconfig.Net[0].Tap, tapPath)
   179  			}
   180  		})
   181  	}
   182  }
   183  
   184  func TestCloudHypervisorBootVM(t *testing.T) {
   185  	clh := &cloudHypervisor{}
   186  	clh.APIClient = &clhClientMock{}
   187  	var ctx context.Context
   188  	if err := clh.bootVM(ctx); err != nil {
   189  		t.Errorf("cloudHypervisor.bootVM() error = %v", err)
   190  	}
   191  }
   192  
   193  func TestCloudHypervisorCleanupVM(t *testing.T) {
   194  	assert := assert.New(t)
   195  	store, err := persist.GetDriver()
   196  	assert.NoError(err, "persist.GetDriver() unexpected error")
   197  
   198  	clh := &cloudHypervisor{
   199  		store: store,
   200  	}
   201  
   202  	err = clh.cleanupVM(true)
   203  	assert.Error(err, "persist.GetDriver() expected error")
   204  
   205  	clh.id = "cleanVMID"
   206  
   207  	err = clh.cleanupVM(true)
   208  	assert.NoError(err, "persist.GetDriver() unexpected error")
   209  
   210  	dir := filepath.Join(clh.store.RunVMStoragePath(), clh.id)
   211  	os.MkdirAll(dir, os.ModePerm)
   212  
   213  	err = clh.cleanupVM(false)
   214  	assert.NoError(err, "persist.GetDriver() unexpected error")
   215  
   216  	_, err = os.Stat(dir)
   217  	assert.Error(err, "dir should not exist %s", dir)
   218  
   219  	assert.True(os.IsNotExist(err), "persist.GetDriver() unexpected error")
   220  }
   221  
   222  func TestClhCreateSandbox(t *testing.T) {
   223  	assert := assert.New(t)
   224  
   225  	clhConfig, err := newClhConfig()
   226  	assert.NoError(err)
   227  
   228  	store, err := persist.GetDriver()
   229  	assert.NoError(err)
   230  
   231  	clh := &cloudHypervisor{
   232  		config: clhConfig,
   233  		store:  store,
   234  	}
   235  
   236  	sandbox := &Sandbox{
   237  		ctx: context.Background(),
   238  		id:  "testSandbox",
   239  		config: &SandboxConfig{
   240  			HypervisorConfig: clhConfig,
   241  		},
   242  	}
   243  
   244  	err = clh.createSandbox(context.Background(), sandbox.id, NetworkNamespace{}, &sandbox.config.HypervisorConfig, false)
   245  	assert.NoError(err)
   246  	assert.Exactly(clhConfig, clh.config)
   247  }
   248  
   249  func TestClooudHypervisorStartSandbox(t *testing.T) {
   250  	assert := assert.New(t)
   251  	clhConfig, err := newClhConfig()
   252  	assert.NoError(err)
   253  
   254  	store, err := persist.GetDriver()
   255  	assert.NoError(err)
   256  
   257  	clh := &cloudHypervisor{
   258  		config:    clhConfig,
   259  		APIClient: &clhClientMock{},
   260  		virtiofsd: &virtiofsdMock{},
   261  		store:     store,
   262  	}
   263  
   264  	err = clh.startSandbox(10)
   265  	assert.NoError(err)
   266  }
   267  
   268  func TestCloudHypervisorResizeMemory(t *testing.T) {
   269  	assert := assert.New(t)
   270  	clhConfig, err := newClhConfig()
   271  	type args struct {
   272  		reqMemMB          uint32
   273  		memoryBlockSizeMB uint32
   274  	}
   275  	tests := []struct {
   276  		name           string
   277  		args           args
   278  		expectedMemDev memoryDevice
   279  		wantErr        bool
   280  	}{
   281  		{"Resize to zero", args{0, 128}, memoryDevice{probe: false, sizeMB: 0}, FAIL},
   282  		{"Resize to aligned size", args{clhConfig.MemorySize + 128, 128}, memoryDevice{probe: false, sizeMB: 128}, PASS},
   283  		{"Resize to aligned size", args{clhConfig.MemorySize + 129, 128}, memoryDevice{probe: false, sizeMB: 256}, PASS},
   284  		{"Resize to NOT aligned size", args{clhConfig.MemorySize + 125, 128}, memoryDevice{probe: false, sizeMB: 128}, PASS},
   285  	}
   286  	for _, tt := range tests {
   287  		t.Run(tt.name, func(t *testing.T) {
   288  			assert.NoError(err)
   289  			clh := cloudHypervisor{}
   290  
   291  			mockClient := &clhClientMock{}
   292  			mockClient.vmInfo.Config.Memory.Size = int64(utils.MemUnit(clhConfig.MemorySize) * utils.MiB)
   293  			mockClient.vmInfo.Config.Memory.HotplugSize = int64(40 * utils.GiB.ToBytes())
   294  
   295  			clh.APIClient = mockClient
   296  			clh.config = clhConfig
   297  
   298  			newMem, memDev, err := clh.resizeMemory(tt.args.reqMemMB, tt.args.memoryBlockSizeMB, false)
   299  
   300  			if (err != nil) != tt.wantErr {
   301  				t.Errorf("cloudHypervisor.resizeMemory() error = %v, expected to fail = %v", err, tt.wantErr)
   302  				return
   303  			}
   304  
   305  			if err != nil {
   306  				return
   307  			}
   308  
   309  			expectedMem := clhConfig.MemorySize + uint32(tt.expectedMemDev.sizeMB)
   310  
   311  			if newMem != expectedMem {
   312  				t.Errorf("cloudHypervisor.resizeMemory() got = %+v, want %+v", newMem, expectedMem)
   313  			}
   314  
   315  			if !reflect.DeepEqual(memDev, tt.expectedMemDev) {
   316  				t.Errorf("cloudHypervisor.resizeMemory() got = %+v, want %+v", memDev, tt.expectedMemDev)
   317  			}
   318  		})
   319  	}
   320  }
   321  
   322  func TestCheckVersion(t *testing.T) {
   323  	clh := &cloudHypervisor{}
   324  	assert := assert.New(t)
   325  	testcases := []struct {
   326  		name  string
   327  		major int
   328  		minor int
   329  		pass  bool
   330  	}{
   331  		{
   332  			name:  "minor lower than supported version",
   333  			major: supportedMajorVersion,
   334  			minor: 2,
   335  			pass:  false,
   336  		},
   337  		{
   338  			name:  "minor equal to supported version",
   339  			major: supportedMajorVersion,
   340  			minor: supportedMinorVersion,
   341  			pass:  true,
   342  		},
   343  		{
   344  			name:  "major exceeding supported version",
   345  			major: 1,
   346  			minor: supportedMinorVersion,
   347  			pass:  true,
   348  		},
   349  	}
   350  	for _, tc := range testcases {
   351  		clh.version = CloudHypervisorVersion{
   352  			Major:    tc.major,
   353  			Minor:    tc.minor,
   354  			Revision: 0,
   355  		}
   356  		err := clh.checkVersion()
   357  		msg := fmt.Sprintf("test: %+v, clh.version: %v, result: %v", tc, clh.version, err)
   358  		if tc.pass {
   359  			assert.NoError(err, msg)
   360  		} else {
   361  			assert.Error(err, msg)
   362  		}
   363  	}
   364  }
   365  
   366  func TestCloudHypervisorHotplugBlockDevice(t *testing.T) {
   367  	assert := assert.New(t)
   368  
   369  	clhConfig, err := newClhConfig()
   370  	assert.NoError(err)
   371  
   372  	clh := &cloudHypervisor{}
   373  	clh.config = clhConfig
   374  	clh.APIClient = &clhClientMock{}
   375  
   376  	clh.config.BlockDeviceDriver = config.VirtioBlock
   377  	err = clh.hotplugBlockDevice(&config.BlockDrive{Pmem: false})
   378  	assert.NoError(err, "Hotplug disk block device expected no error")
   379  
   380  	err = clh.hotplugBlockDevice(&config.BlockDrive{Pmem: true})
   381  	assert.Error(err, "Hotplug pmem block device expected error")
   382  
   383  	clh.config.BlockDeviceDriver = config.VirtioSCSI
   384  	err = clh.hotplugBlockDevice(&config.BlockDrive{Pmem: false})
   385  	assert.Error(err, "Hotplug block device not using 'virtio-blk' expected error")
   386  }