github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/pkg/libvirttools/gc_test.go (about)

     1  /*
     2  Copyright 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 libvirttools
    18  
    19  import (
    20  	"crypto/sha256"
    21  	"io/ioutil"
    22  	"os"
    23  	"path/filepath"
    24  	"testing"
    25  
    26  	libvirtxml "github.com/libvirt/libvirt-go-xml"
    27  
    28  	blockdev "github.com/Mirantis/virtlet/pkg/blockdev"
    29  	fakeblockdev "github.com/Mirantis/virtlet/pkg/blockdev/fake"
    30  	fakeutils "github.com/Mirantis/virtlet/pkg/utils/fake"
    31  	testutils "github.com/Mirantis/virtlet/pkg/utils/testing"
    32  	"github.com/Mirantis/virtlet/tests/gm"
    33  )
    34  
    35  var (
    36  	testUUIDs = [...]string{
    37  		"5edfe2ad-9852-439b-bbfb-3fe8b7c72906",
    38  		"8a6163c3-e4ee-488f-836a-d2abe92d0744",
    39  		"13f51f8d-0f4e-4538-9db0-413380ff9c84",
    40  	}
    41  )
    42  
    43  func TestDomainCleanup(t *testing.T) {
    44  	ct := newContainerTester(t, testutils.NewToplevelRecorder(), nil, nil)
    45  	defer ct.teardown()
    46  
    47  	for _, uuid := range testUUIDs {
    48  		if _, err := ct.domainConn.DefineDomain(&libvirtxml.Domain{
    49  			Name: "virtlet-" + uuid[:13] + "-container1",
    50  			UUID: uuid,
    51  		}); err != nil {
    52  			t.Fatalf("Cannot define the fake domain: %v", err)
    53  		}
    54  	}
    55  	if _, err := ct.domainConn.DefineDomain(&libvirtxml.Domain{
    56  		Name: "other-than-virtlet-domain",
    57  		UUID: "12fdc902-3345-4d8e-a3f1-11a091e59455",
    58  	}); err != nil {
    59  		t.Fatalf("Cannot define new fake domain: %v", err)
    60  	}
    61  
    62  	if domains, _ := ct.domainConn.ListDomains(); len(domains) != 4 {
    63  		t.Errorf("Defined 4 domains in fake libvirt but ListDomains() returned %d of them", len(domains))
    64  	}
    65  
    66  	// this should remove all domains (including other than virlet defined)
    67  	// with an exception of the last listed in testUUIDs slice
    68  	if errors := ct.virtTool.removeOrphanDomains(testUUIDs[2:]); len(errors) != 0 {
    69  		t.Errorf("removeOrphanDomains returned errors: %v", errors)
    70  	}
    71  
    72  	if domains, _ := ct.domainConn.ListDomains(); len(domains) != 1 {
    73  		t.Errorf("Expected a single remaining domain, ListDomains() returned %d of them", len(domains))
    74  	}
    75  
    76  	gm.Verify(t, gm.NewYamlVerifier(ct.rec.Content()))
    77  }
    78  
    79  func TestRootVolumesCleanup(t *testing.T) {
    80  	ct := newContainerTester(t, testutils.NewToplevelRecorder(), nil, nil)
    81  	defer ct.teardown()
    82  
    83  	pool, err := ct.virtTool.StoragePool()
    84  	if err != nil {
    85  		t.Fatalf("StoragePool(): %v", err)
    86  	}
    87  
    88  	for _, uuid := range testUUIDs {
    89  		if _, err := pool.CreateStorageVol(&libvirtxml.StorageVolume{
    90  			Name:   "root for " + uuid,
    91  			Target: &libvirtxml.StorageVolumeTarget{Path: "/some/path/virtlet_root_" + uuid},
    92  		}); err != nil {
    93  			t.Fatalf("Cannot define new fake volume: %v", err)
    94  		}
    95  	}
    96  	if _, err := pool.CreateStorageVol(&libvirtxml.StorageVolume{
    97  		Name:   "some other volume",
    98  		Target: &libvirtxml.StorageVolumeTarget{Path: "/path/with/different/prefix"},
    99  	}); err != nil {
   100  		t.Fatalf("Cannot define new fake volume: %v", err)
   101  	}
   102  
   103  	if volumes, _ := pool.ListVolumes(); len(volumes) != 4 {
   104  		t.Errorf("Defined 4 fake volumes but ListVolumes() returned %d of them", len(volumes))
   105  	}
   106  
   107  	// this should remove only root volumes corresponding to the two first
   108  	// elements of testUUIDs slice, keeping others
   109  	if errors := ct.virtTool.removeOrphanRootVolumes(testUUIDs[2:]); len(errors) != 0 {
   110  		t.Errorf("removeOrphanRootVolumes returned errors: %v", errors)
   111  	}
   112  
   113  	if volumes, _ := pool.ListVolumes(); len(volumes) != 2 {
   114  		t.Errorf("Expected 2 volumes to remain, but ListVolumes() returned %d of them", len(volumes))
   115  	}
   116  
   117  	gm.Verify(t, gm.NewYamlVerifier(ct.rec.Content()))
   118  }
   119  
   120  func TestQcow2VolumesCleanup(t *testing.T) {
   121  	ct := newContainerTester(t, testutils.NewToplevelRecorder(), nil, nil)
   122  	defer ct.teardown()
   123  
   124  	pool, err := ct.virtTool.StoragePool()
   125  	if err != nil {
   126  		t.Fatalf("StoragePool(): %v", err)
   127  	}
   128  
   129  	for _, uuid := range testUUIDs {
   130  		if _, err := pool.CreateStorageVol(&libvirtxml.StorageVolume{
   131  			Name:   "qcow flexvolume for " + uuid,
   132  			Target: &libvirtxml.StorageVolumeTarget{Path: "/some/path/virtlet-" + uuid},
   133  		}); err != nil {
   134  			t.Fatalf("Cannot define new fake volume: %v", err)
   135  		}
   136  	}
   137  	if _, err := pool.CreateStorageVol(&libvirtxml.StorageVolume{
   138  		Name:   "some other volume",
   139  		Target: &libvirtxml.StorageVolumeTarget{Path: "/path/with/different/prefix"},
   140  	}); err != nil {
   141  		t.Fatalf("Cannot define new fake volume: %v", err)
   142  	}
   143  
   144  	if volumes, _ := pool.ListVolumes(); len(volumes) != 4 {
   145  		t.Errorf("Defined 4 fake volumes but ListVolumes() returned %d of them", len(volumes))
   146  	}
   147  
   148  	// this should remove only ephemeral qcow2 volumes corresponding to
   149  	// the two first elements of testUUIDs slice, keeping others
   150  	if errors := ct.virtTool.removeOrphanQcow2Volumes(testUUIDs[2:]); len(errors) != 0 {
   151  		t.Errorf("removeOrphanRootVolumes returned errors: %v", errors)
   152  	}
   153  
   154  	if volumes, _ := pool.ListVolumes(); len(volumes) != 2 {
   155  		t.Errorf("Expected two remaining volumes, ListVolumes() returned %d of them", len(volumes))
   156  	}
   157  
   158  	gm.Verify(t, gm.NewYamlVerifier(ct.rec.Content()))
   159  }
   160  
   161  func TestConfigISOsCleanup(t *testing.T) {
   162  	ct := newContainerTester(t, testutils.NewToplevelRecorder(), nil, nil)
   163  	defer ct.teardown()
   164  
   165  	directory, err := ioutil.TempDir("", "virtlet-tests-")
   166  	if err != nil {
   167  		t.Fatalf("TempDir() returned: %v", err)
   168  	}
   169  	defer os.RemoveAll(directory)
   170  
   171  	for _, uuid := range testUUIDs {
   172  		fname := filepath.Join(directory, "config-"+uuid+".iso")
   173  		if file, err := os.Create(fname); err != nil {
   174  			t.Fatalf("Cannot create fake iso with name %q: %v", fname, err)
   175  		} else {
   176  			file.Close()
   177  		}
   178  	}
   179  	fname := filepath.Join(directory, "some other.iso")
   180  	if file, err := os.Create(fname); err != nil {
   181  		t.Fatalf("Cannot create fake iso with name %q: %v", fname, err)
   182  	} else {
   183  		file.Close()
   184  	}
   185  
   186  	preCallFileNames, err := filepath.Glob(filepath.Join(directory, "*"))
   187  	if err != nil {
   188  		t.Fatalf("Error globbing names in temporary directory: %v", err)
   189  	}
   190  	if len(preCallFileNames) != 4 {
   191  		t.Fatalf("Expected 4 files in temporary directory, found: %d", len(preCallFileNames))
   192  	}
   193  
   194  	// this should remove only config iso file corresponding to the first
   195  	// element of testUUIDs slice, keeping other files
   196  	if errors := ct.virtTool.removeOrphanConfigImages(testUUIDs[1:], directory); len(errors) != 0 {
   197  		t.Errorf("removeOrphanConfigImages returned errors: %v", errors)
   198  	}
   199  
   200  	postCallFileNames, err := filepath.Glob(filepath.Join(directory, "*"))
   201  	if err != nil {
   202  		t.Fatalf("Error globbing names in the temporary directory: %v", err)
   203  	}
   204  
   205  	diff := difference(preCallFileNames, postCallFileNames)
   206  	if len(diff) != 1 {
   207  		t.Fatalf("Expected removeOrphanConfigImages to remove single file, but it removed %d files", len(diff))
   208  	}
   209  
   210  	expectedPath := filepath.Join(directory, "config-"+testUUIDs[0]+".iso")
   211  	if diff[0] != expectedPath {
   212  		t.Fatalf("Expected removeOrphanConfigImages to remove only %q file, but it also removed: %q", expectedPath, diff[0])
   213  	}
   214  
   215  	// no gm validation, because we are testing only file operations in this test
   216  }
   217  
   218  func TestDeviceMapperCleanup(t *testing.T) {
   219  	fakeblockdev.WithFakeRootDevsAndSysfs(t, func(devPaths []string, table, devDir, sysfsDir string) {
   220  		dmRemoveCmd := "dmsetup remove virtlet-dm-9a322047-1f0d-4395-8e43-6e1b310ce6f3"
   221  		ct := newContainerTester(t, testutils.NewToplevelRecorder(), []fakeutils.CmdSpec{
   222  			{
   223  				Match:  "^dmsetup table$",
   224  				Stdout: table,
   225  			},
   226  			{
   227  				Match: "^" + dmRemoveCmd + "$",
   228  			},
   229  		}, nil)
   230  		defer ct.teardown()
   231  
   232  		ldh := blockdev.NewLogicalDeviceHandler(ct.virtTool.commander, devDir, sysfsDir)
   233  		for _, devPath := range devPaths {
   234  			if _, err := ldh.EnsureDevHeaderMatches(devPath, sha256.Sum256([]byte("foobar"))); err != nil {
   235  				t.Fatalf("EnsureDevHeaderMatches(): %v", err)
   236  			}
   237  		}
   238  
   239  		for _, uuid := range testUUIDs {
   240  			if _, err := ct.domainConn.DefineDomain(&libvirtxml.Domain{
   241  				Name: "virtlet-" + uuid[:13] + "-container1",
   242  				UUID: uuid,
   243  			}); err != nil {
   244  				t.Fatalf("Cannot define the fake domain: %v", err)
   245  			}
   246  		}
   247  
   248  		if errors := ct.virtTool.removeOrphanVirtualBlockDevices(testUUIDs[:], devDir, sysfsDir); len(errors) != 0 {
   249  			t.Errorf("removeOrphanDomains returned errors: %v", errors)
   250  		}
   251  
   252  		n := 0
   253  		for _, r := range ct.rec.Content() {
   254  			if r.Name == "CMD" && r.Value.(map[string]string)["cmd"] == dmRemoveCmd {
   255  				n++
   256  			}
   257  		}
   258  		if n != 1 {
   259  			t.Errorf("dmsetup remove for the orphaned volume is expected to be called exactly 1 time, but was called %d times", n)
   260  		}
   261  	})
   262  	// no gm validation b/c we just verify 'dmsetup remove' command above
   263  }
   264  
   265  // https://stackoverflow.com/a/45428032
   266  // difference returns the elements in a that aren't in b
   267  func difference(a, b []string) []string {
   268  	mb := map[string]bool{}
   269  	for _, x := range b {
   270  		mb[x] = true
   271  	}
   272  	ab := []string{}
   273  	for _, x := range a {
   274  		if _, ok := mb[x]; !ok {
   275  			ab = append(ab, x)
   276  		}
   277  	}
   278  	return ab
   279  }