Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
lima-vm
GitHub Repository: lima-vm/lima
Path: blob/master/pkg/driverutil/vm.go
2611 views
1
// SPDX-FileCopyrightText: Copyright The Lima Authors
2
// SPDX-License-Identifier: Apache-2.0
3
4
package driverutil
5
6
import (
7
"bytes"
8
"context"
9
"encoding/json"
10
"errors"
11
"fmt"
12
"os/exec"
13
14
"github.com/sirupsen/logrus"
15
16
"github.com/lima-vm/lima/v2/pkg/limatype"
17
"github.com/lima-vm/lima/v2/pkg/registry"
18
)
19
20
// ResolveVMType sets the VMType field in the given LimaYAML if not already set.
21
// It validates the configuration against the specified or default VMType.
22
func ResolveVMType(ctx context.Context, y *limatype.LimaYAML, filePath string) error {
23
if y.VMType != nil && *y.VMType != "" {
24
if err := validateConfigAgainstDriver(ctx, y, filePath, *y.VMType); err != nil {
25
return err
26
}
27
logrus.Debugf("Using specified vmType %q for %q", *y.VMType, filePath)
28
return nil
29
}
30
31
// If VMType is not specified, we go with the default platform driver.
32
vmType := limatype.DefaultDriver()
33
return validateConfigAgainstDriver(ctx, y, filePath, vmType)
34
}
35
36
func validateConfigAgainstDriver(ctx context.Context, y *limatype.LimaYAML, filePath, vmType string) error {
37
extDriver, intDriver, exists := registry.Get(vmType)
38
if !exists {
39
return fmt.Errorf("vmType %q is not a registered driver", vmType)
40
}
41
42
if extDriver != nil {
43
return handlePreConfiguredDriverAction(ctx, y, extDriver.Path, filePath)
44
}
45
46
if err := intDriver.FillConfig(ctx, y, filePath); err != nil {
47
return err
48
}
49
50
return nil
51
}
52
53
func handlePreConfiguredDriverAction(ctx context.Context, y *limatype.LimaYAML, extDriverPath, filePath string) error {
54
cmd := exec.CommandContext(ctx, extDriverPath, "--pre-driver-action")
55
56
var stderrBuf bytes.Buffer
57
cmd.Stderr = &stderrBuf
58
59
stdout, err := cmd.StdoutPipe()
60
if err != nil {
61
return fmt.Errorf("failed to get stdout pipe: %w", err)
62
}
63
stdin, err := cmd.StdinPipe()
64
if err != nil {
65
return fmt.Errorf("failed to get stdin pipe: %w", err)
66
}
67
if err := cmd.Start(); err != nil {
68
return fmt.Errorf("failed to start external driver: %w", err)
69
}
70
71
encoder := json.NewEncoder(stdin)
72
if err := encoder.Encode(limatype.PreConfiguredDriverPayload{
73
Config: *y,
74
FilePath: filePath,
75
}); err != nil {
76
stdin.Close()
77
return fmt.Errorf("failed to encode pre-configured driver payload: %w", err)
78
}
79
stdin.Close()
80
81
decoder := json.NewDecoder(stdout)
82
var res limatype.LimaYAML
83
if err := decoder.Decode(&res); err != nil {
84
return fmt.Errorf("failed to decode pre-configured driver response: %w", err)
85
}
86
87
if err := cmd.Wait(); err != nil {
88
if stderrBuf.Len() > 0 {
89
return fmt.Errorf("pre-configured driver command failed: %w; stderr: %s", err, stderrBuf.String())
90
}
91
return fmt.Errorf("pre-configured driver command failed: %w", err)
92
}
93
94
if stderrBuf.Len() > 0 {
95
logrus.Debugf("external driver stderr: %s", stderrBuf.String())
96
}
97
98
*y = res
99
logrus.Debugf("Pre-configured driver action completed successfully for %q", extDriverPath)
100
return nil
101
}
102
103
func InspectStatus(ctx context.Context, inst *limatype.Instance) (string, error) {
104
if inst == nil || inst.Config == nil || inst.Config.VMType == nil {
105
return "", errors.New("instance or its configuration is not properly initialized")
106
}
107
108
extDriver, intDriver, exists := registry.Get(*inst.Config.VMType)
109
if !exists {
110
return "", fmt.Errorf("unknown or unsupported VM type: %s", *inst.Config.VMType)
111
}
112
113
if extDriver != nil {
114
status, err := handleInspectStatusAction(ctx, inst, extDriver.Path)
115
if err != nil {
116
extDriver.Logger.Errorf("Failed to inspect status for instance %q: %v", inst.Name, err)
117
return "", err
118
}
119
extDriver.Logger.Debugf("Instance %q inspected successfully with status: %s", inst.Name, inst.Status)
120
return status, nil
121
}
122
123
return intDriver.InspectStatus(ctx, inst), nil
124
}
125
126
func handleInspectStatusAction(ctx context.Context, inst *limatype.Instance, extDriverPath string) (string, error) {
127
cmd := exec.CommandContext(ctx, extDriverPath, "--inspect-status")
128
129
var stderrBuf bytes.Buffer
130
cmd.Stderr = &stderrBuf
131
132
stdin, err := cmd.StdinPipe()
133
if err != nil {
134
return "", err
135
}
136
stdout, err := cmd.StdoutPipe()
137
if err != nil {
138
return "", err
139
}
140
if err := cmd.Start(); err != nil {
141
return "", err
142
}
143
144
encoder := json.NewEncoder(stdin)
145
payload, err := inst.MarshalJSON()
146
if err != nil {
147
return "", fmt.Errorf("failed to marshal instance config: %w", err)
148
}
149
if err := encoder.Encode(payload); err != nil {
150
return "", err
151
}
152
stdin.Close()
153
154
decoder := json.NewDecoder(stdout)
155
var response []byte
156
if err := decoder.Decode(&response); err != nil {
157
return "", err
158
}
159
160
var respInst limatype.Instance
161
if err := respInst.UnmarshalJSON(response); err != nil {
162
return "", fmt.Errorf("failed to unmarshal instance response: %w", err)
163
}
164
165
if err := cmd.Wait(); err != nil {
166
if stderrBuf.Len() > 0 {
167
return "", fmt.Errorf("inspect status command failed: %w; stderr: %s", err, stderrBuf.String())
168
}
169
return "", fmt.Errorf("inspect status command failed: %w", err)
170
}
171
172
if stderrBuf.Len() > 0 {
173
logrus.Debugf("external driver stderr: %s", stderrBuf.String())
174
}
175
176
*inst = respInst
177
logrus.Debugf("Inspecting instance status action completed successfully for %q", extDriverPath)
178
return inst.Status, nil
179
}
180
181