From d1e1c226d9a63432c652d373c039a3ae4430f3a2 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 00:03:04 -0500 Subject: Guard deploys and config changes against leftover state on retry - Extract deploy.Run's snapshot -> apply -> restart -> health-check -> rollback-on-failure sequence into deploy.GuardedApply, shared with cmd/skiff's applyConfigChange, which duplicated it verbatim. - Clean up the worktree and snapshot a deploy creates when it fails before cutover (e.g. a failing release-phase migration), so retrying the same sha no longer needs manual cleanup. - Make checkout and snapshotting idempotent for a same-sha retry after a cutover that later got rolled back on a failed health check, where the release dir and snapshot are deliberately left behind by design. - Document the actual cause of a jail not surviving a host reboot: FreeBSD ships no /etc/jail.conf, so boot-time `service jail start` finds nothing to start regardless of jail.conf.d - only name-targeted starts read jail.conf.d directly, which is why manual starts worked. --- internal/zfsutil/zfsutil.go | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'internal/zfsutil') diff --git a/internal/zfsutil/zfsutil.go b/internal/zfsutil/zfsutil.go index 8f1b0e2..918289c 100644 --- a/internal/zfsutil/zfsutil.go +++ b/internal/zfsutil/zfsutil.go @@ -33,6 +33,14 @@ func Rollback(ctx context.Context, out io.Writer, dataset, tag string) error { return sh.Run(ctx, out, "", "zfs", "rollback", "-r", snapRef(dataset, tag)) } +// DestroySnapshot removes a snapshot that was never needed as a rollback +// target - e.g. one taken right before an apply step that failed before +// anything actually changed. Left behind, it collides with the same tag on +// the next retry. +func DestroySnapshot(ctx context.Context, out io.Writer, dataset, tag string) error { + return sh.Run(ctx, out, "", "zfs", "destroy", snapRef(dataset, tag)) +} + // 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