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
package bootstrap
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"syscall"
"time"
"github.com/johnandersen777/socialweb-computer-kcp/internal/devinstance"
"github.com/johnandersen777/socialweb-computer-kcp/internal/hostsvc"
)
const (
ProfileStandIns = "stand-ins"
StandInStopGrace = 5 * time.Second
)
var standInRole = regexp.MustCompile(`^[a-z0-9][a-z0-9-]{0,23}$`)
func StandInUnit(l devinstance.Layout, role string) string {
return l.Names().StandIn(role)
}
func ReadStandIns(path string) ([]devinstance.StandIn, error) {
b, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var s []devinstance.StandIn
if err := json.Unmarshal(b, &s); err != nil {
return nil, fmt.Errorf("bootstrap: stand-ins %s: %w", path, err)
}
if len(s) == 0 {
return nil, fmt.Errorf("bootstrap: stand-ins %s names no unit", path)
}
seen := map[string]bool{}
for _, u := range s {
if !standInRole.MatchString(u.Role) || seen[u.Role] || u.Role == "core" {
return nil, fmt.Errorf("bootstrap: stand-in role %q: want a unique lower-case name of at most 24, not core", u.Role)
}
seen[u.Role] = true
if len(u.Exec) == 0 || !filepath.IsAbs(u.Exec[0]) {
return nil, fmt.Errorf("bootstrap: stand-in %s: exec must start with an absolute path (never PATH)", u.Role)
}
}
return s, nil
}
const StandInMemoryMax = 1 << 30
func (c Config) standInSpec(s devinstance.StandIn) hostsvc.UnitSpec {
l := c.Layout
log := filepath.Join(l.Logs, s.Role+".log")
return hostsvc.UnitSpec{Name: StandInUnit(l, s.Role), Description: "stand-in " + s.Role + " of " + l.ID, Exec: s.Exec,
Env: s.Env, WorkDir: l.StateDir, Slice: l.KcpSlice(), Restart: hostsvc.RestartNo, KillSignal: syscall.SIGTERM,
StopTimeout: StandInStopGrace, Limits: hostsvc.Limits{MemoryMax: StandInMemoryMax}, Output: hostsvc.Output{StdoutFile: log, StderrFile: log}}
}
func ensureStandIns(ctx context.Context, m hostsvc.Manager, cfg Config, rec *devinstance.Record) error {
cfg.silent = map[string]bool{}
for _, s := range rec.StandIns {
cfg.silent[s.Role] = !s.Listens
}
if err := os.MkdirAll(cfg.Layout.Logs, 0o700); err != nil {
return err
}
for _, s := range rec.StandIns {
u, _, err := ensureUnit(ctx, m, cfg, rec, s.Role, cfg.standInSpec(s), nil)
if err != nil {
return fmt.Errorf("bootstrap: stand-in %s: %w", s.Role, err)
}
if s.Listens {
u.Endpoint = "tcp://127.0.0.1:" + strconv.Itoa(u.Port)
cfg.onListen(s.Role, u.Port)
}
rec.SetUnit(u)
}
rec.Ready = true
rec.Updated = time.Now().UTC()
return nil
}