github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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  }