summaryrefslogtreecommitdiff
path: root/internal/layout/layout.go
blob: 89e6848e3d1fa0a5965d3ee1c5bb0135ee1e401f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// Package layout defines Skiff's directory and dataset conventions in one
// place. Nothing else in the codebase should hardcode a path.
package layout

import (
	"fmt"
	"path/filepath"
)

const (
	DefaultHealthPort = 8080
	DefaultHealthPath = "/healthz"
	HealthTimeoutSecs = 10
)

// Config is Skiff's host-wide configuration. Every field has a sane FreeBSD
// default; override via flags on the skiff binary if a host differs.
type Config struct {
	ZFSPool      string // e.g. "zroot"
	BaseDataset  string // template jail root, cloned per app
	ReposDir     string // bare git repos live here
	JailsMount   string // parent mountpoint for per-app datasets
	JailConfDir  string // /etc/jail.conf.d
	NginxConfDir string // nginx include dir regenerated on every deploy
}

func Default() Config {
	return Config{
		ZFSPool:      "zroot/skiff",
		BaseDataset:  "zroot/skiff/base",
		ReposDir:     "/skiff/repos",
		JailsMount:   "/skiff/apps",
		JailConfDir:  "/etc/jail.conf.d",
		NginxConfDir: "/usr/local/etc/nginx/skiff.d",
	}
}

func (c Config) AppDataset(app string) string {
	return fmt.Sprintf("%s/apps/%s", c.ZFSPool, app)
}

func (c Config) JailRoot(app string) string {
	return filepath.Join(c.JailsMount, app)
}

func (c Config) RepoPath(app string) string {
	return filepath.Join(c.ReposDir, app+".git")
}

func (c Config) JailConfPath(app string) string {
	return filepath.Join(c.JailConfDir, app+".conf")
}

func (c Config) NginxConfPath(app string) string {
	return filepath.Join(c.NginxConfDir, app+".conf")
}

// Everything below is Capistrano-style: each deploy gets its own immutable
// releases/<sha> checkout (built in place, inside the jail's own dataset),
// `current` is a symlink to whichever release is live, and data.db/env are
// persistent siblings that outlive any single release.
func (c Config) AppDir(app string) string {
	return filepath.Join(c.JailRoot(app), "app")
}

func (c Config) ReleaseDir(app, sha string) string {
	return filepath.Join(c.AppDir(app), "releases", sha)
}

func (c Config) CurrentRelease(app string) string {
	return filepath.Join(c.AppDir(app), "current")
}

// ReleaseDirInJail is releases/<sha> as seen from inside the jail's own
// chroot, not the host. The `current` symlink must be written with this
// form as its target: it's dereferenced by the jail's rc.d script (execing
// CurrentBin), where a host-absolute path like ReleaseDir's resolves
// relative to the jail root instead and never exists.
func (c Config) ReleaseDirInJail(sha string) string {
	return filepath.Join("/app", "releases", sha)
}

// CurrentBin is the path the jail's rc.d script execs - must match
// internal/tmpl/rc.d.tmpl's command_args.
func (c Config) CurrentBin(app string) string {
	return filepath.Join(c.CurrentRelease(app), "app")
}

func (c Config) MakefilePath(app, sha string) string {
	return filepath.Join(c.ReleaseDir(app, sha), "Makefile")
}

func (c Config) DataDB(app string) string {
	return filepath.Join(c.AppDir(app), "data.db")
}

func (c Config) EnvFile(app string) string {
	return filepath.Join(c.AppDir(app), "env")
}

func (c Config) MetaPath(app string) string {
	return filepath.Join(c.AppDir(app), "meta.json")
}

func (c Config) SnapshotName(app, sha string) string {
	return fmt.Sprintf("%s@pre-%s", c.AppDataset(app), sha)
}