75 lines
2.1 KiB
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
|
|
}
|