Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
lima-vm
GitHub Repository: lima-vm/lima
Path: blob/master/pkg/driver/wsl2/wsl_driver_windows.go
2637 views
1
// SPDX-FileCopyrightText: Copyright The Lima Authors
2
// SPDX-License-Identifier: Apache-2.0
3
4
package wsl2
5
6
import (
7
"context"
8
"embed"
9
"errors"
10
"fmt"
11
"net"
12
"regexp"
13
14
"github.com/Microsoft/go-winio"
15
"github.com/Microsoft/go-winio/pkg/guid"
16
"github.com/sirupsen/logrus"
17
18
"github.com/lima-vm/lima/v2/pkg/driver"
19
"github.com/lima-vm/lima/v2/pkg/freeport"
20
"github.com/lima-vm/lima/v2/pkg/limatype"
21
"github.com/lima-vm/lima/v2/pkg/limayaml"
22
"github.com/lima-vm/lima/v2/pkg/ptr"
23
"github.com/lima-vm/lima/v2/pkg/reflectutil"
24
"github.com/lima-vm/lima/v2/pkg/windows"
25
)
26
27
var knownYamlProperties = []string{
28
"Arch",
29
"Containerd",
30
"CopyToHost",
31
"CPUType",
32
"Disk",
33
"DNS",
34
"Env",
35
"HostResolver",
36
"Images",
37
"Message",
38
"Mounts",
39
"MountType",
40
"Param",
41
"Plain",
42
"PortForwards",
43
"Probes",
44
"PropagateProxyEnv",
45
"Provision",
46
"SSH",
47
"VMType",
48
}
49
50
const Enabled = true
51
52
type LimaWslDriver struct {
53
Instance *limatype.Instance
54
55
SSHLocalPort int
56
vSockPort int
57
virtioPort string
58
}
59
60
var _ driver.Driver = (*LimaWslDriver)(nil)
61
62
func New() *LimaWslDriver {
63
port, err := freeport.VSock()
64
if err != nil {
65
logrus.WithError(err).Error("failed to get free VSock port")
66
}
67
68
return &LimaWslDriver{
69
vSockPort: port,
70
virtioPort: "",
71
}
72
}
73
74
func (l *LimaWslDriver) Configure(inst *limatype.Instance) *driver.ConfiguredDriver {
75
l.Instance = inst
76
l.SSHLocalPort = inst.SSHLocalPort
77
78
return &driver.ConfiguredDriver{
79
Driver: l,
80
}
81
}
82
83
func (l *LimaWslDriver) FillConfig(ctx context.Context, cfg *limatype.LimaYAML, _ string) error {
84
if cfg.VMType == nil {
85
cfg.VMType = ptr.Of(limatype.WSL2)
86
}
87
if cfg.MountType == nil {
88
cfg.MountType = ptr.Of(limatype.WSLMount)
89
}
90
return validateConfig(ctx, cfg)
91
}
92
93
func (l *LimaWslDriver) Validate(ctx context.Context) error {
94
return validateConfig(ctx, l.Instance.Config)
95
}
96
97
func validateConfig(_ context.Context, cfg *limatype.LimaYAML) error {
98
if cfg == nil {
99
return errors.New("configuration is nil")
100
}
101
if cfg.MountType != nil && *cfg.MountType != limatype.WSLMount {
102
return fmt.Errorf("field `mountType` must be %q for WSL2 driver, got %q", limatype.WSLMount, *cfg.MountType)
103
}
104
// TODO: revise this list for WSL2
105
if cfg.VMType != nil {
106
if unknown := reflectutil.UnknownNonEmptyFields(cfg, knownYamlProperties...); len(unknown) > 0 {
107
logrus.Warnf("Ignoring: vmType %s: %+v", *cfg.VMType, unknown)
108
}
109
}
110
111
if !limayaml.IsNativeArch(*cfg.Arch) {
112
return fmt.Errorf("unsupported arch: %q", *cfg.Arch)
113
}
114
115
if cfg.VMType != nil {
116
if cfg.Images != nil && cfg.Arch != nil {
117
// TODO: real filetype checks
118
tarFileRegex := regexp.MustCompile(`.*tar\.*`)
119
for i, image := range cfg.Images {
120
if unknown := reflectutil.UnknownNonEmptyFields(image, "File"); len(unknown) > 0 {
121
logrus.Warnf("Ignoring: vmType %s: images[%d]: %+v", *cfg.VMType, i, unknown)
122
}
123
match := tarFileRegex.MatchString(image.Location)
124
if image.Arch == *cfg.Arch && !match {
125
return fmt.Errorf("unsupported image type for vmType: %s, tarball root file system required: %q", *cfg.VMType, image.Location)
126
}
127
}
128
}
129
130
if cfg.Mounts != nil {
131
for i, mount := range cfg.Mounts {
132
if unknown := reflectutil.UnknownNonEmptyFields(mount); len(unknown) > 0 {
133
logrus.Warnf("Ignoring: vmType %s: mounts[%d]: %+v", *cfg.VMType, i, unknown)
134
}
135
}
136
}
137
138
if cfg.Networks != nil {
139
for i, network := range cfg.Networks {
140
if unknown := reflectutil.UnknownNonEmptyFields(network); len(unknown) > 0 {
141
logrus.Warnf("Ignoring: vmType %s: networks[%d]: %+v", *cfg.VMType, i, unknown)
142
}
143
}
144
}
145
146
if cfg.Audio.Device != nil {
147
audioDevice := *cfg.Audio.Device
148
if audioDevice != "" {
149
logrus.Warnf("Ignoring: vmType %s: `audio.device`: %+v", *cfg.VMType, audioDevice)
150
}
151
}
152
}
153
154
return nil
155
}
156
157
//go:embed boot/*.sh
158
var bootFS embed.FS
159
160
func (l *LimaWslDriver) BootScripts() (map[string][]byte, error) {
161
scripts := make(map[string][]byte)
162
163
entries, err := bootFS.ReadDir("boot")
164
if err != nil {
165
return scripts, err
166
}
167
168
for _, entry := range entries {
169
if entry.IsDir() {
170
continue
171
}
172
173
content, err := bootFS.ReadFile("boot/" + entry.Name())
174
if err != nil {
175
return nil, err
176
}
177
178
scripts[entry.Name()] = content
179
}
180
181
return scripts, nil
182
}
183
184
func (l *LimaWslDriver) InspectStatus(ctx context.Context, inst *limatype.Instance) string {
185
status, err := getWslStatus(ctx, inst.Name)
186
if err != nil {
187
inst.Status = limatype.StatusBroken
188
inst.Errors = append(inst.Errors, err)
189
} else {
190
inst.Status = status
191
}
192
193
inst.SSHLocalPort = 22
194
195
if inst.Status == limatype.StatusRunning {
196
sshAddr, err := getSSHAddress(ctx, inst.Name)
197
if err == nil {
198
inst.SSHAddress = sshAddr
199
} else {
200
inst.Errors = append(inst.Errors, err)
201
}
202
}
203
204
return inst.Status
205
}
206
207
func (l *LimaWslDriver) Delete(ctx context.Context) error {
208
distroName := "lima-" + l.Instance.Name
209
status, err := getWslStatus(ctx, l.Instance.Name)
210
if err != nil {
211
return err
212
}
213
switch status {
214
case limatype.StatusRunning, limatype.StatusStopped, limatype.StatusBroken, limatype.StatusInstalling:
215
return unregisterVM(ctx, distroName)
216
}
217
218
logrus.Info("WSL VM is not running or does not exist, skipping deletion")
219
return nil
220
}
221
222
func (l *LimaWslDriver) Start(ctx context.Context) (chan error, error) {
223
if l.Instance.Config.SSH.OverVsock != nil && *l.Instance.Config.SSH.OverVsock {
224
// Probably never supportable for WSL2
225
logrus.Warn(".ssh.overVsock is not supported for WSL2 driver")
226
}
227
228
logrus.Infof("Starting WSL VM")
229
status, err := getWslStatus(ctx, l.Instance.Name)
230
if err != nil {
231
return nil, err
232
}
233
234
distroName := "lima-" + l.Instance.Name
235
236
if status == limatype.StatusUninitialized {
237
if err := EnsureFs(ctx, l.Instance); err != nil {
238
return nil, err
239
}
240
if err := initVM(ctx, l.Instance.Dir, distroName); err != nil {
241
return nil, err
242
}
243
}
244
245
errCh := make(chan error)
246
247
if err := startVM(ctx, distroName); err != nil {
248
return nil, err
249
}
250
251
if err := provisionVM(
252
ctx,
253
l.Instance.Dir,
254
l.Instance.Name,
255
distroName,
256
errCh,
257
); err != nil {
258
return nil, err
259
}
260
261
keepAlive(ctx, distroName, errCh)
262
263
return errCh, err
264
}
265
266
// CanRunGUI requires WSLg, which requires specific version of WSL2 to be installed.
267
// TODO: Add check and add support for WSLg (instead of VNC) to hostagent.
268
func (l *LimaWslDriver) canRunGUI() bool {
269
return false
270
}
271
272
func (l *LimaWslDriver) RunGUI() error {
273
return fmt.Errorf("RunGUI is not supported for the given driver '%s' and display '%s'", "wsl", *l.Instance.Config.Video.Display)
274
}
275
276
func (l *LimaWslDriver) Stop(ctx context.Context) error {
277
logrus.Info("Shutting down WSL2 VM")
278
distroName := "lima-" + l.Instance.Name
279
return stopVM(ctx, distroName)
280
}
281
282
// GuestAgentConn returns the guest agent connection, or nil (if forwarded by ssh).
283
// As of 08-01-2024, github.com/mdlayher/vsock does not natively support vsock on
284
// Windows, so use the winio library to create the connection.
285
func (l *LimaWslDriver) GuestAgentConn(ctx context.Context) (net.Conn, string, error) {
286
VMIDStr, err := windows.GetInstanceVMID(ctx, fmt.Sprintf("lima-%s", l.Instance.Name))
287
if err != nil {
288
return nil, "", err
289
}
290
VMIDGUID, err := guid.FromString(VMIDStr)
291
if err != nil {
292
return nil, "", err
293
}
294
sockAddr := &winio.HvsockAddr{
295
VMID: VMIDGUID,
296
ServiceID: winio.VsockServiceID(uint32(l.vSockPort)),
297
}
298
conn, err := winio.Dial(ctx, sockAddr)
299
if err != nil {
300
return nil, "", err
301
}
302
303
return conn, "vsock", nil
304
}
305
306
func (l *LimaWslDriver) Info() driver.Info {
307
var info driver.Info
308
info.Name = "wsl2"
309
if l.Instance != nil {
310
info.InstanceDir = l.Instance.Dir
311
}
312
info.VirtioPort = l.virtioPort
313
info.VsockPort = l.vSockPort
314
315
info.Features = driver.DriverFeatures{
316
DynamicSSHAddress: true,
317
StaticSSHPort: true,
318
SkipSocketForwarding: true,
319
NoCloudInit: true,
320
CanRunGUI: l.canRunGUI(),
321
}
322
return info
323
}
324
325
func (l *LimaWslDriver) SSHAddress(_ context.Context) (string, error) {
326
return "127.0.0.1", nil
327
}
328
329
func (l *LimaWslDriver) Create(_ context.Context) error {
330
return nil
331
}
332
333
func (l *LimaWslDriver) CreateDisk(_ context.Context) error {
334
return nil
335
}
336
337
func (l *LimaWslDriver) ChangeDisplayPassword(_ context.Context, _ string) error {
338
return nil
339
}
340
341
func (l *LimaWslDriver) DisplayConnection(_ context.Context) (string, error) {
342
return "", nil
343
}
344
345
func (l *LimaWslDriver) CreateSnapshot(_ context.Context, _ string) error {
346
return errUnimplemented
347
}
348
349
func (l *LimaWslDriver) ApplySnapshot(_ context.Context, _ string) error {
350
return errUnimplemented
351
}
352
353
func (l *LimaWslDriver) DeleteSnapshot(_ context.Context, _ string) error {
354
return errUnimplemented
355
}
356
357
func (l *LimaWslDriver) ListSnapshots(_ context.Context) (string, error) {
358
return "", errUnimplemented
359
}
360
361
func (l *LimaWslDriver) ForwardGuestAgent() bool {
362
// If driver is not providing, use host agent
363
return l.vSockPort == 0 && l.virtioPort == ""
364
}
365
366
func (l *LimaWslDriver) AdditionalSetupForSSH(_ context.Context) error {
367
return nil
368
}
369
370