volume.go

65 lines
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
package scaffold

import (
	"fmt"
	"log"

	"congo.gg/pkg/platform"
)

func ensureVolume(p *platform.Platform, server *platform.Server, spec *VolumeSpec, region platform.Region) error {
	if !SafeName(spec.Name) {
		return fmt.Errorf("volume name contains invalid characters: %q (use alphanumeric, dash, underscore)", spec.Name)
	}
	if !SafePath(spec.Mount) {
		return fmt.Errorf("volume mount path invalid: %q (must be absolute, no ..)", spec.Mount)
	}
	log.Printf("   Ensuring volume %s...", spec.Name)

	vol, err := p.GetVolume(spec.Name)
	if err == platform.ErrUnsupported {
		// Metal provider: no cloud volumes, just ensure mount dir exists
		log.Printf("   Using local disk for %s", spec.Name)
		if _, err := server.SSH("mkdir", "-p", spec.Mount); err != nil {
			return fmt.Errorf("create mount dir: %w", err)
		}
		return nil
	}

	isNew := err == platform.ErrNotFound
	if isNew {
		vol, err = p.CreateVolume(spec.Name, spec.Size, region)
		if err != nil {
			return fmt.Errorf("create volume: %w", err)
		}
	} else if err != nil {
		return fmt.Errorf("get volume: %w", err)
	}

	if err := p.AttachVolume(vol.ID, server.ID); err != nil {
		log.Printf("   Volume attach: %v (may already be attached)", err)
	}

	devicePath := fmt.Sprintf("/dev/disk/by-id/scsi-0DO_Volume_%s", spec.Name)
	if isNew {
		formatScript := fmt.Sprintf(`
			for i in $(seq 1 30); do [ -e %s ] && break; sleep 1; done
			if ! blkid %s >/dev/null 2>&1; then mkfs.ext4 %s; fi
		`, devicePath, devicePath, devicePath)
		if _, err := server.SSH("bash", "-c", formatScript); err != nil {
			return fmt.Errorf("format volume: %w", err)
		}
	}

	mountScript := fmt.Sprintf(`
		mkdir -p %s
		grep -qF "%s" /etc/fstab || echo "%s %s ext4 defaults,nofail,discard 0 2" >> /etc/fstab
		mountpoint -q %s || mount %s || true
	`, spec.Mount, spec.Name, devicePath, spec.Mount, spec.Mount, spec.Mount)

	if _, err := server.SSH("bash", "-c", mountScript); err != nil {
		return fmt.Errorf("mount volume: %w", err)
	}

	return nil
}