github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/osutil/disks/mockdisk.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2020 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package disks 21 22 import ( 23 "fmt" 24 25 "github.com/snapcore/snapd/osutil" 26 ) 27 28 // MockDiskMapping is an implementation of Disk for mocking purposes, it is 29 // exported so that other packages can easily mock a specific disk layout 30 // without needing to mock the mount setup, sysfs, or udev commands just to test 31 // high level logic. 32 // DevNum must be a unique string per unique mocked disk, if only one disk is 33 // being mocked it can be left empty. 34 type MockDiskMapping struct { 35 FilesystemLabelToPartUUID map[string]string 36 DiskHasPartitions bool 37 DevNum string 38 } 39 40 // FindMatchingPartitionUUID returns a matching PartitionUUID for the specified 41 // label if it exists. Part of the Disk interface. 42 func (d *MockDiskMapping) FindMatchingPartitionUUID(label string) (string, error) { 43 osutil.MustBeTestBinary("mock disks only to be used in tests") 44 if partuuid, ok := d.FilesystemLabelToPartUUID[label]; ok { 45 return partuuid, nil 46 } 47 return "", FilesystemLabelNotFoundError{Label: label} 48 } 49 50 // HasPartitions returns if the mock disk has partitions or not. Part of the 51 // Disk interface. 52 func (d *MockDiskMapping) HasPartitions() bool { 53 return d.DiskHasPartitions 54 } 55 56 // MountPointIsFromDisk returns if the disk that the specified mount point comes 57 // from is the same disk as the object. Part of the Disk interface. 58 func (d *MockDiskMapping) MountPointIsFromDisk(mountpoint string, opts *Options) (bool, error) { 59 osutil.MustBeTestBinary("mock disks only to be used in tests") 60 61 // this is relying on the fact that DiskFromMountPoint should have been 62 // mocked for us to be using this mockDisk method anyways 63 otherDisk, err := DiskFromMountPoint(mountpoint, opts) 64 if err != nil { 65 return false, err 66 } 67 68 if otherDisk.Dev() == d.Dev() && otherDisk.HasPartitions() == d.HasPartitions() { 69 return true, nil 70 } 71 72 return false, nil 73 } 74 75 // Dev returns a unique representation of the mock disk that is suitable for 76 // comparing two mock disks to see if they are the same. Part of the Disk 77 // interface. 78 func (d *MockDiskMapping) Dev() string { 79 return d.DevNum 80 } 81 82 // Mountpoint is a combination of a mountpoint location and whether that 83 // mountpoint is a decrypted device. It is only used in identifying mount points 84 // with MountPointIsFromDisk and DiskFromMountPoint with 85 // MockMountPointDisksToPartitionMapping. 86 type Mountpoint struct { 87 Mountpoint string 88 IsDecryptedDevice bool 89 } 90 91 // MockMountPointDisksToPartitionMapping will mock DiskFromMountPoint such that 92 // the specified mapping is returned/used. Specifically, keys in the provided 93 // map are mountpoints, and the values for those keys are the disks that will 94 // be returned from DiskFromMountPoint or used internally in 95 // MountPointIsFromDisk. 96 func MockMountPointDisksToPartitionMapping(mockedMountPoints map[Mountpoint]*MockDiskMapping) (restore func()) { 97 osutil.MustBeTestBinary("mock disks only to be used in tests") 98 99 // verify that all unique MockDiskMapping's have unique DevNum's 100 alreadySeen := make(map[string]*MockDiskMapping, len(mockedMountPoints)) 101 for _, mockDisk := range mockedMountPoints { 102 if old, ok := alreadySeen[mockDisk.DevNum]; ok { 103 if mockDisk != old { 104 // we already saw a disk with this DevNum as a different pointer 105 // so just assume it's different 106 msg := fmt.Sprintf("mocked disks %+v and %+v have the same DevNum (%s) but are not the same object", old, mockDisk, mockDisk.DevNum) 107 panic(msg) 108 } 109 // otherwise same ptr, no point in comparing them 110 } else { 111 // didn't see it before, save it now 112 alreadySeen[mockDisk.DevNum] = mockDisk 113 } 114 } 115 116 old := diskFromMountPoint 117 118 diskFromMountPoint = func(mountpoint string, opts *Options) (Disk, error) { 119 if opts == nil { 120 opts = &Options{} 121 } 122 m := Mountpoint{mountpoint, opts.IsDecryptedDevice} 123 if mockedDisk, ok := mockedMountPoints[m]; ok { 124 return mockedDisk, nil 125 } 126 return nil, fmt.Errorf("mountpoint %s not mocked", mountpoint) 127 } 128 return func() { 129 diskFromMountPoint = old 130 } 131 }