Skip to content

Commit e57439b

Browse files
committed
pkg/driver: Add Driver.AdditionalSetupForSSH()
## pkg/driver: Add `Driver.AdditionalSetupForSSH()` AdditionalSetupForSSH provides additional setup required for SSH connection. It is called after the VM is started, before the first SSH connection. It returns an error if the setup fails. ## pkg/driver/vz: Change waiting `waitSSHLocalPortAccessible` in `Driver.AdditionalSetupForSSH()` instead of `startVM`. `waitSSHLocalPortAccessible` is being used for waiting configuration SSH over VSOCK. It makes `LimaVzDriver.Start()` to return as soon as possible. Signed-off-by: Norio Nomura <norio.nomura@gmail.com>
1 parent d936fb0 commit e57439b

File tree

13 files changed

+164
-66
lines changed

13 files changed

+164
-66
lines changed

pkg/driver/driver.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ type Driver interface {
8888
FillConfig(ctx context.Context, cfg *limatype.LimaYAML, filePath string) error
8989

9090
SSHAddress(ctx context.Context) (string, error)
91+
92+
// AdditionalSetupForSSH provides additional setup required for SSH connection.
93+
// It is called after VM is started, before first SSH connection.
94+
// It returns an error if the setup fails.
95+
AdditionalSetupForSSH(ctx context.Context) error
9196
}
9297

9398
type ConfiguredDriver struct {

pkg/driver/external/client/methods.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,3 +323,16 @@ func (d *DriverClient) BootScripts() (map[string][]byte, error) {
323323
d.logger.Debugf("Boot scripts retrieved successfully: %d scripts", len(resp.Scripts))
324324
return resp.Scripts, nil
325325
}
326+
327+
func (d *DriverClient) AdditionalSetupForSSH(ctx context.Context) error {
328+
d.logger.Debug("Performing additional setup for SSH connection")
329+
330+
_, err := d.DriverSvc.AdditionalSetupForSSH(ctx, &emptypb.Empty{})
331+
if err != nil {
332+
d.logger.Errorf("Failed to perform additional setup for SSH: %v", err)
333+
return err
334+
}
335+
336+
d.logger.Debug("Additional setup for SSH completed successfully")
337+
return nil
338+
}

pkg/driver/external/driver.pb.desc

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-

2+

33
driver.protogoogle/protobuf/empty.proto"�
44
BootScriptsResponse;
55
scripts ( 2!.BootScriptsResponse.ScriptsEntryRscripts:
@@ -30,7 +30,7 @@ connection")
3030
ListSnapshotsResponse
3131
snapshots ( R snapshots"B
3232
ForwardGuestAgentResponse%
33-
should_forward (RshouldForward2�
33+
should_forward (RshouldForward2�
3434
Driver:
3535
Validate.google.protobuf.Empty.google.protobuf.Empty8
3636
Create.google.protobuf.Empty.google.protobuf.Empty<
@@ -52,4 +52,5 @@ CreateDisk.google.protobuf.Empty.google.protobuf.Empty1
5252
Configure.SetConfigRequest.google.protobuf.Empty-
5353
Info.google.protobuf.Empty.InfoResponse9
5454

55-
SSHAddress.google.protobuf.Empty.SSHAddressResponseB0Z.github.com/lima-vm/lima/v2/pkg/driver/externalbproto3
55+
SSHAddress.google.protobuf.Empty.SSHAddressResponseG
56+
AdditionalSetupForSSH.google.protobuf.Empty.google.protobuf.EmptyB0Z.github.com/lima-vm/lima/v2/pkg/driver/externalbproto3

pkg/driver/external/driver.pb.go

Lines changed: 26 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/driver/external/driver.proto

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ service Driver {
2828
rpc Configure(SetConfigRequest) returns (google.protobuf.Empty);
2929
rpc Info(google.protobuf.Empty) returns (InfoResponse);
3030
rpc SSHAddress(google.protobuf.Empty) returns (SSHAddressResponse);
31+
32+
// AdditionalSetupForSSH provides additional setup required for SSH connection.
33+
// It is called after VM is started, before first SSH connection.
34+
rpc AdditionalSetupForSSH(google.protobuf.Empty) returns (google.protobuf.Empty);
3135
}
3236

3337
message BootScriptsResponse {

pkg/driver/external/driver_grpc.pb.go

Lines changed: 42 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/driver/external/server/methods.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,3 +284,14 @@ func (s *DriverServer) ForwardGuestAgent(_ context.Context, _ *emptypb.Empty) (*
284284
s.logger.Debug("Received ForwardGuestAgent request")
285285
return &pb.ForwardGuestAgentResponse{ShouldForward: s.driver.ForwardGuestAgent()}, nil
286286
}
287+
288+
func (s *DriverServer) AdditionalSetupForSSH(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) {
289+
s.logger.Debug("Received AdditionalSetupForSSH request")
290+
err := s.driver.AdditionalSetupForSSH(ctx)
291+
if err != nil {
292+
s.logger.Errorf("AdditionalSetupForSSH failed: %v", err)
293+
return &emptypb.Empty{}, err
294+
}
295+
s.logger.Debug("AdditionalSetupForSSH succeeded")
296+
return &emptypb.Empty{}, nil
297+
}

pkg/driver/qemu/qemu_driver.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -720,3 +720,7 @@ func (l *LimaQemuDriver) ForwardGuestAgent() bool {
720720
// if driver is not providing, use host agent
721721
return l.vSockPort == 0 && l.virtioPort == ""
722722
}
723+
724+
func (l *LimaQemuDriver) AdditionalSetupForSSH(_ context.Context) error {
725+
return nil
726+
}

pkg/driver/vz/vm_darwin.go

Lines changed: 37 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -52,28 +52,26 @@ type virtualMachineWrapper struct {
5252
// Hold all *os.File created via socketpair() so that they won't get garbage collected. f.FD() gets invalid if f gets garbage collected.
5353
var vmNetworkFiles = make([]*os.File, 1)
5454

55-
func startVM(ctx context.Context, inst *limatype.Instance, sshLocalPort int) (*virtualMachineWrapper, chan error, error) {
55+
func startVM(ctx context.Context, inst *limatype.Instance, sshLocalPort int) (vm *virtualMachineWrapper, waitSSHLocalPortAccessible <-chan any, errCh chan error, err error) {
5656
usernetClient, stopUsernet, err := startUsernet(ctx, inst)
5757
if err != nil {
58-
return nil, nil, err
58+
return nil, nil, nil, err
5959
}
6060

6161
machine, err := createVM(ctx, inst)
6262
if err != nil {
63-
return nil, nil, err
63+
return nil, nil, nil, err
6464
}
6565

6666
err = machine.Start()
6767
if err != nil {
68-
return nil, nil, err
68+
return nil, nil, nil, err
6969
}
7070

7171
wrapper := &virtualMachineWrapper{VirtualMachine: machine, stopped: false}
72+
notifySSHLocalPortAccessible := make(chan any)
73+
sendErrCh := make(chan error)
7274

73-
errCh := make(chan error)
74-
75-
waitSSHLocalPortAccessible := make(chan struct{})
76-
defer close(waitSSHLocalPortAccessible)
7775
go func() {
7876
// Handle errors via errCh and handle stop vm during context close
7977
defer func() {
@@ -95,42 +93,44 @@ func startVM(ctx context.Context, inst *limatype.Instance, sshLocalPort int) (*v
9593
pidFile := filepath.Join(inst.Dir, filenames.PIDFile(*inst.Config.VMType))
9694
if _, err := os.Stat(pidFile); !errors.Is(err, os.ErrNotExist) {
9795
logrus.Errorf("pidfile %q already exists", pidFile)
98-
errCh <- err
96+
sendErrCh <- err
9997
}
10098
if err := os.WriteFile(pidFile, []byte(strconv.Itoa(os.Getpid())+"\n"), 0o644); err != nil {
10199
logrus.Errorf("error writing to pid fil %q", pidFile)
102-
errCh <- err
100+
sendErrCh <- err
103101
}
104102
logrus.Info("[VZ] - vm state change: running")
105103

106-
usernetSSHLocalPort := sshLocalPort
107-
useSSHOverVsock := true
108-
if envVar := os.Getenv("LIMA_SSH_OVER_VSOCK"); envVar != "" {
109-
b, err := strconv.ParseBool(envVar)
110-
if err != nil {
111-
logrus.WithError(err).Warnf("invalid LIMA_SSH_OVER_VSOCK value %q", envVar)
112-
} else {
113-
useSSHOverVsock = b
104+
go func() {
105+
defer close(notifySSHLocalPortAccessible)
106+
usernetSSHLocalPort := sshLocalPort
107+
useSSHOverVsock := true
108+
if envVar := os.Getenv("LIMA_SSH_OVER_VSOCK"); envVar != "" {
109+
b, err := strconv.ParseBool(envVar)
110+
if err != nil {
111+
logrus.WithError(err).Warnf("invalid LIMA_SSH_OVER_VSOCK value %q", envVar)
112+
} else {
113+
useSSHOverVsock = b
114+
}
114115
}
115-
}
116-
if !useSSHOverVsock {
117-
logrus.Info("LIMA_SSH_OVER_VSOCK is false, skipping detection of SSH server on vsock port")
118-
} else if err := usernetClient.WaitOpeningSSHPort(ctx, inst); err == nil {
119-
hostAddress := net.JoinHostPort(inst.SSHAddress, strconv.Itoa(usernetSSHLocalPort))
120-
if err := wrapper.startVsockForwarder(ctx, 22, hostAddress); err == nil {
121-
logrus.Infof("Detected SSH server is listening on the vsock port; changed %s to proxy for the vsock port", hostAddress)
122-
usernetSSHLocalPort = 0 // disable gvisor ssh port forwarding
116+
if !useSSHOverVsock {
117+
logrus.Info("LIMA_SSH_OVER_VSOCK is false, skipping detection of SSH server on vsock port")
118+
} else if err := usernetClient.WaitOpeningSSHPort(ctx, inst); err == nil {
119+
hostAddress := net.JoinHostPort(inst.SSHAddress, strconv.Itoa(usernetSSHLocalPort))
120+
if err := wrapper.startVsockForwarder(ctx, 22, hostAddress); err == nil {
121+
logrus.Infof("Detected SSH server is listening on the vsock port; changed %s to proxy for the vsock port", hostAddress)
122+
usernetSSHLocalPort = 0 // disable gvisor ssh port forwarding
123+
} else {
124+
logrus.WithError(err).Warn("Failed to detect SSH server on vsock port, falling back to usernet forwarder")
125+
}
123126
} else {
124-
logrus.WithError(err).Warn("Failed to detect SSH server on vsock port, falling back to usernet forwarder")
127+
logrus.WithError(err).Warn("Failed to wait for the guest SSH server to become available, falling back to usernet forwarder")
125128
}
126-
} else {
127-
logrus.WithError(err).Warn("Failed to wait for the guest SSH server to become available, falling back to usernet forwarder")
128-
}
129-
err := usernetClient.ConfigureDriver(ctx, inst, usernetSSHLocalPort)
130-
if err != nil {
131-
errCh <- err
132-
}
133-
waitSSHLocalPortAccessible <- struct{}{}
129+
err := usernetClient.ConfigureDriver(ctx, inst, usernetSSHLocalPort)
130+
if err != nil {
131+
sendErrCh <- err
132+
}
133+
}()
134134
case vz.VirtualMachineStateStopped:
135135
logrus.Info("[VZ] - vm state change: stopped")
136136
wrapper.mu.Lock()
@@ -140,15 +140,14 @@ func startVM(ctx context.Context, inst *limatype.Instance, sshLocalPort int) (*v
140140
if stopUsernet != nil {
141141
stopUsernet()
142142
}
143-
errCh <- errors.New("vz driver state stopped")
143+
sendErrCh <- errors.New("vz driver state stopped")
144144
default:
145145
logrus.Debugf("[VZ] - vm state change: %q", newState)
146146
}
147147
}
148148
}
149149
}()
150-
<-waitSSHLocalPortAccessible
151-
return wrapper, errCh, err
150+
return wrapper, notifySSHLocalPortAccessible, sendErrCh, err
152151
}
153152

154153
func startUsernet(ctx context.Context, inst *limatype.Instance) (*usernet.Client, context.CancelFunc, error) {

0 commit comments

Comments
 (0)