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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
package bidder
import (
"context"
"fmt"
"log/slog"
"maps"
"os"
"path/filepath"
"sync/atomic"
"time"
"github.com/johnandersen777/socialweb-computer-kcp/api/v1alpha1"
"github.com/johnandersen777/socialweb-computer-kcp/internal/bootstrap"
"github.com/johnandersen777/socialweb-computer-kcp/internal/bootstrap/table"
"github.com/johnandersen777/socialweb-computer-kcp/internal/computevm"
"github.com/johnandersen777/socialweb-computer-kcp/internal/devcore"
"github.com/johnandersen777/socialweb-computer-kcp/internal/devinstance"
"github.com/johnandersen777/socialweb-computer-kcp/internal/hostsvc"
"github.com/johnandersen777/socialweb-computer-kcp/internal/install"
"github.com/johnandersen777/socialweb-computer-kcp/internal/kit/fsx"
"github.com/johnandersen777/socialweb-computer-kcp/internal/notify"
)
const (
ExeCore = "socialweb-computer"
ExeKcp = "kcp"
ExeKine = "kine"
ExeBao = "bao"
ApplyTimeout = time.Minute
RootApplyDeadline = 3 * time.Minute
RootRetryLogEvery = time.Minute
RootRetryMax = 15 * time.Second
)
type CoreConfig struct {
StateDir string
Prefix string
Handle string
Executables map[string]devinstance.Executable
Spec v1alpha1.MarketBidderSpec
VM devinstance.VMRuntime
ComputeVM computevm.Config
FrontAddress string
ReadyTimeout time.Duration
Unit string
Log *slog.Logger
}
func NewLayout(state, prefix, handle string) devinstance.Layout {
return install.BidderLayout(state, prefix, handle)
}
func (c CoreConfig) app() table.App {
a := table.Bidder(c.Handle, install.HandleID(c.Handle))
if c.Prefix != "" {
a.Prefix = c.Prefix
}
return a
}
func (c CoreConfig) record(l devinstance.Layout) (devinstance.Record, error) {
var rec devinstance.Record
if err := fsx.ReadJSON(l.Record, &rec); err != nil {
rec = devinstance.Record{Created: time.Now().UTC()}
}
p := bootstrap.InstallParams(c.app(), c.StateDir)
exes := map[string]devinstance.Executable{}
maps.Copy(exes, c.Executables)
if kcp, kine := c.Executables[ExeKcp].Path, c.Executables[ExeKine].Path; kcp != "" && kine != "" {
if err := bootstrap.LinkAssets(p, kcp, kine, ""); err != nil {
return rec, err
}
}
for name, link := range map[string]string{ExeKcp: p.KcpPath, ExeKine: p.KinePath} {
if _, err := os.Stat(link); err != nil {
return rec, fmt.Errorf("bidder: %s is not linked at %s (install links it; a dev build names it with its -path option): %w", name, link, err)
}
e := exes[name]
e.Path = link
exes[name] = e
}
vm := c.VM
rec.Layout, rec.Profile, rec.Backend, rec.Executables = l, bootstrap.ProfileQuick, hostsvc.BackendSystemdUser, exes
rec.BaseWorkspace, rec.Host, rec.Parent, rec.StandIns, rec.OpenBao, rec.VM = "", false, nil, nil, nil, &vm
return rec, nil
}
func RunCore(ctx context.Context, c CoreConfig, m hostsvc.Manager) error {
if !filepath.IsAbs(c.StateDir) {
return fmt.Errorf("bidder: state dir %q is not absolute", c.StateDir)
}
l := NewLayout(c.StateDir, c.Prefix, c.Handle)
for _, d := range []string{l.StateDir, l.KcpDir(), l.RuntimeDir, l.Logs} {
if err := os.MkdirAll(d, 0o700); err != nil {
return err
}
}
if n := len(filepath.Join(l.RuntimeDir, "kine-a.sock")); n > devinstance.MaxSocketPath {
return fmt.Errorf("bidder: the runtime dir %s makes socket paths of %d bytes, over %d", l.RuntimeDir, n, devinstance.MaxSocketPath)
}
rec, err := c.record(l)
if err != nil {
return err
}
if err := fsx.WriteJSON(l.Record, rec, 0o600); err != nil {
return err
}
app := c.app()
log := c.Log.With("component", "run-bidder", "handle", c.Handle)
log.Info("the bidder application instance starts: kcp store, kine, front, its slot, then its MarketBidder root object",
"state", l.StateDir, "units", app.Names().UnitPrefix(), "profile", c.Spec.Profile)
cvm := c.ComputeVM
var watchdogLogged atomic.Bool
go notify.KeepAlive(ctx, func(err error) {
if watchdogLogged.CompareAndSwap(false, true) {
log.Warn("telling systemd's watchdog the core is alive", "err", err.Error())
}
})
return devcore.Run(ctx, devcore.Config{Layout: l, ReadyTimeout: c.ReadyTimeout, Log: c.Log, Application: &app,
FrontAddress: c.FrontAddress, ComputeVM: &cvm, SignInGuard: CheckSignIn,
OnStep: func(step string) { log.Info("the core brings up the stack", "step", step) },
OnTick: func() {
if err := notify.Watchdog(); err != nil && watchdogLogged.CompareAndSwap(false, true) {
log.Warn("telling systemd's watchdog the core is alive", "err", err.Error())
}
},
Members: func(ctx context.Context, rec devinstance.Record) error {
k, err := OpenKcp(rec.AdminKubeconfig)
if err != nil {
return err
}
if err := c.applyRoot(ctx, k, log); err != nil {
return err
}
if err := notify.Ready(); err != nil {
return fmt.Errorf("bidder: telling systemd the core is ready: %w", err)
}
<-ctx.Done()
return nil
}}, m)
}
func attempts(n int) string {
if n == 1 {
return "1 attempt"
}
return fmt.Sprintf("%d attempts", n)
}
func (c CoreConfig) applyRoot(ctx context.Context, k *Kcp, log *slog.Logger) error {
started := time.Now()
last := started
var lastErr error
delay := Poll
for attempt := 1; ; attempt++ {
remaining := RootApplyDeadline - time.Since(started)
if remaining <= 0 {
return fmt.Errorf("bidder: the MarketBidder root object is not applied after %s and %s: %w",
time.Since(started).Round(time.Second), attempts(attempt-1), lastErr)
}
actx, cancel := context.WithTimeout(ctx, remaining)
err := k.ApplyRoot(actx, c.Spec)
cancel()
if err == nil {
log.Info("the MarketBidder root object is applied; the core renders its components", "profile", c.Spec.Profile,
"attempts", attempt, "after", time.Since(started).Round(time.Second).String())
return nil
}
lastErr = err
if attempt == 1 || time.Since(last) >= RootRetryLogEvery {
log.Warn("applying the MarketBidder root object; still retrying",
"attempt", attempt, "elapsed", time.Since(started).Round(time.Second).String(),
"deadline", RootApplyDeadline.String(), "err", err.Error())
last = time.Now()
}
retry := time.NewTimer(min(delay, max(RootApplyDeadline-time.Since(started), 0)))
select {
case <-ctx.Done():
retry.Stop()
return ctx.Err()
case <-retry.C:
}
delay = min(2*delay, RootRetryMax)
}
}