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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package kcpdatastore
import (
"errors"
"fmt"
"path/filepath"
"syscall"
"time"
"github.com/johnandersen777/socialweb-computer-kcp/internal/hostsvc"
"github.com/johnandersen777/socialweb-computer-kcp/internal/pgunit"
)
const (
KineStopTimeout = 10 * time.Second
MaxSocketPath = 107
PostgresUser = "kcp"
KineDatabase = "kine"
SqliteParams = "_journal_mode=WAL&_synchronous=NORMAL&_busy_timeout=30000&_txlock=immediate&_stmt_cache_size=20&_auto_vacuum=incremental"
)
var PostgresSettings = []string{"autovacuum=on", "track_counts=on"}
type KineConfig struct {
Unit string
Slice string
Exec string
Dir string
Socket string
PostgresSocketDir string
}
type PostgresConfig struct {
Unit string
Slice string
Install pgunit.Install
Data string
SocketDir string
}
func (c PostgresConfig) Server() pgunit.Server {
return pgunit.Server{Data: c.Data, SocketDir: c.SocketDir, User: PostgresUser, Settings: PostgresSettings}
}
func checkSocket(path string) error {
if !filepath.IsAbs(path) {
return fmt.Errorf("kcpdatastore: socket path %q must be absolute", path)
}
if len(path) > MaxSocketPath {
return fmt.Errorf("kcpdatastore: socket path %s is %d bytes, over the %d-byte sun_path limit", path, len(path), MaxSocketPath)
}
return nil
}
func kineArgv(c KineConfig, endpoint string) ([]string, error) {
if !filepath.IsAbs(c.Exec) {
return nil, fmt.Errorf("kcpdatastore: the kine executable must be an absolute path, got %q (dev builds take it from kine-path, never PATH)", c.Exec)
}
if !filepath.IsAbs(c.Dir) {
return nil, errors.New("kcpdatastore: the kine directory must be absolute")
}
if err := checkSocket(c.Socket); err != nil {
return nil, err
}
return []string{c.Exec,
"--endpoint", endpoint,
"--listen-address", "unix://" + c.Socket,
"--metrics-bind-address=0",
}, nil
}
func KineSqliteArgv(c KineConfig) ([]string, error) {
return kineArgv(c, "sqlite://"+filepath.Join(c.Dir, SqliteFile)+"?"+SqliteParams)
}
func PostgresDSN(socketDir string) string {
return pgunit.DSN(socketDir, PostgresUser, KineDatabase)
}
func KinePostgresArgv(c KineConfig) ([]string, error) {
if err := pgunit.CheckSocketDir(c.PostgresSocketDir); err != nil {
return nil, err
}
return kineArgv(c, PostgresDSN(c.PostgresSocketDir))
}
const KineMemoryMax = 512 << 20
func kineSpec(c KineConfig, argv []string, desc string) hostsvc.UnitSpec {
return hostsvc.UnitSpec{
Name: c.Unit,
Description: desc,
Exec: argv,
WorkDir: c.Dir,
Slice: c.Slice,
Restart: hostsvc.RestartOnFailure,
KillSignal: syscall.SIGTERM,
StopTimeout: KineStopTimeout,
Limits: hostsvc.Limits{MemoryMax: KineMemoryMax},
}
}
func KineSqliteSpec(c KineConfig) (hostsvc.UnitSpec, error) {
argv, err := KineSqliteArgv(c)
if err != nil {
return hostsvc.UnitSpec{}, err
}
return kineSpec(c, argv, "kine on sqlite for one application instance's kcp"), nil
}
func KinePostgresSpec(c KineConfig, store string) (hostsvc.UnitSpec, error) {
argv, err := KinePostgresArgv(c)
if err != nil {
return hostsvc.UnitSpec{}, err
}
spec := kineSpec(c, argv, "kine on the application instance's postgres for one kcp slot")
spec.After, spec.Wants = []string{store}, []string{store}
return spec, nil
}
func PostgresSpec(c PostgresConfig) (hostsvc.UnitSpec, error) {
if c.Install.Bin == "" || !filepath.IsAbs(c.Install.Bin) {
return hostsvc.UnitSpec{}, fmt.Errorf("kcpdatastore: the postgres asset tree is unset or relative (%q)", c.Install.Root)
}
return c.Install.UnitSpec(c.Unit, c.Slice, "postgres holding one application instance's kcp store (unix socket only)", c.Server())
}