github.com/Mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/manager/runtime_test.go (about)

     1  /*
     2  Copyright 2016-2017 Mirantis
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package manager
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io/ioutil"
    24  	"log"
    25  	"net"
    26  	"os"
    27  	"path/filepath"
    28  	"reflect"
    29  	"strings"
    30  	"testing"
    31  	"time"
    32  
    33  	cnitypes "github.com/containernetworking/cni/pkg/types"
    34  	cnicurrent "github.com/containernetworking/cni/pkg/types/current"
    35  	"github.com/davecgh/go-spew/spew"
    36  	"github.com/jonboulle/clockwork"
    37  	kubeapi "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
    38  
    39  	"github.com/Mirantis/virtlet/pkg/cni"
    40  	"github.com/Mirantis/virtlet/pkg/flexvolume"
    41  	"github.com/Mirantis/virtlet/pkg/fs"
    42  	fakefs "github.com/Mirantis/virtlet/pkg/fs/fake"
    43  	"github.com/Mirantis/virtlet/pkg/image"
    44  	fakeimage "github.com/Mirantis/virtlet/pkg/image/fake"
    45  	"github.com/Mirantis/virtlet/pkg/libvirttools"
    46  	"github.com/Mirantis/virtlet/pkg/metadata"
    47  	"github.com/Mirantis/virtlet/pkg/network"
    48  	"github.com/Mirantis/virtlet/pkg/tapmanager"
    49  	"github.com/Mirantis/virtlet/pkg/utils"
    50  	fakeutils "github.com/Mirantis/virtlet/pkg/utils/fake"
    51  	testutils "github.com/Mirantis/virtlet/pkg/utils/testing"
    52  	fakevirt "github.com/Mirantis/virtlet/pkg/virt/fake"
    53  	"github.com/Mirantis/virtlet/tests/criapi"
    54  	"github.com/Mirantis/virtlet/tests/gm"
    55  )
    56  
    57  const (
    58  	podTimestap = 1524648266720331175
    59  )
    60  
    61  type fakeFDManager struct {
    62  	rec         testutils.Recorder
    63  	items       map[string]bool
    64  	lastIpOctet byte
    65  }
    66  
    67  var _ tapmanager.FDManager = &fakeFDManager{}
    68  
    69  func newFakeFDManager(rec testutils.Recorder) *fakeFDManager {
    70  	return &fakeFDManager{
    71  		rec:         rec,
    72  		items:       make(map[string]bool),
    73  		lastIpOctet: 5,
    74  	}
    75  }
    76  
    77  func (m *fakeFDManager) AddFDs(key string, data interface{}) ([]byte, error) {
    78  	m.rec.Rec("AddFDs", map[string]interface{}{
    79  		"key":  key,
    80  		"data": data,
    81  	})
    82  
    83  	if m.items[key] {
    84  		return nil, fmt.Errorf("duplicate key: %q", key)
    85  	}
    86  
    87  	if strings.Contains(key, "should-fail-cni") {
    88  		return nil, fmt.Errorf("simulated cni failure on request")
    89  	}
    90  
    91  	fdPayload := data.(*tapmanager.GetFDPayload)
    92  	if fdPayload.Description == nil {
    93  		return nil, fmt.Errorf("AddFDs(): bad data: %#v", data)
    94  	}
    95  	macAddr := "42:a4:a6:22:80:2e"
    96  	parsedMacAddr, err := net.ParseMAC(macAddr)
    97  	if err != nil {
    98  		log.Panicf("Error parsing hwaddr %q: %v", macAddr, err)
    99  	}
   100  	nsPath := cni.PodNetNSPath(fdPayload.Description.PodID)
   101  	csn := &network.ContainerSideNetwork{
   102  		Result: &cnicurrent.Result{
   103  			Interfaces: []*cnicurrent.Interface{
   104  				{
   105  					Name:    "eth0",
   106  					Mac:     macAddr,
   107  					Sandbox: nsPath,
   108  				},
   109  			},
   110  			IPs: []*cnicurrent.IPConfig{
   111  				{
   112  					Version:   "4",
   113  					Interface: 0,
   114  					Address: net.IPNet{
   115  						IP:   net.IP{10, 1, 90, m.lastIpOctet},
   116  						Mask: net.IPMask{255, 255, 255, 0},
   117  					},
   118  					Gateway: net.IP{10, 1, 90, 1},
   119  				},
   120  			},
   121  			Routes: []*cnitypes.Route{
   122  				{
   123  					Dst: net.IPNet{
   124  						IP:   net.IP{0, 0, 0, 0},
   125  						Mask: net.IPMask{0, 0, 0, 0},
   126  					},
   127  					GW: net.IP{10, 1, 90, 1},
   128  				},
   129  			},
   130  		},
   131  		NsPath: nsPath,
   132  		Interfaces: []*network.InterfaceDescription{
   133  			{
   134  				Type:         network.InterfaceTypeTap,
   135  				HardwareAddr: parsedMacAddr,
   136  			},
   137  		},
   138  	}
   139  
   140  	respData, err := json.Marshal(csn)
   141  	if err != nil {
   142  		return nil, fmt.Errorf("error marshalling net config: %v", err)
   143  	}
   144  
   145  	m.lastIpOctet++
   146  	m.items[key] = true
   147  	return respData, nil
   148  }
   149  
   150  func (m *fakeFDManager) ReleaseFDs(key string) error {
   151  	m.rec.Rec("ReleaseFDs", key)
   152  	if !m.items[key] {
   153  		return fmt.Errorf("key not found: %q", key)
   154  	}
   155  	return nil
   156  }
   157  
   158  func (m *fakeFDManager) Recover(key string, data interface{}) error {
   159  	m.rec.Rec("Recover", key)
   160  	if m.items[key] {
   161  		return fmt.Errorf("duplicate key: %q", key)
   162  	}
   163  	return nil
   164  }
   165  
   166  type fakeStreamServer struct {
   167  	rec testutils.Recorder
   168  }
   169  
   170  var _ StreamServer = &fakeStreamServer{}
   171  
   172  func newFakeStreamServer(rec testutils.Recorder) *fakeStreamServer {
   173  	return &fakeStreamServer{rec}
   174  }
   175  
   176  func (s *fakeStreamServer) GetAttach(req *kubeapi.AttachRequest) (*kubeapi.AttachResponse, error) {
   177  	s.rec.Rec("GetAttach", req)
   178  	return &kubeapi.AttachResponse{
   179  		Url: "http://localhost:4242/",
   180  	}, nil
   181  }
   182  
   183  func (s *fakeStreamServer) GetPortForward(req *kubeapi.PortForwardRequest) (*kubeapi.PortForwardResponse, error) {
   184  	s.rec.Rec("GetPortForward", req)
   185  	return &kubeapi.PortForwardResponse{
   186  		Url: "http://localhost:4242/",
   187  	}, nil
   188  }
   189  
   190  func TestPodSanboxConfigValidation(t *testing.T) {
   191  	invalidSandboxes := criapi.GetSandboxes(1)
   192  
   193  	// Now let's make generated configs to be invalid
   194  	invalidSandboxes[0].Metadata = nil
   195  
   196  	if err := validatePodSandboxConfig(invalidSandboxes[0]); err == nil {
   197  		t.Errorf("Invalid pod sandbox passed validation:\n%s", spew.Sdump(invalidSandboxes[0]))
   198  	}
   199  }
   200  
   201  func translateImageName(ctx context.Context, name string) image.Endpoint {
   202  	return image.Endpoint{URL: name, MaxRedirects: -1}
   203  }
   204  
   205  type criHandler struct {
   206  	*VirtletRuntimeService
   207  	*VirtletImageService
   208  }
   209  
   210  type virtletCRITester struct {
   211  	t              *testing.T
   212  	rec            *testutils.TopLevelRecorder
   213  	handler        *criHandler
   214  	tmpDir         string
   215  	kubeletRootDir string
   216  }
   217  
   218  func makeVirtletCRITester(t *testing.T) *virtletCRITester {
   219  	rec := testutils.NewToplevelRecorder()
   220  	tmpDir, err := ioutil.TempDir("", "virtualization-test-")
   221  	if err != nil {
   222  		t.Fatalf("TempDir(): %v", err)
   223  	}
   224  	// __config__  is a hint for fake libvirt domain to fix the path
   225  	libvirttools.SetConfigIsoDir(filepath.Join(tmpDir, "__config__"))
   226  	fdManager := newFakeFDManager(rec.Child("fdManager"))
   227  	imageStore := fakeimage.NewFakeStore(rec.Child("imageStore"))
   228  	metadataStore, err := metadata.NewFakeStore()
   229  	if err != nil {
   230  		t.Fatalf("Failed to create fake bolt client: %v", err)
   231  	}
   232  	domainConn := fakevirt.NewFakeDomainConnection(rec.Child("domain conn"))
   233  	storageConn := fakevirt.NewFakeStorageConnection(rec.Child("storage"))
   234  	clock := clockwork.NewFakeClockAt(time.Unix(0, podTimestap))
   235  	kubeletRootDir := filepath.Join(tmpDir, "kubelet-root")
   236  	virtConfig := libvirttools.VirtualizationConfig{
   237  		VolumePoolName:     "volumes",
   238  		RawDevices:         []string{"loop*"},
   239  		KubeletRootDir:     kubeletRootDir,
   240  		StreamerSocketPath: streamerSocketPath,
   241  	}
   242  	commander := fakeutils.NewCommander(rec, nil)
   243  	virtTool := libvirttools.NewVirtualizationTool(
   244  		domainConn, storageConn, imageStore, metadataStore,
   245  		libvirttools.GetDefaultVolumeSource(), virtConfig,
   246  		fakefs.NewFakeFileSystem(t, rec, "", nil), commander)
   247  	virtTool.SetClock(clock)
   248  	streamServer := newFakeStreamServer(rec.Child("streamServer"))
   249  	criHandler := &criHandler{
   250  		VirtletRuntimeService: NewVirtletRuntimeService(virtTool, metadataStore, fdManager, streamServer, imageStore, clock),
   251  		VirtletImageService:   NewVirtletImageService(imageStore, translateImageName, clock),
   252  	}
   253  	return &virtletCRITester{
   254  		t:              t,
   255  		rec:            rec,
   256  		handler:        criHandler,
   257  		tmpDir:         tmpDir,
   258  		kubeletRootDir: kubeletRootDir,
   259  	}
   260  }
   261  
   262  func (tst *virtletCRITester) teardown() {
   263  	os.RemoveAll(tst.tmpDir)
   264  }
   265  
   266  func (tst *virtletCRITester) invoke(name string, req interface{}, failOnError bool) (interface{}, error) {
   267  	tst.rec.Rec("enter: "+name, req)
   268  	v := reflect.ValueOf(tst.handler)
   269  	method := v.MethodByName(name)
   270  	if method.Kind() == reflect.Invalid {
   271  		tst.t.Fatalf("bad method %q", name)
   272  	}
   273  	ctx := context.Background()
   274  	vals := method.Call([]reflect.Value{
   275  		reflect.ValueOf(ctx),
   276  		reflect.ValueOf(req),
   277  	})
   278  	if len(vals) != 2 {
   279  		tst.t.Fatalf("expected method %q to return 2 values but it returned %#v", name, vals)
   280  	}
   281  	if !vals[1].IsNil() {
   282  		err, ok := vals[1].Interface().(error)
   283  		if !ok {
   284  			tst.t.Fatalf("2nd returned value is %#v instead of error", vals[1].Interface())
   285  		} else {
   286  			if failOnError {
   287  				tst.t.Errorf("method %q returned error: %v", name, err)
   288  			}
   289  		}
   290  		return nil, err
   291  	}
   292  	resp := vals[0].Interface()
   293  	tst.rec.Rec("leave: "+name, resp)
   294  	return resp, nil
   295  }
   296  
   297  func (tst *virtletCRITester) getSampleFlexvolMounts(podSandboxID string) []*kubeapi.Mount {
   298  	flexVolumeDriver := flexvolume.NewDriver(func() string {
   299  		return "abb67e3c-71b3-4ddd-5505-8c4215d5c4eb"
   300  	}, fs.NullFileSystem)
   301  	flexVolDir := filepath.Join(tst.kubeletRootDir, podSandboxID, "volumes/virtlet~flexvolume_driver", "vol1")
   302  	flexVolDef := map[string]interface{}{
   303  		"type":     "qcow2",
   304  		"capacity": "2MB",
   305  	}
   306  	resultStr := flexVolumeDriver.Run([]string{"mount", flexVolDir, utils.ToJSON(flexVolDef)})
   307  	var r map[string]interface{}
   308  	if err := json.Unmarshal([]byte(resultStr), &r); err != nil {
   309  		tst.t.Fatalf("failed to unmarshal flexvolume definition")
   310  	}
   311  	if r["status"] != "Success" {
   312  		tst.t.Fatalf("mounting flexvolume vol1 failed: %s", r["message"])
   313  	}
   314  	return []*kubeapi.Mount{
   315  		{
   316  			ContainerPath: "/mnt",
   317  			HostPath:      flexVolDir,
   318  		},
   319  	}
   320  }
   321  
   322  func (tst *virtletCRITester) verify() {
   323  	verifier := gm.NewYamlVerifier(tst.rec.Content())
   324  	gm.Verify(tst.t, gm.NewSubstVerifier(verifier, []gm.Replacement{
   325  		{
   326  			Old: tst.tmpDir,
   327  			New: "__top__",
   328  		},
   329  	}))
   330  }
   331  
   332  func (tst *virtletCRITester) listImages(filter *kubeapi.ImageFilter) {
   333  	tst.invoke("ListImages", &kubeapi.ListImagesRequest{Filter: filter}, true)
   334  }
   335  
   336  func (tst *virtletCRITester) pullImage(image *kubeapi.ImageSpec) {
   337  	tst.invoke("PullImage", &kubeapi.PullImageRequest{Image: image}, true)
   338  }
   339  
   340  func (tst *virtletCRITester) imageStatus(image *kubeapi.ImageSpec) {
   341  	tst.invoke("ImageStatus", &kubeapi.ImageStatusRequest{Image: image}, true)
   342  }
   343  
   344  func (tst *virtletCRITester) removeImage(image *kubeapi.ImageSpec) {
   345  	tst.invoke("RemoveImage", &kubeapi.RemoveImageRequest{Image: image}, true)
   346  }
   347  
   348  func (tst *virtletCRITester) listPodSandbox(filter *kubeapi.PodSandboxFilter) {
   349  	tst.invoke("ListPodSandbox", &kubeapi.ListPodSandboxRequest{Filter: filter}, true)
   350  }
   351  
   352  func (tst *virtletCRITester) runPodSandbox(config *kubeapi.PodSandboxConfig) {
   353  	tst.invoke("RunPodSandbox", &kubeapi.RunPodSandboxRequest{Config: config}, true)
   354  }
   355  
   356  func (tst *virtletCRITester) runPodSandboxAndExpectError(config *kubeapi.PodSandboxConfig) {
   357  	_, err := tst.invoke("RunPodSandbox", &kubeapi.RunPodSandboxRequest{Config: config}, false)
   358  	if err == nil {
   359  		tst.t.Errorf("didn't get an expected error from RunPodSandbox")
   360  	}
   361  }
   362  
   363  func (tst *virtletCRITester) podSandboxStatus(podSandboxID string) {
   364  	tst.invoke("PodSandboxStatus", &kubeapi.PodSandboxStatusRequest{PodSandboxId: podSandboxID}, true)
   365  }
   366  
   367  func (tst *virtletCRITester) stopPodSandox(podSandboxID string) {
   368  	tst.invoke("StopPodSandbox", &kubeapi.StopPodSandboxRequest{PodSandboxId: podSandboxID}, true)
   369  }
   370  
   371  func (tst *virtletCRITester) removePodSandox(podSandboxID string) {
   372  	tst.invoke("RemovePodSandbox", &kubeapi.RemovePodSandboxRequest{PodSandboxId: podSandboxID}, true)
   373  }
   374  
   375  func (tst *virtletCRITester) listContainers(filter *kubeapi.ContainerFilter) {
   376  	tst.invoke("ListContainers", &kubeapi.ListContainersRequest{Filter: filter}, true)
   377  }
   378  
   379  func (tst *virtletCRITester) listContainerStats(filter *kubeapi.ContainerStatsFilter) {
   380  	tst.invoke("ListContainerStats", &kubeapi.ListContainerStatsRequest{Filter: filter}, true)
   381  }
   382  
   383  func (tst *virtletCRITester) createContainer(sandbox *kubeapi.PodSandboxConfig, container *criapi.ContainerTestConfig, imageSpec *kubeapi.ImageSpec, mounts []*kubeapi.Mount) string {
   384  	req := createContainerRequest(sandbox, container, imageSpec, mounts)
   385  	resp, err := tst.invoke("CreateContainer", req, true)
   386  	if err != nil {
   387  		tst.t.Fatalf("CreateContainer returned an error: %v", err)
   388  		return "" // unreachable
   389  	}
   390  	if r, ok := resp.(*kubeapi.CreateContainerResponse); ok {
   391  		return r.ContainerId
   392  	}
   393  	tst.t.Fatalf("bad value returned by CreateContainer: %#v", resp)
   394  	return "" // unreachable
   395  }
   396  
   397  func (tst *virtletCRITester) containerStatus(containerID string) {
   398  	tst.invoke("ContainerStatus", &kubeapi.ContainerStatusRequest{ContainerId: containerID}, true)
   399  }
   400  
   401  func (tst *virtletCRITester) startContainer(containerID string) {
   402  	tst.invoke("StartContainer", &kubeapi.StartContainerRequest{ContainerId: containerID}, true)
   403  }
   404  
   405  func (tst *virtletCRITester) stopContainer(containerID string) {
   406  	tst.invoke("StopContainer", &kubeapi.StopContainerRequest{ContainerId: containerID}, true)
   407  }
   408  
   409  func (tst *virtletCRITester) containerStats(containerID string) {
   410  	tst.invoke("ContainerStats", &kubeapi.ContainerStatsRequest{ContainerId: containerID}, true)
   411  }
   412  
   413  func (tst *virtletCRITester) updateContainerResources(containerID, cpuSet string) {
   414  	tst.invoke("UpdateContainerResources", &kubeapi.UpdateContainerResourcesRequest{
   415  		ContainerId: containerID,
   416  		Linux:       &kubeapi.LinuxContainerResources{CpusetCpus: cpuSet},
   417  	}, true)
   418  }
   419  
   420  func (tst *virtletCRITester) removeContainer(containerID string) {
   421  	tst.invoke("RemoveContainer", &kubeapi.RemoveContainerRequest{ContainerId: containerID}, true)
   422  }
   423  
   424  func (tst *virtletCRITester) attach(req *kubeapi.AttachRequest) {
   425  	tst.invoke("Attach", req, true)
   426  }
   427  
   428  func (tst *virtletCRITester) portForward(req *kubeapi.PortForwardRequest) {
   429  	tst.invoke("PortForward", req, true)
   430  }
   431  
   432  func (tst *virtletCRITester) imageFsInfo(req *kubeapi.ImageFsInfoRequest) {
   433  	tst.invoke("ImageFsInfo", req, true)
   434  }
   435  
   436  func cirrosImg() *kubeapi.ImageSpec {
   437  	// return new object each time b/c in theory it can be
   438  	// modified by the handler
   439  	return &kubeapi.ImageSpec{Image: "localhost/cirros.img"}
   440  }
   441  
   442  func ubuntuImg() *kubeapi.ImageSpec {
   443  	// return new object each time b/c in theory it can be
   444  	// modified by the handler
   445  	return &kubeapi.ImageSpec{Image: "localhost/ubuntu.img"}
   446  }
   447  
   448  func createContainerRequest(sandbox *kubeapi.PodSandboxConfig, container *criapi.ContainerTestConfig, imageSpec *kubeapi.ImageSpec, mounts []*kubeapi.Mount) *kubeapi.CreateContainerRequest {
   449  	return &kubeapi.CreateContainerRequest{
   450  		PodSandboxId: sandbox.Metadata.Uid,
   451  		Config: &kubeapi.ContainerConfig{
   452  			Image:  imageSpec,
   453  			Labels: container.Labels,
   454  			Mounts: mounts,
   455  			Metadata: &kubeapi.ContainerMetadata{
   456  				Name: container.Name,
   457  			},
   458  		},
   459  		SandboxConfig: sandbox,
   460  	}
   461  }
   462  
   463  func TestCRIPods(t *testing.T) {
   464  	tst := makeVirtletCRITester(t)
   465  	defer tst.teardown()
   466  	tst.listPodSandbox(nil)
   467  	tst.listContainers(nil)
   468  
   469  	sandboxes := criapi.GetSandboxes(2)
   470  	containers := criapi.GetContainersConfig(sandboxes)
   471  	tst.pullImage(cirrosImg())
   472  	tst.runPodSandbox(sandboxes[0])
   473  	tst.listPodSandbox(nil)
   474  	tst.podSandboxStatus(sandboxes[0].Metadata.Uid)
   475  	containerId1 := tst.createContainer(sandboxes[0], containers[0], cirrosImg(), nil)
   476  	tst.listContainers(nil)
   477  	tst.containerStatus(containerId1)
   478  	tst.startContainer(containerId1)
   479  	tst.containerStatus(containerId1)
   480  	tst.containerStats(containerId1)
   481  	tst.listContainerStats(nil)
   482  
   483  	tst.pullImage(ubuntuImg())
   484  	tst.runPodSandbox(sandboxes[1])
   485  	containerId2 := tst.createContainer(sandboxes[1], containers[1], ubuntuImg(), nil)
   486  	tst.listPodSandbox(nil)
   487  	tst.listContainers(nil)
   488  	tst.containerStatus(containerId2)
   489  	tst.startContainer(containerId2)
   490  	tst.containerStatus(containerId2)
   491  
   492  	tst.stopContainer(containerId1)
   493  	tst.stopContainer(containerId2)
   494  	// this should not cause an error
   495  	tst.stopContainer(containerId2)
   496  
   497  	tst.listContainers(nil)
   498  	tst.containerStatus(containerId1)
   499  
   500  	tst.removeContainer(containerId1)
   501  	tst.removeContainer(containerId2)
   502  	// this should not cause an error
   503  	tst.removeContainer(containerId2)
   504  
   505  	tst.stopPodSandox(sandboxes[0].Metadata.Uid)
   506  	tst.stopPodSandox(sandboxes[1].Metadata.Uid)
   507  	// this should not cause an error
   508  	tst.stopPodSandox(sandboxes[1].Metadata.Uid)
   509  
   510  	tst.listPodSandbox(nil)
   511  	tst.podSandboxStatus(sandboxes[0].Metadata.Uid)
   512  
   513  	tst.removePodSandox(sandboxes[0].Metadata.Uid)
   514  	tst.removePodSandox(sandboxes[1].Metadata.Uid)
   515  	// this should not cause an error
   516  	tst.removePodSandox(sandboxes[1].Metadata.Uid)
   517  
   518  	tst.listPodSandbox(nil)
   519  	tst.listContainers(nil)
   520  
   521  	tst.verify()
   522  }
   523  
   524  func TestRunPodSandboxWithFailingCNI(t *testing.T) {
   525  	tst := makeVirtletCRITester(t)
   526  	defer tst.teardown()
   527  
   528  	sandboxes := criapi.GetSandboxes(1)
   529  	sandboxes[0].Metadata.Uid = "should-fail-cni"
   530  	tst.runPodSandboxAndExpectError(sandboxes[0])
   531  	tst.verify()
   532  }
   533  
   534  func TestCRIMounts(t *testing.T) {
   535  	tst := makeVirtletCRITester(t)
   536  	defer tst.teardown()
   537  
   538  	sandboxes := criapi.GetSandboxes(1)
   539  	containers := criapi.GetContainersConfig(sandboxes)
   540  
   541  	tst.pullImage(cirrosImg())
   542  	tst.runPodSandbox(sandboxes[0])
   543  	tst.podSandboxStatus(sandboxes[0].Metadata.Uid)
   544  
   545  	mounts := tst.getSampleFlexvolMounts(sandboxes[0].Metadata.Uid)
   546  	containerId1 := tst.createContainer(sandboxes[0], containers[0], cirrosImg(), mounts)
   547  	tst.containerStatus(containerId1)
   548  	tst.startContainer(containerId1)
   549  	tst.stopContainer(containerId1)
   550  	tst.removeContainer(containerId1)
   551  	tst.stopPodSandox(sandboxes[0].Metadata.Uid)
   552  	tst.removePodSandox(sandboxes[0].Metadata.Uid)
   553  	tst.verify()
   554  }
   555  
   556  func TestCRIPodFilters(t *testing.T) {
   557  	tst := makeVirtletCRITester(t)
   558  	tst.rec.AddFilter("ListPodSandbox")
   559  	defer tst.teardown()
   560  
   561  	sandboxes := criapi.GetSandboxes(2)
   562  	sandboxes[1].Labels["foo"] = "bar2"
   563  	tst.runPodSandbox(sandboxes[0])
   564  	tst.runPodSandbox(sandboxes[1])
   565  
   566  	tst.listPodSandbox(nil)
   567  	tst.listPodSandbox(&kubeapi.PodSandboxFilter{Id: sandboxes[0].Metadata.Uid})
   568  	tst.listPodSandbox(&kubeapi.PodSandboxFilter{
   569  		State: &kubeapi.PodSandboxStateValue{
   570  			State: kubeapi.PodSandboxState_SANDBOX_READY,
   571  		},
   572  	})
   573  	tst.listPodSandbox(&kubeapi.PodSandboxFilter{
   574  		State: &kubeapi.PodSandboxStateValue{
   575  			State: kubeapi.PodSandboxState_SANDBOX_NOTREADY,
   576  		},
   577  	})
   578  	tst.listPodSandbox(&kubeapi.PodSandboxFilter{
   579  		LabelSelector: map[string]string{
   580  			"foo": "bar2",
   581  		},
   582  	})
   583  
   584  	tst.stopPodSandox(sandboxes[1].Metadata.Uid)
   585  	tst.listPodSandbox(&kubeapi.PodSandboxFilter{
   586  		State: &kubeapi.PodSandboxStateValue{
   587  			State: kubeapi.PodSandboxState_SANDBOX_READY,
   588  		},
   589  	})
   590  	tst.listPodSandbox(&kubeapi.PodSandboxFilter{
   591  		State: &kubeapi.PodSandboxStateValue{
   592  			State: kubeapi.PodSandboxState_SANDBOX_NOTREADY,
   593  		},
   594  	})
   595  
   596  	tst.verify()
   597  }
   598  
   599  func TestCRIContainerFilters(t *testing.T) {
   600  	tst := makeVirtletCRITester(t)
   601  	tst.rec.AddFilter("ListContainers")
   602  	defer tst.teardown()
   603  
   604  	sandboxes := criapi.GetSandboxes(2)
   605  	containers := criapi.GetContainersConfig(sandboxes)
   606  	tst.pullImage(cirrosImg())
   607  	tst.runPodSandbox(sandboxes[0])
   608  	containerId1 := tst.createContainer(sandboxes[0], containers[0], cirrosImg(), nil)
   609  	tst.startContainer(containerId1)
   610  	tst.pullImage(ubuntuImg())
   611  	tst.runPodSandbox(sandboxes[1])
   612  	containerId2 := tst.createContainer(sandboxes[1], containers[1], ubuntuImg(), nil)
   613  	tst.startContainer(containerId2)
   614  
   615  	tst.listContainers(nil)
   616  	tst.listContainers(&kubeapi.ContainerFilter{Id: containerId1})
   617  	tst.listContainers(&kubeapi.ContainerFilter{
   618  		State: &kubeapi.ContainerStateValue{
   619  			State: kubeapi.ContainerState_CONTAINER_RUNNING,
   620  		},
   621  	})
   622  	tst.listContainers(&kubeapi.ContainerFilter{
   623  		State: &kubeapi.ContainerStateValue{
   624  			State: kubeapi.ContainerState_CONTAINER_EXITED,
   625  		},
   626  	})
   627  	tst.listContainers(&kubeapi.ContainerFilter{
   628  		LabelSelector: map[string]string{
   629  			"io.kubernetes.pod.name": "testName_1",
   630  		},
   631  	})
   632  	tst.listContainers(&kubeapi.ContainerFilter{
   633  		PodSandboxId: sandboxes[0].Metadata.Uid,
   634  	})
   635  
   636  	tst.stopContainer(containerId1)
   637  	tst.listContainers(&kubeapi.ContainerFilter{
   638  		State: &kubeapi.ContainerStateValue{
   639  			State: kubeapi.ContainerState_CONTAINER_RUNNING,
   640  		},
   641  	})
   642  	tst.listContainers(&kubeapi.ContainerFilter{
   643  		State: &kubeapi.ContainerStateValue{
   644  			State: kubeapi.ContainerState_CONTAINER_EXITED,
   645  		},
   646  	})
   647  
   648  	tst.verify()
   649  }
   650  
   651  func TestContainerListStats(t *testing.T) {
   652  	tst := makeVirtletCRITester(t)
   653  	tst.rec.AddFilter("ListContainers")
   654  	tst.rec.AddFilter("ListContainerStats")
   655  	defer tst.teardown()
   656  
   657  	sandboxes := criapi.GetSandboxes(2)
   658  	containers := criapi.GetContainersConfig(sandboxes)
   659  	tst.pullImage(cirrosImg())
   660  	tst.runPodSandbox(sandboxes[0])
   661  	containerId1 := tst.createContainer(sandboxes[0], containers[0], cirrosImg(), nil)
   662  	tst.startContainer(containerId1)
   663  	tst.pullImage(ubuntuImg())
   664  	tst.runPodSandbox(sandboxes[1])
   665  	containerId2 := tst.createContainer(sandboxes[1], containers[1], ubuntuImg(), nil)
   666  	tst.startContainer(containerId2)
   667  
   668  	tst.listContainerStats(nil)
   669  	tst.listContainerStats(&kubeapi.ContainerStatsFilter{Id: containerId1})
   670  	tst.listContainerStats(&kubeapi.ContainerStatsFilter{
   671  		LabelSelector: map[string]string{
   672  			"io.kubernetes.pod.name": "testName_1",
   673  		},
   674  	})
   675  	tst.listContainerStats(&kubeapi.ContainerStatsFilter{
   676  		PodSandboxId: sandboxes[0].Metadata.Uid,
   677  	})
   678  
   679  	tst.verify()
   680  }
   681  
   682  func TestCRIAttachPortForward(t *testing.T) {
   683  	tst := makeVirtletCRITester(t)
   684  	tst.rec.AddFilter("Attach")
   685  	tst.rec.AddFilter("PortForward")
   686  	defer tst.teardown()
   687  
   688  	sandboxes := criapi.GetSandboxes(1)
   689  	containers := criapi.GetContainersConfig(sandboxes)
   690  
   691  	tst.pullImage(cirrosImg())
   692  	tst.runPodSandbox(sandboxes[0])
   693  	tst.podSandboxStatus(sandboxes[0].Metadata.Uid)
   694  
   695  	containerId1 := tst.createContainer(sandboxes[0], containers[0], cirrosImg(), nil)
   696  	tst.containerStatus(containerId1)
   697  	tst.startContainer(containerId1)
   698  
   699  	tst.attach(&kubeapi.AttachRequest{
   700  		ContainerId: containerId1,
   701  		Stdin:       true,
   702  		Stdout:      true,
   703  		Stderr:      true,
   704  	})
   705  	tst.attach(&kubeapi.AttachRequest{
   706  		ContainerId: containerId1,
   707  		Stdin:       true,
   708  	})
   709  	tst.portForward(&kubeapi.PortForwardRequest{
   710  		PodSandboxId: sandboxes[0].Metadata.Uid,
   711  		Port:         []int32{42000},
   712  	})
   713  
   714  	tst.verify()
   715  }
   716  
   717  func TestUpdateResources(t *testing.T) {
   718  	tst := makeVirtletCRITester(t)
   719  	tst.rec.AddFilter("UpdateContainerResources")
   720  	tst.rec.AddFilter("DefineDomain")
   721  	tst.rec.AddFilter("Undefine")
   722  	defer tst.teardown()
   723  
   724  	sandboxes := criapi.GetSandboxes(1)
   725  	containers := criapi.GetContainersConfig(sandboxes)
   726  
   727  	tst.pullImage(cirrosImg())
   728  	tst.runPodSandbox(sandboxes[0])
   729  	tst.podSandboxStatus(sandboxes[0].Metadata.Uid)
   730  
   731  	containerId1 := tst.createContainer(sandboxes[0], containers[0], cirrosImg(), nil)
   732  
   733  	tst.updateContainerResources(containerId1, "42")
   734  
   735  	tst.verify()
   736  }
   737  
   738  // TODO: make sure non-default Linux namespace settings cause pod startup to fail.