fix(metis): recognize scratch-backed bind mounts
This commit is contained in:
parent
fd3ff8380c
commit
77d34e4f1d
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"metis/pkg/facts"
|
"metis/pkg/facts"
|
||||||
@ -182,8 +183,60 @@ func bindHealthy(target, source string) bool {
|
|||||||
if target == "" || source == "" {
|
if target == "" || source == "" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
targetReal := realPath(target)
|
||||||
|
sourceReal := realPath(source)
|
||||||
|
if sourceReal != "" && pathWithin(targetReal, sourceReal) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
mountSource, _, mounted := mountInfo(target)
|
mountSource, _, mounted := mountInfo(target)
|
||||||
return mounted && strings.TrimSpace(mountSource) == source
|
if !mounted {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(mountSource) == source {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
rootSource, _, rootMounted := mountInfo(source)
|
||||||
|
if !rootMounted {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
device, subpath := splitFindmntSource(mountSource)
|
||||||
|
return device != "" && device == rootSource && subpath == filepath.Clean(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
func realPath(path string) string {
|
||||||
|
path = strings.TrimSpace(path)
|
||||||
|
if path == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
out, err := commandOutput("readlink", "-f", path)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(string(out))
|
||||||
|
}
|
||||||
|
|
||||||
|
func pathWithin(path, root string) bool {
|
||||||
|
path = filepath.Clean(strings.TrimSpace(path))
|
||||||
|
root = filepath.Clean(strings.TrimSpace(root))
|
||||||
|
if path == "" || root == "" || path == "." || root == "." {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return path == root || strings.HasPrefix(path, root+string(os.PathSeparator))
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitFindmntSource(source string) (string, string) {
|
||||||
|
source = strings.TrimSpace(source)
|
||||||
|
if source == "" {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
start := strings.Index(source, "[")
|
||||||
|
if start == -1 || !strings.HasSuffix(source, "]") {
|
||||||
|
return source, ""
|
||||||
|
}
|
||||||
|
return source[:start], source[start+1 : len(source)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveDeviceByUUID(uuid string) string {
|
func resolveDeviceByUUID(uuid string) string {
|
||||||
|
|||||||
@ -120,6 +120,82 @@ esac`,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBindHealthyAcceptsScratchSymlinksAndDeviceSubpaths(t *testing.T) {
|
||||||
|
dir := fakeCollectorCommands(t, map[string]string{
|
||||||
|
"readlink": `case "${2:-}" in
|
||||||
|
/mnt/scratch) printf '/mnt/scratch\n' ;;
|
||||||
|
/var/log/pods) printf '/mnt/scratch/var/log/pods\n' ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac`,
|
||||||
|
"findmnt": `target=""
|
||||||
|
for ((i=1; i<=$#; i++)); do
|
||||||
|
if [[ "${!i}" == "-T" ]]; then
|
||||||
|
j=$((i + 1))
|
||||||
|
target="${!j}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
case "${target}" in
|
||||||
|
/mnt/scratch) printf 'SOURCE="/dev/sdz1" TARGET="/mnt/scratch" FSTYPE="ext4"\n' ;;
|
||||||
|
/var/tmp) printf 'SOURCE="/dev/sdz1[/var/tmp]" TARGET="/var/tmp" FSTYPE="ext4"\n' ;;
|
||||||
|
/other) printf 'SOURCE="/dev/sdz1[/unexpected]" TARGET="/other" FSTYPE="ext4"\n' ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac`,
|
||||||
|
})
|
||||||
|
t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
|
||||||
|
|
||||||
|
if !bindHealthy("/var/log/pods", "/mnt/scratch") {
|
||||||
|
t.Fatal("scratch-backed symlink target should be healthy")
|
||||||
|
}
|
||||||
|
if !bindHealthy("/var/tmp", "/mnt/scratch") {
|
||||||
|
t.Fatal("device subpath bind target should be healthy")
|
||||||
|
}
|
||||||
|
if bindHealthy("/other", "/mnt/scratch") {
|
||||||
|
t.Fatal("unrelated device subpath should be unhealthy")
|
||||||
|
}
|
||||||
|
if device, subpath := splitFindmntSource(`/dev/sdz1[/var/tmp]`); device != "/dev/sdz1" || subpath != "/var/tmp" {
|
||||||
|
t.Fatalf("splitFindmntSource device/subpath = %q %q", device, subpath)
|
||||||
|
}
|
||||||
|
if device, subpath := splitFindmntSource("/dev/sdz1"); device != "/dev/sdz1" || subpath != "" {
|
||||||
|
t.Fatalf("plain splitFindmntSource device/subpath = %q %q", device, subpath)
|
||||||
|
}
|
||||||
|
if device, subpath := splitFindmntSource(""); device != "" || subpath != "" {
|
||||||
|
t.Fatalf("empty splitFindmntSource device/subpath = %q %q", device, subpath)
|
||||||
|
}
|
||||||
|
if got := realPath(""); got != "" {
|
||||||
|
t.Fatalf("empty realPath = %q", got)
|
||||||
|
}
|
||||||
|
if got := realPath("/missing"); got != "" {
|
||||||
|
t.Fatalf("missing realPath = %q", got)
|
||||||
|
}
|
||||||
|
if pathWithin("", "/mnt/scratch") || pathWithin("/mnt/scratch/file", "") {
|
||||||
|
t.Fatal("empty pathWithin inputs should be false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindHealthyRejectsDeviceSubpathWithoutRootMount(t *testing.T) {
|
||||||
|
dir := fakeCollectorCommands(t, map[string]string{
|
||||||
|
"readlink": `exit 1`,
|
||||||
|
"findmnt": `target=""
|
||||||
|
for ((i=1; i<=$#; i++)); do
|
||||||
|
if [[ "${!i}" == "-T" ]]; then
|
||||||
|
j=$((i + 1))
|
||||||
|
target="${!j}"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
case "${target}" in
|
||||||
|
/var/tmp) printf 'SOURCE="/dev/sdz1[/var/tmp]" TARGET="/var/tmp" FSTYPE="ext4"\n' ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac`,
|
||||||
|
})
|
||||||
|
t.Setenv("PATH", dir+string(os.PathListSeparator)+os.Getenv("PATH"))
|
||||||
|
|
||||||
|
if bindHealthy("/var/tmp", "/mnt/scratch") {
|
||||||
|
t.Fatal("device subpath should be unhealthy when root scratch mount is unknown")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCollectUSBScratchDefaultsAndParserEdges(t *testing.T) {
|
func TestCollectUSBScratchDefaultsAndParserEdges(t *testing.T) {
|
||||||
dir := fakeCollectorCommands(t, map[string]string{
|
dir := fakeCollectorCommands(t, map[string]string{
|
||||||
"cat": `printf '%s\n' '{"usb_scratch":{"mountpoint":"/mnt/scratch"}}'`,
|
"cat": `printf '%s\n' '{"usb_scratch":{"mountpoint":"/mnt/scratch"}}'`,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user