diff --git a/pkg/cluster/manager/deploy_volume_server.go b/pkg/cluster/manager/deploy_volume_server.go index d2353c9..2018795 100644 --- a/pkg/cluster/manager/deploy_volume_server.go +++ b/pkg/cluster/manager/deploy_volume_server.go @@ -3,11 +3,13 @@ package manager import ( "bytes" "fmt" + "strings" + "time" + "github.com/seaweedfs/seaweed-up/pkg/cluster/spec" "github.com/seaweedfs/seaweed-up/pkg/disks" "github.com/seaweedfs/seaweed-up/pkg/operator" "github.com/seaweedfs/seaweed-up/scripts" - "strings" ) func (m *Manager) DeployVolumeServer(masters []string, volumeServerSpec *spec.VolumeServerSpec, index int) error { @@ -119,7 +121,8 @@ func (m *Manager) prepareUnmountedDisks(op operator.CommandOperator) error { } fmt.Printf("disks2: %+v\n", disks) - // format disk if no fstype + // format disk if no fstype, then resolve the resulting UUID so the fstab + // entry written below can mount by UUID instead of by device path. for _, dev := range disks { if dev.FilesystemType == "" { info("mkfs " + dev.Path) @@ -127,6 +130,13 @@ func (m *Manager) prepareUnmountedDisks(op operator.CommandOperator) error { return fmt.Errorf("create file system on %s: %v", dev.Path, err) } } + if dev.UUID == "" { + uuid, err := m.probeDiskUUID(op, dev.Path) + if err != nil { + return fmt.Errorf("resolve UUID for %s: %v", dev.Path, err) + } + dev.UUID = uuid + } } // mount them @@ -148,6 +158,7 @@ func (m *Manager) prepareUnmountedDisks(op operator.CommandOperator) error { data := map[string]interface{}{ "DevicePath": dev.Path, + "DeviceUUID": dev.UUID, "MountPoint": targetMountPoint, } prepareScript, err := scripts.RenderScript("prepare_disk.sh", data) @@ -160,8 +171,8 @@ func (m *Manager) prepareUnmountedDisks(op operator.CommandOperator) error { return fmt.Errorf("error received during upload mount script: %s", err) } - info("mount " + dev.DeviceName + "...") - err = op.Execute(fmt.Sprintf("cat /tmp/mount_%s.sh | SUDO_PASS=\"%s\" sh -\n", dev.DeviceName, m.sudoPass)) + info(fmt.Sprintf("mount %s (UUID=%s) at %s", dev.DeviceName, dev.UUID, targetMountPoint)) + err = op.Execute(fmt.Sprintf("cat /tmp/mount_%s.sh | SUDO_PASS=%s sh -\n", dev.DeviceName, shellSingleQuote(m.sudoPass))) if err != nil { return fmt.Errorf("error received during mount: %s", err) } @@ -171,3 +182,43 @@ func (m *Manager) prepareUnmountedDisks(op operator.CommandOperator) error { return nil } + +// probeDiskUUID reads the filesystem UUID of path via blkid. After mkfs the +// superblock is written but udev may not yet have re-read it, so we let it +// settle and retry a few times before giving up. blkid needs root to read +// raw block devices on most distros (and -p always needs root), so we wrap +// it the same way m.sudo does. Returning an error here aborts the deploy +// rather than writing a broken fstab entry that would leave the host unable +// to boot. +func (m *Manager) probeDiskUUID(op operator.CommandOperator, path string) (string, error) { + // Best-effort settle. Ignore errors — `udevadm` is missing on some + // minimal images and that's OK; the retry loop below will still pick + // up the UUID once it's available. + _ = m.sudo(op, "command -v udevadm >/dev/null 2>&1 && udevadm settle || true") + + // -p bypasses the blkid cache and re-probes the superblock directly, + // which is what we want right after mkfs. + probeCmd := fmt.Sprintf("blkid -p -s UUID -o value %s", shellSingleQuote(path)) + if m.sudoPass != "" { + probeCmd = fmt.Sprintf("echo %s | sudo -S %s", shellSingleQuote(m.sudoPass), probeCmd) + } + + const attempts = 5 + var lastErr error + for i := 0; i < attempts; i++ { + out, err := op.Output(probeCmd) + if err == nil { + uuid := strings.TrimSpace(string(out)) + if uuid != "" { + return uuid, nil + } + } else { + lastErr = err + } + time.Sleep(500 * time.Millisecond) + } + if lastErr != nil { + return "", fmt.Errorf("blkid returned no UUID after %d attempts: %v", attempts, lastErr) + } + return "", fmt.Errorf("blkid returned no UUID after %d attempts", attempts) +} diff --git a/scripts/prepare_disk.sh b/scripts/prepare_disk.sh index 25dee89..404e6f0 100644 --- a/scripts/prepare_disk.sh +++ b/scripts/prepare_disk.sh @@ -27,16 +27,25 @@ setup_env() { MOUNT_POINT={{.MountPoint}} DEVICE_PATH={{.DevicePath}} + DEVICE_UUID={{.DeviceUUID}} } setup_mount() { info "Setup Mount Point" $SUDO mkdir -p -m 755 ${MOUNT_POINT} - info "add ${DEVICE_PATH} ${MOUNT_POINT} to fstab" - echo "${DEVICE_PATH} ${MOUNT_POINT} ext4 noatime 0 2" | $SUDO tee -a /etc/fstab - info "mount ${DEVICE_PATH} ${MOUNT_POINT}" - $SUDO mount ${DEVICE_PATH} ${MOUNT_POINT} + if grep -qE "^UUID=${DEVICE_UUID}[[:space:]]" /etc/fstab; then + info "UUID=${DEVICE_UUID} already in fstab; skipping append" + else + info "add UUID=${DEVICE_UUID} (${DEVICE_PATH}) ${MOUNT_POINT} to fstab" + echo "UUID=${DEVICE_UUID} ${MOUNT_POINT} ext4 noatime 0 2" | $SUDO tee -a /etc/fstab + fi + if mountpoint -q "${MOUNT_POINT}"; then + info "${MOUNT_POINT} already mounted; skipping mount" + else + info "mount ${DEVICE_PATH} (UUID=${DEVICE_UUID}) at ${MOUNT_POINT}" + $SUDO mount ${MOUNT_POINT} + fi return 0 }