Files
URBM/internal/platform/mounts.go
T

102 lines
2.8 KiB
Go

package platform
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"git.casaderoll.de/michael/urbm/internal/model"
)
type CredentialGetter interface{ Get(string) (string, error) }
type MountManager struct {
RuntimeDir string
Secrets CredentialGetter
}
type MountedRepository struct {
Repository model.Repository
Mounted bool
CredentialFile string
}
func (m *MountManager) Prepare(ctx context.Context, repo model.Repository) (MountedRepository, error) {
result := MountedRepository{Repository: repo}
if repo.Type != model.RepositorySMB && repo.Type != model.RepositoryNFS {
return result, nil
}
if repo.Mount == nil || !repo.Mount.Managed {
if _, err := os.Stat(repo.Location); err != nil {
return result, fmt.Errorf("connectivity: external mount unavailable: %w", err)
}
return result, nil
}
point := filepath.Clean(repo.Mount.MountPoint)
if !strings.HasPrefix(point, "/mnt/remotes/") && !strings.HasPrefix(point, "/mnt/disks/") {
return result, errors.New("validation: managed mountPoint must be below /mnt/remotes or /mnt/disks")
}
if err := os.MkdirAll(point, 0700); err != nil {
return result, err
}
mountType := string(repo.Type)
if repo.Type == model.RepositorySMB {
mountType = "cifs"
}
args := []string{"-t", mountType, repo.Mount.Remote, point}
if repo.Type == model.RepositorySMB {
options := []string{"nosuid", "nodev"}
if repo.CredentialRef != "" {
credential, err := m.Secrets.Get(repo.CredentialRef)
if err != nil {
return result, fmt.Errorf("authentication: load mount credential: %w", err)
}
dir := filepath.Join(m.RuntimeDir, "secrets")
if err := os.MkdirAll(dir, 0700); err != nil {
return result, err
}
f, err := os.CreateTemp(dir, "smb-credentials-*")
if err != nil {
return result, err
}
result.CredentialFile = f.Name()
if err := f.Chmod(0600); err != nil {
f.Close()
return result, err
}
if _, err := f.WriteString(credential); err != nil {
f.Close()
return result, err
}
if err := f.Close(); err != nil {
return result, err
}
options = append(options, "credentials="+result.CredentialFile)
}
args = append(args, "-o", strings.Join(options, ","))
} else {
args = append(args, "-o", "nosuid,nodev")
}
if err := exec.CommandContext(ctx, "mount", args...).Run(); err != nil {
m.Cleanup(context.Background(), result)
return result, fmt.Errorf("connectivity: mount repository: %w", err)
}
result.Mounted = true
result.Repository.Location = point
return result, nil
}
func (m *MountManager) Cleanup(ctx context.Context, mounted MountedRepository) error {
if mounted.CredentialFile != "" {
defer os.Remove(mounted.CredentialFile)
}
if !mounted.Mounted {
return nil
}
return exec.CommandContext(ctx, "umount", mounted.Repository.Location).Run()
}