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
package computevm
import (
"cmp"
"context"
"errors"
"fmt"
"log/slog"
"strings"
"time"
"k8s.io/client-go/rest"
"github.com/johnandersen777/socialweb-computer-kcp/api/v1alpha1"
"github.com/johnandersen777/socialweb-computer-kcp/internal/controllers/runtime/kcpapply"
"github.com/johnandersen777/socialweb-computer-kcp/internal/controllers/runtime/kube"
"github.com/johnandersen777/socialweb-computer-kcp/internal/controllers/runtime/manifests"
"github.com/johnandersen777/socialweb-computer-kcp/internal/hostprocess"
"github.com/johnandersen777/socialweb-computer-kcp/internal/hostsvc"
"github.com/johnandersen777/socialweb-computer-kcp/internal/kcpserver"
"github.com/johnandersen777/socialweb-computer-kcp/internal/openbaoctl"
"github.com/johnandersen777/socialweb-computer-kcp/internal/statesync"
"github.com/johnandersen777/socialweb-computer-kcp/internal/vault/openbao"
"github.com/johnandersen777/socialweb-computer-kcp/internal/vmruntime"
)
const (
ChildPrefix = "vm-"
LabelComputeVM = "socialweb.computer/computevm"
LabelSlot = "socialweb.computer/vm-slot"
BidderSet = "bidder"
RetryAfter = 3 * time.Second
OrphanGrace = 2 * time.Minute
MessageCap = 4000
ApplyTimeout = time.Minute
)
var GVR = v1alpha1.ComputeVMGVR
type Config struct {
Kubeconfig string
Workspace string
Handle string
Runtime vmruntime.Runtime
Manager hostsvc.Manager
OrphanGrace time.Duration
VaultServer string
VaultClient string
VaultMount string
Logins *openbao.Logins
Log *slog.Logger
}
type Host struct {
c Config
Rest *rest.Config
VMs kube.Resource
Backends kube.Resource
HostProcesses kube.Resource
servers kube.Resource
grants kube.Resource
syncs kube.Resource
}
func (c Config) log() *slog.Logger {
if c.Log == nil {
return slog.New(slog.DiscardHandler)
}
return c.Log.With("component", "computevm", "handle", c.Handle)
}
func (c Config) Grace() time.Duration {
return cmp.Or(c.OrphanGrace, OrphanGrace)
}
func ChildName(name string) string { return ChildPrefix + name }
func Open(ctx context.Context, c Config) (*Host, error) {
if c.Handle == "" {
return nil, errors.New("computevm: the controller names no handle; VM units and work dirs are namespaced by it")
}
if c.Runtime == nil || c.Manager == nil {
return nil, errors.New("computevm: no VM runtime")
}
if c.Logins == nil {
return nil, errors.New("computevm: no OpenBao logins")
}
c.Workspace = cmp.Or(c.Workspace, hostprocess.DefaultWorkspace)
restCfg, err := kcpserver.FrontRestConfig(c.Kubeconfig)
if err != nil {
return nil, err
}
if err := applySets(ctx, c, restCfg); err != nil {
return nil, err
}
cfg := rest.CopyConfig(restCfg)
cfg.Host = strings.TrimSuffix(cfg.Host, "/") + "/clusters/" + c.Workspace
dc, err := kube.New(cfg)
if err != nil {
return nil, err
}
h := &Host{c: c, Rest: restCfg, VMs: dc.Resource(GVR), Backends: dc.Resource(v1alpha1.FirecrackerVMGVR),
HostProcesses: dc.Resource(hostprocess.GVR), servers: dc.Resource(openbaoctl.GVR), grants: dc.Resource(openbaoctl.GrantGVR),
syncs: dc.Resource(statesync.StateSyncGVR)}
return h, nil
}
func (h *Host) Config() Config { return h.c }
func ApplyBidderSets(ctx context.Context, c Config, kubeconfig, workspace string) error {
restCfg, err := kcpserver.FrontRestConfig(kubeconfig)
if err != nil {
return err
}
c.Workspace = cmp.Or(workspace, hostprocess.DefaultWorkspace)
return applySets(ctx, c, restCfg)
}
func (h *Host) Log() *slog.Logger { return h.c.log() }
func applySets(ctx context.Context, c Config, restCfg *rest.Config) error {
ap, err := kcpapply.New(restCfg, kcpapply.Options{WaitTimeout: ApplyTimeout, Base: c.Workspace, Log: c.log()})
if err != nil {
return err
}
ix, err := manifests.Load()
if err != nil {
return err
}
for _, name := range []string{hostprocess.ProviderSet, hostprocess.ManifestSet, BidderSet} {
set, ok := ix.Set(name)
if !ok {
return fmt.Errorf("computevm: deploy/sets.yaml names no %s set", name)
}
values, err := manifests.DecodeValues(set.Values, nil)
if err != nil {
return err
}
if _, err := ap.Apply(ctx, set, values); err != nil {
return fmt.Errorf("computevm: applying the %s set: %w", name, err)
}
}
return nil
}
func CapMessage(s string) string {
if len(s) <= MessageCap {
return s
}
return s[:MessageCap/2] + "\n...\n" + s[len(s)-MessageCap/2:]
}
func Supervisor(c Config) hostprocess.Config {
return hostprocess.Config{Kubeconfig: c.Kubeconfig, Workspace: cmp.Or(c.Workspace, hostprocess.DefaultWorkspace), Manager: c.Manager,
Prefix: vmruntime.UnitPrefix(c.Handle), Bound: true, Selector: LabelComputeVM, KeepOrphans: true, Name: "computevm", Log: c.Log}
}