-
Notifications
You must be signed in to change notification settings - Fork 147
/
inspector.go
121 lines (106 loc) · 4.44 KB
/
inspector.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// Copyright 2021 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package mount
import (
"fmt"
"strings"
"github.com/GoogleCloudPlatform/compute-image-tools/cli_tools/common/utils/shell"
)
// To rebuild mocks, run `go generate ./...`
//go:generate go run github.com/golang/mock/mockgen -package mount -source $GOFILE -destination mock_mount_inspector.go
// Inspector inspects a mount directory to return:
// - The underlying block device.
// - The block device's type.
// - If the block device is virtual, the number of block devices
// that comprise it.
type Inspector interface {
Inspect(dir string) (InspectionResults, error)
}
// InspectionResults contains information about the mountpoint of a directory.
type InspectionResults struct {
BlockDevicePath string
BlockDeviceIsVirtual bool
UnderlyingBlockDevices []string
}
// NewMountInspector returns a new inspector that uses command-line utilities.
func NewMountInspector() Inspector {
return &defaultMountInspector{shell.NewShellExecutor()}
}
type defaultMountInspector struct {
shellExecutor shell.Executor
}
// Inspect returns the mount information for dir.
func (mi *defaultMountInspector) Inspect(dir string) (mountInfo InspectionResults, err error) {
if mountInfo.BlockDevicePath, err = mi.getDeviceForMount(dir); err != nil {
return InspectionResults{}, fmt.Errorf("unable to find mount information for `%s`: %w", dir, err)
}
if mountInfo.BlockDeviceIsVirtual, err = mi.isDeviceVirtual(mountInfo.BlockDevicePath); err != nil {
return InspectionResults{}, fmt.Errorf("unable to find the type of device `%s`: %w", mountInfo.BlockDevicePath, err)
}
if mountInfo.UnderlyingBlockDevices, err = mi.getPhysicalDisks(mountInfo.BlockDevicePath); err != nil {
return InspectionResults{}, fmt.Errorf("unable to find the physical disks for the block device `%s`: %w",
mountInfo.BlockDevicePath, err)
}
return mountInfo, nil
}
// getDeviceForMount returns the path of the block device that is mounted for dir.
func (mi *defaultMountInspector) getDeviceForMount(dir string) (string, error) {
stdout, err := mi.shellExecutor.Exec("findmnt", "--noheadings", "--output=SOURCE", dir)
if err != nil {
return "", err
}
return strings.TrimSpace(stdout), nil
}
// isDeviceVirtual returns whether the block device is LVM
func (mi *defaultMountInspector) isDeviceVirtual(device string) (bool, error) {
stdout, err := mi.shellExecutor.Exec("lsblk", "--noheadings", "--output=TYPE", device)
return strings.TrimSpace(strings.ToLower(stdout)) == "lvm", err
}
// getPhysicalDisks returns the list of physical disks that are used for the blockDevice.
// For example:
// 1. blockDevice is an MBR-style partition:
// blockDevice: /dev/sdb1
// getPhysicalDisks: [/dev/sdb]
// 2. blockDevice is an LVM logical volume that is spread across three disks:
// blockDevice: /dev/mapper/vg-lv
// getPhysicalDisks: [/dev/sda, /dev/sdb, /dev/sdc]
func (mi *defaultMountInspector) getPhysicalDisks(blockDevice string) (disksForDevice []string, err error) {
disks, err := mi.getAllPhysicalDisks()
if err != nil {
return nil, err
}
for _, disk := range disks {
blkDevices, err := mi.blockDevicesOnDisk(disk)
if err != nil {
return nil, err
}
for _, blkDevice := range blkDevices {
if blkDevice == blockDevice {
disksForDevice = append(disksForDevice, disk)
break
}
}
}
return disksForDevice, nil
}
// getAllPhysicalDisks returns the paths of all physical disks on the system.
func (mi *defaultMountInspector) getAllPhysicalDisks() (allDisks []string, err error) {
return mi.shellExecutor.ExecLines(
"lsblk", "--noheadings", "--paths", "--list", "--nodeps", "--output=NAME")
}
// blockDevicesOnDisk returns the paths of all block devices contained on a disk.
func (mi *defaultMountInspector) blockDevicesOnDisk(disk string) ([]string, error) {
return mi.shellExecutor.ExecLines(
"lsblk", "--noheadings", "--paths", "--list", "--output=NAME", disk)
}