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 }