From 025a365a23f5ed2b3df4df71cbff9689575968d9 Mon Sep 17 00:00:00 2001 From: Mike Wild Date: Tue, 25 Aug 2026 23:04:10 -0500 Subject: Split per-app dataset into OS/data layers, add skiff rebuild Each app now gets two ZFS datasets - a disposable OS layer (zroot/skiff/appbase/) cloned from base, and a persistent data layer (zroot/skiff/appdata/) holding releases/current/env/ meta.json/data.db. `skiff rebuild --app|--all` discards and reclones the OS layer to pick up an OS/toolchain/config-template upgrade, guarded by the same snapshot-and-rollback safety net as deploy and config changes; the data layer is never touched. Also fixes stale docs (builds already run via jexec inside the jail, not on the host; --iface already auto-aliases the jail's IP, so the manual ifconfig step and its troubleshooting entry are gone) found while updating the same sections for this feature. Co-Authored-By: Claude Sonnet 5 --- internal/zfsutil/zfsutil.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'internal/zfsutil') diff --git a/internal/zfsutil/zfsutil.go b/internal/zfsutil/zfsutil.go index 918289c..5e68e30 100644 --- a/internal/zfsutil/zfsutil.go +++ b/internal/zfsutil/zfsutil.go @@ -41,6 +41,39 @@ func DestroySnapshot(ctx context.Context, out io.Writer, dataset, tag string) er return sh.Run(ctx, out, "", "zfs", "destroy", snapRef(dataset, tag)) } +// Create makes a new, empty dataset mounted at mountpoint - unlike Clone, +// it has no origin snapshot to inherit from. +func Create(ctx context.Context, out io.Writer, dataset, mountpoint string) error { + return sh.Run(ctx, out, "", "zfs", "create", "-o", "mountpoint="+mountpoint, dataset) +} + +// Rename renames a dataset. It deliberately does not touch the dataset's +// mountpoint property, which zfs(8) leaves unchanged on a rename - callers +// that care where the renamed dataset mounts must call SetMountpoint too. +func Rename(ctx context.Context, out io.Writer, oldName, newName string) error { + return sh.Run(ctx, out, "", "zfs", "rename", oldName, newName) +} + +func SetMountpoint(ctx context.Context, out io.Writer, dataset, mountpoint string) error { + return sh.Run(ctx, out, "", "zfs", "set", "mountpoint="+mountpoint, dataset) +} + +func Mount(ctx context.Context, out io.Writer, dataset string) error { + return sh.Run(ctx, out, "", "zfs", "mount", dataset) +} + +func Unmount(ctx context.Context, out io.Writer, dataset string) error { + return sh.Run(ctx, out, "", "zfs", "unmount", dataset) +} + +// Destroy removes an entire dataset and any snapshots on it, as opposed to +// DestroySnapshot's single dataset@tag. Recursive because callers use this +// to discard a whole discarded OS-layer clone - an operator's own manual +// snapshot on it shouldn't be able to block that. +func Destroy(ctx context.Context, out io.Writer, dataset string) error { + return sh.Run(ctx, out, "", "zfs", "destroy", "-r", dataset) +} + // Snapshots lists snapshot names (without the dataset@ prefix), newest last. func Snapshots(ctx context.Context, dataset string) ([]string, error) { raw, err := sh.Output(ctx, "", "zfs", "list", "-H", "-t", "snapshot", "-o", "name", "-s", "creation", "-r", dataset) -- cgit v1.3