Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
lima-vm
GitHub Repository: lima-vm/lima
Path: blob/master/cmd/limactl/hostagent.go
1645 views
1
// SPDX-FileCopyrightText: Copyright The Lima Authors
2
// SPDX-License-Identifier: Apache-2.0
3
4
package main
5
6
import (
7
"errors"
8
"fmt"
9
"io"
10
"net"
11
"net/http"
12
"os"
13
"os/signal"
14
"runtime"
15
"strconv"
16
"syscall"
17
18
"github.com/sirupsen/logrus"
19
"github.com/spf13/cobra"
20
21
"github.com/lima-vm/lima/v2/pkg/hostagent"
22
"github.com/lima-vm/lima/v2/pkg/hostagent/api/server"
23
)
24
25
func newHostagentCommand() *cobra.Command {
26
hostagentCommand := &cobra.Command{
27
Use: "hostagent INSTANCE",
28
Short: "Run hostagent",
29
Args: WrapArgsError(cobra.ExactArgs(1)),
30
RunE: hostagentAction,
31
Hidden: true,
32
}
33
hostagentCommand.Flags().StringP("pidfile", "p", "", "Write PID to file")
34
hostagentCommand.Flags().String("socket", "", "Path of hostagent socket")
35
hostagentCommand.Flags().Bool("run-gui", false, "Run GUI synchronously within hostagent")
36
hostagentCommand.Flags().String("guestagent", "", "Local file path (not URL) of lima-guestagent.OS-ARCH[.gz]")
37
hostagentCommand.Flags().String("nerdctl-archive", "", "Local file path (not URL) of nerdctl-full-VERSION-GOOS-GOARCH.tar.gz")
38
hostagentCommand.Flags().Bool("progress", false, "Show provision script progress by monitoring cloud-init logs")
39
return hostagentCommand
40
}
41
42
func hostagentAction(cmd *cobra.Command, args []string) error {
43
ctx := cmd.Context()
44
pidfile, err := cmd.Flags().GetString("pidfile")
45
if err != nil {
46
return err
47
}
48
if pidfile != "" {
49
if _, err := os.Stat(pidfile); !errors.Is(err, os.ErrNotExist) {
50
return fmt.Errorf("pidfile %q already exists", pidfile)
51
}
52
if err := os.WriteFile(pidfile, []byte(strconv.Itoa(os.Getpid())+"\n"), 0o644); err != nil {
53
return err
54
}
55
defer os.RemoveAll(pidfile)
56
}
57
socket, err := cmd.Flags().GetString("socket")
58
if err != nil {
59
return err
60
}
61
if socket == "" {
62
return errors.New("socket must be specified (limactl version mismatch?)")
63
}
64
65
instName := args[0]
66
67
runGUI, err := cmd.Flags().GetBool("run-gui")
68
if err != nil {
69
return err
70
}
71
if runGUI {
72
// Without this the call to vz.RunGUI fails. Adding it here, as this has to be called before the vz cgo loads.
73
runtime.LockOSThread()
74
defer runtime.UnlockOSThread()
75
}
76
77
signalCh := make(chan os.Signal, 1)
78
signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)
79
80
stdout := &syncWriter{w: cmd.OutOrStdout()}
81
stderr := &syncWriter{w: cmd.ErrOrStderr()}
82
83
initLogrus(stderr)
84
var opts []hostagent.Opt
85
guestagentBinary, err := cmd.Flags().GetString("guestagent")
86
if err != nil {
87
return err
88
}
89
if guestagentBinary != "" {
90
opts = append(opts, hostagent.WithGuestAgentBinary(guestagentBinary))
91
}
92
nerdctlArchive, err := cmd.Flags().GetString("nerdctl-archive")
93
if err != nil {
94
return err
95
}
96
if nerdctlArchive != "" {
97
opts = append(opts, hostagent.WithNerdctlArchive(nerdctlArchive))
98
}
99
showProgress, err := cmd.Flags().GetBool("progress")
100
if err != nil {
101
return err
102
}
103
if showProgress {
104
opts = append(opts, hostagent.WithCloudInitProgress(showProgress))
105
}
106
ha, err := hostagent.New(ctx, instName, stdout, signalCh, opts...)
107
if err != nil {
108
return err
109
}
110
111
backend := &server.Backend{
112
Agent: ha,
113
}
114
r := http.NewServeMux()
115
server.AddRoutes(r, backend)
116
srv := &http.Server{Handler: r}
117
err = os.RemoveAll(socket)
118
if err != nil {
119
return err
120
}
121
var lc net.ListenConfig
122
l, err := lc.Listen(ctx, "unix", socket)
123
logrus.Infof("hostagent socket created at %s", socket)
124
if err != nil {
125
return err
126
}
127
go func() {
128
if serveErr := srv.Serve(l); serveErr != http.ErrServerClosed {
129
logrus.WithError(serveErr).Warn("hostagent API server exited with an error")
130
}
131
}()
132
defer srv.Close()
133
return ha.Run(cmd.Context())
134
}
135
136
// syncer is implemented by *os.File.
137
type syncer interface {
138
Sync() error
139
}
140
141
type syncWriter struct {
142
w io.Writer
143
}
144
145
func (w *syncWriter) Write(p []byte) (int, error) {
146
written, err := w.w.Write(p)
147
if err == nil {
148
if s, ok := w.w.(syncer); ok {
149
_ = s.Sync()
150
}
151
}
152
return written, err
153
}
154
155
func initLogrus(stderr io.Writer) {
156
logrus.SetOutput(stderr)
157
// JSON logs are parsed in pkg/hostagent/events.Watcher()
158
logrus.SetFormatter(new(logrus.JSONFormatter))
159
// HostAgent logging is one level more verbose than the start command itself
160
if logrus.GetLevel() == logrus.DebugLevel {
161
logrus.SetLevel(logrus.TraceLevel)
162
} else {
163
logrus.SetLevel(logrus.DebugLevel)
164
}
165
}
166
167