summaryrefslogtreecommitdiff
path: root/internal/layout/layout.go
diff options
context:
space:
mode:
authorClaude <noreply@anthropic.com>2026-08-07 19:02:40 -0500
committerClaude <noreply@anthropic.com>2026-08-07 19:02:40 -0500
commitacde3827f60bc90738429fa490a36a1856a93283 (patch)
treeae4b345b30a1308170ffe3e4c00cc1a8360c796b /internal/layout/layout.go
parentd1e1c226d9a63432c652d373c039a3ae4430f3a2 (diff)
Add per-app flock locking around deploy, config change, rollback, new-app
Nothing previously serialized these against each other, so concurrent invocations for the same app (a manual config change racing an in-flight deploy, a double git push, a rollback mid-deploy) could interleave snapshot/apply/restart/rollback sequences against the same dataset and jail. The lock file lives under a new LocksDir, outside any per-app ZFS dataset, since those datasets get rolled back on a failed health check or a manual rollback.
Diffstat (limited to 'internal/layout/layout.go')
-rw-r--r--internal/layout/layout.go11
1 files changed, 11 insertions, 0 deletions
diff --git a/internal/layout/layout.go b/internal/layout/layout.go
index d5888dc..5c2cf30 100644
--- a/internal/layout/layout.go
+++ b/internal/layout/layout.go
@@ -22,6 +22,7 @@ type Config struct {
JailsMount string // parent mountpoint for per-app datasets
JailConfDir string // /etc/jail.conf.d
NginxConfDir string // nginx include dir regenerated on every deploy
+ LocksDir string // per-app flock files live here, outside any per-app dataset
}
func Default() Config {
@@ -32,6 +33,7 @@ func Default() Config {
JailsMount: "/skiff/apps",
JailConfDir: "/etc/jail.conf.d",
NginxConfDir: "/usr/local/etc/nginx/skiff.d",
+ LocksDir: "/skiff/locks",
}
}
@@ -47,6 +49,15 @@ func (c Config) RepoPath(app string) string {
return filepath.Join(c.ReposDir, app+".git")
}
+// LockPath is the per-app flock file that serializes deploy, config change,
+// and rollback against each other. It deliberately lives outside
+// AppDataset(app): that dataset gets `zfs rollback`ed on a failed health
+// check or a manual rollback, which would discard a lock file living inside
+// it along with everything else written since the snapshot.
+func (c Config) LockPath(app string) string {
+ return filepath.Join(c.LocksDir, app+".lock")
+}
+
func (c Config) JailConfPath(app string) string {
return filepath.Join(c.JailConfDir, app+".conf")
}