Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pkg/driver/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,11 @@ type Driver interface {
FillConfig(ctx context.Context, cfg *limatype.LimaYAML, filePath string) error

SSHAddress(ctx context.Context) (string, error)

// AdditionalSetupForSSH provides additional setup required for SSH connection.
// It is called after VM is started, before first SSH connection.
// It returns an error if the setup fails.
AdditionalSetupForSSH(ctx context.Context) error
}

type ConfiguredDriver struct {
Expand Down
13 changes: 13 additions & 0 deletions pkg/driver/external/client/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,16 @@ func (d *DriverClient) BootScripts() (map[string][]byte, error) {
d.logger.Debugf("Boot scripts retrieved successfully: %d scripts", len(resp.Scripts))
return resp.Scripts, nil
}

func (d *DriverClient) AdditionalSetupForSSH(ctx context.Context) error {
d.logger.Debug("Performing additional setup for SSH connection")

_, err := d.DriverSvc.AdditionalSetupForSSH(ctx, &emptypb.Empty{})
if err != nil {
d.logger.Errorf("Failed to perform additional setup for SSH: %v", err)
return err
}

d.logger.Debug("Additional setup for SSH completed successfully")
return nil
}
7 changes: 4 additions & 3 deletions pkg/driver/external/driver.pb.desc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

ý
Æ
driver.protogoogle/protobuf/empty.proto"Ž
BootScriptsResponse;
scripts ( 2!.BootScriptsResponse.ScriptsEntryRscripts:
Expand Down Expand Up @@ -30,7 +30,7 @@ connection")
ListSnapshotsResponse
snapshots ( R snapshots"B
ForwardGuestAgentResponse%
should_forward (RshouldForward2©
should_forward (RshouldForward2ò
Driver:
Validate.google.protobuf.Empty.google.protobuf.Empty8
Create.google.protobuf.Empty.google.protobuf.Empty<
Expand All @@ -52,4 +52,5 @@ CreateDisk.google.protobuf.Empty.google.protobuf.Empty1
Configure.SetConfigRequest.google.protobuf.Empty-
Info.google.protobuf.Empty.InfoResponse9

SSHAddress.google.protobuf.Empty.SSHAddressResponseB0Z.github.com/lima-vm/lima/v2/pkg/driver/externalbproto3
SSHAddress.google.protobuf.Empty.SSHAddressResponseG
AdditionalSetupForSSH.google.protobuf.Empty.google.protobuf.EmptyB0Z.github.com/lima-vm/lima/v2/pkg/driver/externalbproto3
Expand Down
49 changes: 26 additions & 23 deletions pkg/driver/external/driver.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pkg/driver/external/driver.proto
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ service Driver {
rpc Configure(SetConfigRequest) returns (google.protobuf.Empty);
rpc Info(google.protobuf.Empty) returns (InfoResponse);
rpc SSHAddress(google.protobuf.Empty) returns (SSHAddressResponse);

// AdditionalSetupForSSH provides additional setup required for SSH connection.
// It is called after VM is started, before first SSH connection.
rpc AdditionalSetupForSSH(google.protobuf.Empty) returns (google.protobuf.Empty);
}

message BootScriptsResponse {
Expand Down
42 changes: 42 additions & 0 deletions pkg/driver/external/driver_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions pkg/driver/external/server/methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,3 +284,14 @@ func (s *DriverServer) ForwardGuestAgent(_ context.Context, _ *emptypb.Empty) (*
s.logger.Debug("Received ForwardGuestAgent request")
return &pb.ForwardGuestAgentResponse{ShouldForward: s.driver.ForwardGuestAgent()}, nil
}

func (s *DriverServer) AdditionalSetupForSSH(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) {
s.logger.Debug("Received AdditionalSetupForSSH request")
err := s.driver.AdditionalSetupForSSH(ctx)
if err != nil {
s.logger.Errorf("AdditionalSetupForSSH failed: %v", err)
return &emptypb.Empty{}, err
}
s.logger.Debug("AdditionalSetupForSSH succeeded")
return &emptypb.Empty{}, nil
}
4 changes: 4 additions & 0 deletions pkg/driver/krunkit/krunkit_driver_darwin_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,7 @@ func (l *LimaKrunkitDriver) Unregister(_ context.Context) error {
func (l *LimaKrunkitDriver) GuestAgentConn(_ context.Context) (net.Conn, string, error) {
return nil, "unix", nil
}

func (l *LimaKrunkitDriver) AdditionalSetupForSSH(_ context.Context) error {
return nil
}
4 changes: 4 additions & 0 deletions pkg/driver/qemu/qemu_driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,3 +720,7 @@ func (l *LimaQemuDriver) ForwardGuestAgent() bool {
// if driver is not providing, use host agent
return l.vSockPort == 0 && l.virtioPort == ""
}

func (l *LimaQemuDriver) AdditionalSetupForSSH(_ context.Context) error {
return nil
}
75 changes: 37 additions & 38 deletions pkg/driver/vz/vm_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,28 +52,26 @@ type virtualMachineWrapper struct {
// Hold all *os.File created via socketpair() so that they won't get garbage collected. f.FD() gets invalid if f gets garbage collected.
var vmNetworkFiles = make([]*os.File, 1)

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

machine, err := createVM(ctx, inst)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}

err = machine.Start()
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}

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

errCh := make(chan error)

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

usernetSSHLocalPort := sshLocalPort
useSSHOverVsock := true
if envVar := os.Getenv("LIMA_SSH_OVER_VSOCK"); envVar != "" {
b, err := strconv.ParseBool(envVar)
if err != nil {
logrus.WithError(err).Warnf("invalid LIMA_SSH_OVER_VSOCK value %q", envVar)
} else {
useSSHOverVsock = b
go func() {
defer close(notifySSHLocalPortAccessible)
usernetSSHLocalPort := sshLocalPort
useSSHOverVsock := true
if envVar := os.Getenv("LIMA_SSH_OVER_VSOCK"); envVar != "" {
b, err := strconv.ParseBool(envVar)
if err != nil {
logrus.WithError(err).Warnf("invalid LIMA_SSH_OVER_VSOCK value %q", envVar)
} else {
useSSHOverVsock = b
}
}
}
if !useSSHOverVsock {
logrus.Info("LIMA_SSH_OVER_VSOCK is false, skipping detection of SSH server on vsock port")
} else if err := usernetClient.WaitOpeningSSHPort(ctx, inst); err == nil {
hostAddress := net.JoinHostPort(inst.SSHAddress, strconv.Itoa(usernetSSHLocalPort))
if err := wrapper.startVsockForwarder(ctx, 22, hostAddress); err == nil {
logrus.Infof("Detected SSH server is listening on the vsock port; changed %s to proxy for the vsock port", hostAddress)
usernetSSHLocalPort = 0 // disable gvisor ssh port forwarding
if !useSSHOverVsock {
logrus.Info("LIMA_SSH_OVER_VSOCK is false, skipping detection of SSH server on vsock port")
} else if err := usernetClient.WaitOpeningSSHPort(ctx, inst); err == nil {
hostAddress := net.JoinHostPort(inst.SSHAddress, strconv.Itoa(usernetSSHLocalPort))
if err := wrapper.startVsockForwarder(ctx, 22, hostAddress); err == nil {
logrus.Infof("Detected SSH server is listening on the vsock port; changed %s to proxy for the vsock port", hostAddress)
usernetSSHLocalPort = 0 // disable gvisor ssh port forwarding
} else {
logrus.WithError(err).Warn("Failed to detect SSH server on vsock port, falling back to usernet forwarder")
}
} else {
logrus.WithError(err).Warn("Failed to detect SSH server on vsock port, falling back to usernet forwarder")
logrus.WithError(err).Warn("Failed to wait for the guest SSH server to become available, falling back to usernet forwarder")
}
} else {
logrus.WithError(err).Warn("Failed to wait for the guest SSH server to become available, falling back to usernet forwarder")
}
err := usernetClient.ConfigureDriver(ctx, inst, usernetSSHLocalPort)
if err != nil {
errCh <- err
}
waitSSHLocalPortAccessible <- struct{}{}
err := usernetClient.ConfigureDriver(ctx, inst, usernetSSHLocalPort)
if err != nil {
sendErrCh <- err
}
}()
case vz.VirtualMachineStateStopped:
logrus.Info("[VZ] - vm state change: stopped")
wrapper.mu.Lock()
Expand All @@ -140,15 +140,14 @@ func startVM(ctx context.Context, inst *limatype.Instance, sshLocalPort int) (*v
if stopUsernet != nil {
stopUsernet()
}
errCh <- errors.New("vz driver state stopped")
sendErrCh <- errors.New("vz driver state stopped")
default:
logrus.Debugf("[VZ] - vm state change: %q", newState)
}
}
}
}()
<-waitSSHLocalPortAccessible
return wrapper, errCh, err
return wrapper, notifySSHLocalPortAccessible, sendErrCh, err
}

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