Files
storage-appliance/internal/infra/nfs/nfs.go

75 lines
2.1 KiB
Go

package nfs
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/example/storage-appliance/internal/domain"
"github.com/example/storage-appliance/internal/infra/osexec"
)
type Adapter struct {
Runner osexec.Runner
ExportsPath string
}
func NewAdapter(runner osexec.Runner, exportsPath string) *Adapter {
if exportsPath == "" {
exportsPath = "/etc/exports"
}
return &Adapter{Runner: runner, ExportsPath: exportsPath}
}
// RenderExports renders the given shares into /etc/exports atomically
func (a *Adapter) RenderExports(ctx context.Context, shares []domain.Share) error {
var lines []string
for _, s := range shares {
// default options for NFS export
opts := "rw,sync,no_root_squash"
if s.Type == "nfs" {
// if options stored as JSON use it
if sPath := s.Path; sPath != "" {
// options may be in s.Name? No, for now use default
}
}
lines = append(lines, fmt.Sprintf("%s %s", s.Path, opts))
}
content := strings.Join(lines, "\n") + "\n"
dir := filepath.Dir(a.ExportsPath)
tmp := filepath.Join(dir, ".exports.tmp")
if err := os.WriteFile(tmp, []byte(content), 0644); err != nil {
return err
}
// atomic rename
if err := os.Rename(tmp, a.ExportsPath); err != nil {
return err
}
return nil
}
// Apply runs exportfs -ra to apply exports
func (a *Adapter) Apply(ctx context.Context) error {
_, stderr, _, err := osexec.ExecWithRunner(a.Runner, ctx, "exportfs", "-ra")
if err != nil {
return fmt.Errorf("exportfs failed: %s", stderr)
}
return nil
}
// Status checks systemd for nfs server status
func (a *Adapter) Status(ctx context.Context) (string, error) {
// try common unit names
names := []string{"nfs-server", "nfs-kernel-server"}
for _, n := range names {
out, _, _, err := osexec.ExecWithRunner(a.Runner, ctx, "systemctl", "is-active", n)
if err == nil && strings.TrimSpace(out) != "" {
return strings.TrimSpace(out), nil
}
}
return "unknown", nil
}