From 7bd7066415d372ffaf9d7d7e54521ab21be0480a Mon Sep 17 00:00:00 2001 From: Ansuman Sahoo Date: Tue, 27 May 2025 18:21:49 +0530 Subject: [PATCH] external driver plugin system and update Makefile to build exteral bins Signed-off-by: Ansuman Sahoo --- Makefile | 34 +- cmd/lima-driver-qemu/main.go | 14 + cmd/lima-driver-vz/main_darwin.go | 14 + cmd/lima-driver-wsl2/main_windows.go | 14 + cmd/limactl/main.go | 3 + pkg/driver/external/client/client.go | 49 ++ pkg/driver/external/client/methods.go | 309 +++++++++++ pkg/driver/external/driver.pb.go | 625 +++++++++++++++++++++ pkg/driver/external/driver.proto | 74 +++ pkg/driver/external/driver_grpc.pb.go | 772 ++++++++++++++++++++++++++ pkg/driver/external/server/methods.go | 265 +++++++++ pkg/driver/external/server/server.go | 213 +++++++ pkg/driverutil/instance.go | 21 +- pkg/instance/start.go | 3 +- pkg/networks/usernet/client.go | 3 + pkg/registry/registry.go | 34 +- pkg/store/filenames/filenames.go | 67 +-- pkg/store/instance.go | 36 ++ 18 files changed, 2508 insertions(+), 42 deletions(-) create mode 100644 cmd/lima-driver-qemu/main.go create mode 100644 cmd/lima-driver-vz/main_darwin.go create mode 100644 cmd/lima-driver-wsl2/main_windows.go create mode 100644 pkg/driver/external/client/client.go create mode 100644 pkg/driver/external/client/methods.go create mode 100644 pkg/driver/external/driver.pb.go create mode 100644 pkg/driver/external/driver.proto create mode 100644 pkg/driver/external/driver_grpc.pb.go create mode 100644 pkg/driver/external/server/methods.go create mode 100644 pkg/driver/external/server/server.go diff --git a/Makefile b/Makefile index 4015ed391fd..b2035b7532e 100644 --- a/Makefile +++ b/Makefile @@ -243,18 +243,48 @@ endif # calls the native resolver library and not the simplistic version in the Go library. ENVS__output/bin/limactl$(exe) = CGO_ENABLED=1 GOOS="$(GOOS)" GOARCH="$(GOARCH)" CC="$(CC)" +LIMACTL_DRIVER_TAGS := +ifneq (,$(findstring vz,$(ADDITIONAL_DRIVERS))) +LIMACTL_DRIVER_TAGS += external_vz +endif +ifneq (,$(findstring qemu,$(ADDITIONAL_DRIVERS))) +LIMACTL_DRIVER_TAGS += external_qemu +endif +ifneq (,$(findstring wsl2,$(ADDITIONAL_DRIVERS))) +LIMACTL_DRIVER_TAGS += external_wsl2 +endif + +GO_BUILDTAGS ?= +GO_BUILDTAGS_LIMACTL := $(strip $(GO_BUILDTAGS) $(LIMACTL_DRIVER_TAGS)) + _output/bin/limactl$(exe): $(LIMACTL_DEPS) $$(call force_build,$$@) -# If the previous cross-compilation was for GOOS=windows, limactl.exe might still be present. ifneq ($(GOOS),windows) # @rm -rf _output/bin/limactl.exe else @rm -rf _output/bin/limactl endif - $(ENVS_$@) $(GO_BUILD) -o $@ ./cmd/limactl + $(ENVS_$@) $(GO_BUILD) -tags '$(GO_BUILDTAGS_LIMACTL)' -o $@ ./cmd/limactl ifeq ($(GOOS),darwin) codesign -f -v --entitlements vz.entitlements -s - $@ endif +DRIVER_INSTALL_DIR := _output/libexec/lima + +.PHONY: additional-drivers +additional-drivers: + @mkdir -p $(DRIVER_INSTALL_DIR) + @for drv in $(ADDITIONAL_DRIVERS); do \ + echo "Building $$drv as external"; \ + if [ "$(GOOS)" = "windows" ]; then \ + $(GO_BUILD) -o $(DRIVER_INSTALL_DIR)/lima-driver-$$drv.exe ./cmd/lima-driver-$$drv; \ + else \ + $(GO_BUILD) -o $(DRIVER_INSTALL_DIR)/lima-driver-$$drv ./cmd/lima-driver-$$drv; \ + fi; \ + if [ "$$drv" = "vz" ] && [ "$(GOOS)" = "darwin" ]; then \ + codesign -f -v --entitlements vz.entitlements -s - $(DRIVER_INSTALL_DIR)/lima-driver-vz; \ + fi; \ + done + LIMA_CMDS = $(sort lima lima$(bat)) # $(sort ...) deduplicates the list LIMA_DEPS = $(addprefix _output/bin/,$(LIMA_CMDS)) lima: $(LIMA_DEPS) diff --git a/cmd/lima-driver-qemu/main.go b/cmd/lima-driver-qemu/main.go new file mode 100644 index 00000000000..e7ef0e7713e --- /dev/null +++ b/cmd/lima-driver-qemu/main.go @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "github.com/lima-vm/lima/pkg/driver/external/server" + "github.com/lima-vm/lima/pkg/driver/qemu" +) + +// To be used as an external driver for Lima. +func main() { + server.Serve(qemu.New()) +} diff --git a/cmd/lima-driver-vz/main_darwin.go b/cmd/lima-driver-vz/main_darwin.go new file mode 100644 index 00000000000..4b5c7baf35a --- /dev/null +++ b/cmd/lima-driver-vz/main_darwin.go @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "github.com/lima-vm/lima/pkg/driver/external/server" + "github.com/lima-vm/lima/pkg/driver/vz" +) + +// To be used as an external driver for Lima. +func main() { + server.Serve(vz.New()) +} diff --git a/cmd/lima-driver-wsl2/main_windows.go b/cmd/lima-driver-wsl2/main_windows.go new file mode 100644 index 00000000000..867a1e94e14 --- /dev/null +++ b/cmd/lima-driver-wsl2/main_windows.go @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "github.com/lima-vm/lima/pkg/driver/external/server" + "github.com/lima-vm/lima/pkg/driver/wsl2" +) + +// To be used as an external driver for Lima. +func main() { + server.Serve(wsl2.New()) +} diff --git a/cmd/limactl/main.go b/cmd/limactl/main.go index 5b256900be6..d12b7813b65 100644 --- a/cmd/limactl/main.go +++ b/cmd/limactl/main.go @@ -16,6 +16,7 @@ import ( "github.com/spf13/cobra" "github.com/lima-vm/lima/pkg/debugutil" + "github.com/lima-vm/lima/pkg/driver/external/server" "github.com/lima-vm/lima/pkg/fsutil" "github.com/lima-vm/lima/pkg/osutil" "github.com/lima-vm/lima/pkg/store/dirnames" @@ -43,6 +44,8 @@ func main() { handleExitCoder(err) logrus.Fatal(err) } + + server.StopAllExternalDrivers() } func newApp() *cobra.Command { diff --git a/pkg/driver/external/client/client.go b/pkg/driver/external/client/client.go new file mode 100644 index 00000000000..ea4420d894a --- /dev/null +++ b/pkg/driver/external/client/client.go @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package client + +import ( + "context" + "net" + + "github.com/sirupsen/logrus" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + pb "github.com/lima-vm/lima/pkg/driver/external" +) + +type DriverClient struct { + socketPath string + Conn *grpc.ClientConn + DriverSvc pb.DriverClient + logger *logrus.Logger +} + +func NewDriverClient(socketPath string, logger *logrus.Logger) (*DriverClient, error) { + opts := []grpc.DialOption{ + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(16 << 20)), + grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(16 << 20)), + grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) { + return net.Dial("unix", socketPath) + }), + grpc.WithTransportCredentials(insecure.NewCredentials()), + } + + //nolint:staticcheck // grpc.Dial is used for compatibility reasons + conn, err := grpc.Dial("unix://"+socketPath, opts...) + if err != nil { + logger.Errorf("failed to dial gRPC driver client connection: %v", err) + return nil, err + } + + driverSvc := pb.NewDriverClient(conn) + + return &DriverClient{ + socketPath: socketPath, + Conn: conn, + DriverSvc: driverSvc, + logger: logger, + }, nil +} diff --git a/pkg/driver/external/client/methods.go b/pkg/driver/external/client/methods.go new file mode 100644 index 00000000000..c2269d82b01 --- /dev/null +++ b/pkg/driver/external/client/methods.go @@ -0,0 +1,309 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package client + +import ( + "context" + "encoding/json" + "errors" + "net" + "time" + + "google.golang.org/protobuf/types/known/emptypb" + + "github.com/lima-vm/lima/pkg/driver" + pb "github.com/lima-vm/lima/pkg/driver/external" + "github.com/lima-vm/lima/pkg/store" +) + +func (d *DriverClient) Validate() error { + d.logger.Debug("Validating driver for the given config") + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + _, err := d.DriverSvc.Validate(ctx, &emptypb.Empty{}) + if err != nil { + d.logger.Errorf("Validation failed: %v", err) + return err + } + + d.logger.Debug("Driver validated successfully") + return nil +} + +func (d *DriverClient) Initialize(ctx context.Context) error { + d.logger.Debug("Initializing driver instance") + + _, err := d.DriverSvc.Initialize(ctx, &emptypb.Empty{}) + if err != nil { + d.logger.Errorf("Initialization failed: %v", err) + return err + } + + d.logger.Debug("Driver instance initialized successfully") + return nil +} + +func (d *DriverClient) CreateDisk(ctx context.Context) error { + d.logger.Debug("Creating disk for the instance") + + _, err := d.DriverSvc.CreateDisk(ctx, &emptypb.Empty{}) + if err != nil { + d.logger.Errorf("Disk creation failed: %v", err) + return err + } + + d.logger.Debug("Disk created successfully") + return nil +} + +func (d *DriverClient) Start(ctx context.Context) (chan error, error) { + d.logger.Debug("Starting driver instance") + + stream, err := d.DriverSvc.Start(ctx, &emptypb.Empty{}) + if err != nil { + d.logger.Errorf("Failed to start driver instance: %v", err) + return nil, err + } + + errCh := make(chan error, 1) + go func() { + for { + errorStream, err := stream.Recv() + if err != nil { + d.logger.Errorf("Error receiving response from driver: %v", err) + return + } + d.logger.Debugf("Received response: %v", errorStream) + if !errorStream.Success { + errCh <- errors.New(errorStream.Error) + } else { + errCh <- nil + return + } + } + }() + + d.logger.Debug("Driver instance started successfully") + return errCh, nil +} + +func (d *DriverClient) Stop(ctx context.Context) error { + d.logger.Debug("Stopping driver instance") + + connCtx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + _, err := d.DriverSvc.Stop(connCtx, &emptypb.Empty{}) + if err != nil { + d.logger.Errorf("Failed to stop driver instance: %v", err) + return err + } + + d.logger.Debug("Driver instance stopped successfully") + return nil +} + +func (d *DriverClient) RunGUI() error { + d.logger.Debug("Running GUI for the driver instance") + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + _, err := d.DriverSvc.RunGUI(ctx, &emptypb.Empty{}) + if err != nil { + d.logger.Errorf("Failed to run GUI: %v", err) + return err + } + + d.logger.Debug("GUI started successfully") + return nil +} + +func (d *DriverClient) ChangeDisplayPassword(ctx context.Context, password string) error { + d.logger.Debug("Changing display password for the driver instance") + + _, err := d.DriverSvc.ChangeDisplayPassword(ctx, &pb.ChangeDisplayPasswordRequest{ + Password: password, + }) + if err != nil { + d.logger.Errorf("Failed to change display password: %v", err) + return err + } + + d.logger.Debug("Display password changed successfully") + return nil +} + +func (d *DriverClient) DisplayConnection(ctx context.Context) (string, error) { + d.logger.Debug("Getting display connection for the driver instance") + + resp, err := d.DriverSvc.GetDisplayConnection(ctx, &emptypb.Empty{}) + if err != nil { + d.logger.Errorf("Failed to get display connection: %v", err) + return "", err + } + + d.logger.Debugf("Display connection retrieved: %s", resp.Connection) + return resp.Connection, nil +} + +func (d *DriverClient) CreateSnapshot(ctx context.Context, tag string) error { + d.logger.Debugf("Creating snapshot with tag: %s", tag) + + _, err := d.DriverSvc.CreateSnapshot(ctx, &pb.CreateSnapshotRequest{ + Tag: tag, + }) + if err != nil { + d.logger.Errorf("Failed to create snapshot: %v", err) + return err + } + + d.logger.Debugf("Snapshot '%s' created successfully", tag) + return nil +} + +func (d *DriverClient) ApplySnapshot(ctx context.Context, tag string) error { + d.logger.Debugf("Applying snapshot with tag: %s", tag) + + _, err := d.DriverSvc.ApplySnapshot(ctx, &pb.ApplySnapshotRequest{ + Tag: tag, + }) + if err != nil { + d.logger.Errorf("Failed to apply snapshot: %v", err) + return err + } + + d.logger.Debugf("Snapshot '%s' applied successfully", tag) + return nil +} + +func (d *DriverClient) DeleteSnapshot(ctx context.Context, tag string) error { + d.logger.Debugf("Deleting snapshot with tag: %s", tag) + + _, err := d.DriverSvc.DeleteSnapshot(ctx, &pb.DeleteSnapshotRequest{ + Tag: tag, + }) + if err != nil { + d.logger.Errorf("Failed to delete snapshot: %v", err) + return err + } + + d.logger.Debugf("Snapshot '%s' deleted successfully", tag) + return nil +} + +func (d *DriverClient) ListSnapshots(ctx context.Context) (string, error) { + d.logger.Debug("Listing snapshots") + + resp, err := d.DriverSvc.ListSnapshots(ctx, &emptypb.Empty{}) + if err != nil { + d.logger.Errorf("Failed to list snapshots: %v", err) + return "", err + } + + d.logger.Debugf("Snapshots listed successfully: %s", resp.Snapshots) + return resp.Snapshots, nil +} + +func (d *DriverClient) Register(ctx context.Context) error { + d.logger.Debug("Registering driver instance") + + _, err := d.DriverSvc.Register(ctx, &emptypb.Empty{}) + if err != nil { + d.logger.Errorf("Failed to register driver instance: %v", err) + return err + } + + d.logger.Debug("Driver instance registered successfully") + return nil +} + +func (d *DriverClient) Unregister(ctx context.Context) error { + d.logger.Debug("Unregistering driver instance") + + _, err := d.DriverSvc.Unregister(ctx, &emptypb.Empty{}) + if err != nil { + d.logger.Errorf("Failed to unregister driver instance: %v", err) + return err + } + + d.logger.Debug("Driver instance unregistered successfully") + return nil +} + +func (d *DriverClient) ForwardGuestAgent() bool { + d.logger.Debug("Checking if guest agent needs to be forwarded") + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + resp, err := d.DriverSvc.ForwardGuestAgent(ctx, &emptypb.Empty{}) + if err != nil { + d.logger.Errorf("Failed to check guest agent forwarding: %v", err) + return false + } + + return resp.ShouldForward +} + +func (d *DriverClient) GuestAgentConn(ctx context.Context) (net.Conn, string, error) { + d.logger.Info("Getting guest agent connection") + _, err := d.DriverSvc.GuestAgentConn(ctx, &emptypb.Empty{}) + if err != nil { + d.logger.Errorf("Failed to get guest agent connection: %v", err) + return nil, "", err + } + + return nil, "", nil +} + +func (d *DriverClient) Info() driver.Info { + d.logger.Debug("Getting driver info") + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + resp, err := d.DriverSvc.GetInfo(ctx, &emptypb.Empty{}) + if err != nil { + d.logger.Errorf("Failed to get driver info: %v", err) + return driver.Info{} + } + + var info driver.Info + if err := json.Unmarshal(resp.InfoJson, &info); err != nil { + d.logger.Errorf("Failed to unmarshal driver info: %v", err) + return driver.Info{} + } + + d.logger.Debugf("Driver info retrieved: %+v", info) + return info +} + +func (d *DriverClient) Configure(inst *store.Instance, sshLocalPort int) *driver.ConfiguredDriver { + d.logger.Debugf("Setting config for instance %s with SSH local port %d", inst.Name, sshLocalPort) + + instJSON, err := inst.MarshalJSON() + if err != nil { + d.logger.Errorf("Failed to marshal instance config: %v", err) + return nil + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + _, err = d.DriverSvc.SetConfig(ctx, &pb.SetConfigRequest{ + InstanceConfigJson: instJSON, + SshLocalPort: int64(sshLocalPort), + }) + if err != nil { + d.logger.Errorf("Failed to set config: %v", err) + return nil + } + + d.logger.Debugf("Config set successfully for instance %s", inst.Name) + return &driver.ConfiguredDriver{ + Driver: d, + } +} diff --git a/pkg/driver/external/driver.pb.go b/pkg/driver/external/driver.pb.go new file mode 100644 index 00000000000..29719a4a2ec --- /dev/null +++ b/pkg/driver/external/driver.pb.go @@ -0,0 +1,625 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.6 +// protoc v5.29.3 +// source: pkg/driver/external/driver.proto + +package external + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type InfoResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + InfoJson []byte `protobuf:"bytes,1,opt,name=info_json,json=infoJson,proto3" json:"info_json,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InfoResponse) Reset() { + *x = InfoResponse{} + mi := &file_pkg_driver_external_driver_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InfoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InfoResponse) ProtoMessage() {} + +func (x *InfoResponse) ProtoReflect() protoreflect.Message { + mi := &file_pkg_driver_external_driver_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InfoResponse.ProtoReflect.Descriptor instead. +func (*InfoResponse) Descriptor() ([]byte, []int) { + return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{0} +} + +func (x *InfoResponse) GetInfoJson() []byte { + if x != nil { + return x.InfoJson + } + return nil +} + +type StartResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StartResponse) Reset() { + *x = StartResponse{} + mi := &file_pkg_driver_external_driver_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StartResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartResponse) ProtoMessage() {} + +func (x *StartResponse) ProtoReflect() protoreflect.Message { + mi := &file_pkg_driver_external_driver_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartResponse.ProtoReflect.Descriptor instead. +func (*StartResponse) Descriptor() ([]byte, []int) { + return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{1} +} + +func (x *StartResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *StartResponse) GetError() string { + if x != nil { + return x.Error + } + return "" +} + +type SetConfigRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + InstanceConfigJson []byte `protobuf:"bytes,1,opt,name=instance_config_json,json=instanceConfigJson,proto3" json:"instance_config_json,omitempty"` + SshLocalPort int64 `protobuf:"varint,3,opt,name=ssh_local_port,json=sshLocalPort,proto3" json:"ssh_local_port,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SetConfigRequest) Reset() { + *x = SetConfigRequest{} + mi := &file_pkg_driver_external_driver_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SetConfigRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SetConfigRequest) ProtoMessage() {} + +func (x *SetConfigRequest) ProtoReflect() protoreflect.Message { + mi := &file_pkg_driver_external_driver_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SetConfigRequest.ProtoReflect.Descriptor instead. +func (*SetConfigRequest) Descriptor() ([]byte, []int) { + return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{2} +} + +func (x *SetConfigRequest) GetInstanceConfigJson() []byte { + if x != nil { + return x.InstanceConfigJson + } + return nil +} + +func (x *SetConfigRequest) GetSshLocalPort() int64 { + if x != nil { + return x.SshLocalPort + } + return 0 +} + +type ChangeDisplayPasswordRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Password string `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ChangeDisplayPasswordRequest) Reset() { + *x = ChangeDisplayPasswordRequest{} + mi := &file_pkg_driver_external_driver_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ChangeDisplayPasswordRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ChangeDisplayPasswordRequest) ProtoMessage() {} + +func (x *ChangeDisplayPasswordRequest) ProtoReflect() protoreflect.Message { + mi := &file_pkg_driver_external_driver_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ChangeDisplayPasswordRequest.ProtoReflect.Descriptor instead. +func (*ChangeDisplayPasswordRequest) Descriptor() ([]byte, []int) { + return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{3} +} + +func (x *ChangeDisplayPasswordRequest) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +type GetDisplayConnectionResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Connection string `protobuf:"bytes,1,opt,name=connection,proto3" json:"connection,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetDisplayConnectionResponse) Reset() { + *x = GetDisplayConnectionResponse{} + mi := &file_pkg_driver_external_driver_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetDisplayConnectionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetDisplayConnectionResponse) ProtoMessage() {} + +func (x *GetDisplayConnectionResponse) ProtoReflect() protoreflect.Message { + mi := &file_pkg_driver_external_driver_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetDisplayConnectionResponse.ProtoReflect.Descriptor instead. +func (*GetDisplayConnectionResponse) Descriptor() ([]byte, []int) { + return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{4} +} + +func (x *GetDisplayConnectionResponse) GetConnection() string { + if x != nil { + return x.Connection + } + return "" +} + +type CreateSnapshotRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateSnapshotRequest) Reset() { + *x = CreateSnapshotRequest{} + mi := &file_pkg_driver_external_driver_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateSnapshotRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateSnapshotRequest) ProtoMessage() {} + +func (x *CreateSnapshotRequest) ProtoReflect() protoreflect.Message { + mi := &file_pkg_driver_external_driver_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateSnapshotRequest.ProtoReflect.Descriptor instead. +func (*CreateSnapshotRequest) Descriptor() ([]byte, []int) { + return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{5} +} + +func (x *CreateSnapshotRequest) GetTag() string { + if x != nil { + return x.Tag + } + return "" +} + +type ApplySnapshotRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ApplySnapshotRequest) Reset() { + *x = ApplySnapshotRequest{} + mi := &file_pkg_driver_external_driver_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ApplySnapshotRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ApplySnapshotRequest) ProtoMessage() {} + +func (x *ApplySnapshotRequest) ProtoReflect() protoreflect.Message { + mi := &file_pkg_driver_external_driver_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ApplySnapshotRequest.ProtoReflect.Descriptor instead. +func (*ApplySnapshotRequest) Descriptor() ([]byte, []int) { + return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{6} +} + +func (x *ApplySnapshotRequest) GetTag() string { + if x != nil { + return x.Tag + } + return "" +} + +type DeleteSnapshotRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteSnapshotRequest) Reset() { + *x = DeleteSnapshotRequest{} + mi := &file_pkg_driver_external_driver_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteSnapshotRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteSnapshotRequest) ProtoMessage() {} + +func (x *DeleteSnapshotRequest) ProtoReflect() protoreflect.Message { + mi := &file_pkg_driver_external_driver_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteSnapshotRequest.ProtoReflect.Descriptor instead. +func (*DeleteSnapshotRequest) Descriptor() ([]byte, []int) { + return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{7} +} + +func (x *DeleteSnapshotRequest) GetTag() string { + if x != nil { + return x.Tag + } + return "" +} + +type ListSnapshotsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Snapshots string `protobuf:"bytes,1,opt,name=snapshots,proto3" json:"snapshots,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListSnapshotsResponse) Reset() { + *x = ListSnapshotsResponse{} + mi := &file_pkg_driver_external_driver_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListSnapshotsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListSnapshotsResponse) ProtoMessage() {} + +func (x *ListSnapshotsResponse) ProtoReflect() protoreflect.Message { + mi := &file_pkg_driver_external_driver_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListSnapshotsResponse.ProtoReflect.Descriptor instead. +func (*ListSnapshotsResponse) Descriptor() ([]byte, []int) { + return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{8} +} + +func (x *ListSnapshotsResponse) GetSnapshots() string { + if x != nil { + return x.Snapshots + } + return "" +} + +type ForwardGuestAgentResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + ShouldForward bool `protobuf:"varint,1,opt,name=should_forward,json=shouldForward,proto3" json:"should_forward,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ForwardGuestAgentResponse) Reset() { + *x = ForwardGuestAgentResponse{} + mi := &file_pkg_driver_external_driver_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ForwardGuestAgentResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ForwardGuestAgentResponse) ProtoMessage() {} + +func (x *ForwardGuestAgentResponse) ProtoReflect() protoreflect.Message { + mi := &file_pkg_driver_external_driver_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ForwardGuestAgentResponse.ProtoReflect.Descriptor instead. +func (*ForwardGuestAgentResponse) Descriptor() ([]byte, []int) { + return file_pkg_driver_external_driver_proto_rawDescGZIP(), []int{9} +} + +func (x *ForwardGuestAgentResponse) GetShouldForward() bool { + if x != nil { + return x.ShouldForward + } + return false +} + +var File_pkg_driver_external_driver_proto protoreflect.FileDescriptor + +const file_pkg_driver_external_driver_proto_rawDesc = "" + + "\n" + + " pkg/driver/external/driver.proto\x1a\x1bgoogle/protobuf/empty.proto\"+\n" + + "\fInfoResponse\x12\x1b\n" + + "\tinfo_json\x18\x01 \x01(\fR\binfoJson\"?\n" + + "\rStartResponse\x12\x18\n" + + "\asuccess\x18\x01 \x01(\bR\asuccess\x12\x14\n" + + "\x05error\x18\x02 \x01(\tR\x05error\"j\n" + + "\x10SetConfigRequest\x120\n" + + "\x14instance_config_json\x18\x01 \x01(\fR\x12instanceConfigJson\x12$\n" + + "\x0essh_local_port\x18\x03 \x01(\x03R\fsshLocalPort\":\n" + + "\x1cChangeDisplayPasswordRequest\x12\x1a\n" + + "\bpassword\x18\x01 \x01(\tR\bpassword\">\n" + + "\x1cGetDisplayConnectionResponse\x12\x1e\n" + + "\n" + + "connection\x18\x01 \x01(\tR\n" + + "connection\")\n" + + "\x15CreateSnapshotRequest\x12\x10\n" + + "\x03tag\x18\x01 \x01(\tR\x03tag\"(\n" + + "\x14ApplySnapshotRequest\x12\x10\n" + + "\x03tag\x18\x01 \x01(\tR\x03tag\")\n" + + "\x15DeleteSnapshotRequest\x12\x10\n" + + "\x03tag\x18\x01 \x01(\tR\x03tag\"5\n" + + "\x15ListSnapshotsResponse\x12\x1c\n" + + "\tsnapshots\x18\x01 \x01(\tR\tsnapshots\"B\n" + + "\x19ForwardGuestAgentResponse\x12%\n" + + "\x0eshould_forward\x18\x01 \x01(\bR\rshouldForward2\xf8\b\n" + + "\x06Driver\x12:\n" + + "\bValidate\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12<\n" + + "\n" + + "Initialize\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12<\n" + + "\n" + + "CreateDisk\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x121\n" + + "\x05Start\x12\x16.google.protobuf.Empty\x1a\x0e.StartResponse0\x01\x126\n" + + "\x04Stop\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x128\n" + + "\x06RunGUI\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12N\n" + + "\x15ChangeDisplayPassword\x12\x1d.ChangeDisplayPasswordRequest\x1a\x16.google.protobuf.Empty\x12M\n" + + "\x14GetDisplayConnection\x12\x16.google.protobuf.Empty\x1a\x1d.GetDisplayConnectionResponse\x12@\n" + + "\x0eCreateSnapshot\x12\x16.CreateSnapshotRequest\x1a\x16.google.protobuf.Empty\x12>\n" + + "\rApplySnapshot\x12\x15.ApplySnapshotRequest\x1a\x16.google.protobuf.Empty\x12@\n" + + "\x0eDeleteSnapshot\x12\x16.DeleteSnapshotRequest\x1a\x16.google.protobuf.Empty\x12?\n" + + "\rListSnapshots\x12\x16.google.protobuf.Empty\x1a\x16.ListSnapshotsResponse\x12:\n" + + "\bRegister\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12<\n" + + "\n" + + "Unregister\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x12G\n" + + "\x11ForwardGuestAgent\x12\x16.google.protobuf.Empty\x1a\x1a.ForwardGuestAgentResponse\x12@\n" + + "\x0eGuestAgentConn\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\x126\n" + + "\tSetConfig\x12\x11.SetConfigRequest\x1a\x16.google.protobuf.Empty\x120\n" + + "\aGetInfo\x12\x16.google.protobuf.Empty\x1a\r.InfoResponseB-Z+github.com/lima-vm/lima/pkg/driver/externalb\x06proto3" + +var ( + file_pkg_driver_external_driver_proto_rawDescOnce sync.Once + file_pkg_driver_external_driver_proto_rawDescData []byte +) + +func file_pkg_driver_external_driver_proto_rawDescGZIP() []byte { + file_pkg_driver_external_driver_proto_rawDescOnce.Do(func() { + file_pkg_driver_external_driver_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_pkg_driver_external_driver_proto_rawDesc), len(file_pkg_driver_external_driver_proto_rawDesc))) + }) + return file_pkg_driver_external_driver_proto_rawDescData +} + +var file_pkg_driver_external_driver_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_pkg_driver_external_driver_proto_goTypes = []any{ + (*InfoResponse)(nil), // 0: InfoResponse + (*StartResponse)(nil), // 1: StartResponse + (*SetConfigRequest)(nil), // 2: SetConfigRequest + (*ChangeDisplayPasswordRequest)(nil), // 3: ChangeDisplayPasswordRequest + (*GetDisplayConnectionResponse)(nil), // 4: GetDisplayConnectionResponse + (*CreateSnapshotRequest)(nil), // 5: CreateSnapshotRequest + (*ApplySnapshotRequest)(nil), // 6: ApplySnapshotRequest + (*DeleteSnapshotRequest)(nil), // 7: DeleteSnapshotRequest + (*ListSnapshotsResponse)(nil), // 8: ListSnapshotsResponse + (*ForwardGuestAgentResponse)(nil), // 9: ForwardGuestAgentResponse + (*emptypb.Empty)(nil), // 10: google.protobuf.Empty +} +var file_pkg_driver_external_driver_proto_depIdxs = []int32{ + 10, // 0: Driver.Validate:input_type -> google.protobuf.Empty + 10, // 1: Driver.Initialize:input_type -> google.protobuf.Empty + 10, // 2: Driver.CreateDisk:input_type -> google.protobuf.Empty + 10, // 3: Driver.Start:input_type -> google.protobuf.Empty + 10, // 4: Driver.Stop:input_type -> google.protobuf.Empty + 10, // 5: Driver.RunGUI:input_type -> google.protobuf.Empty + 3, // 6: Driver.ChangeDisplayPassword:input_type -> ChangeDisplayPasswordRequest + 10, // 7: Driver.GetDisplayConnection:input_type -> google.protobuf.Empty + 5, // 8: Driver.CreateSnapshot:input_type -> CreateSnapshotRequest + 6, // 9: Driver.ApplySnapshot:input_type -> ApplySnapshotRequest + 7, // 10: Driver.DeleteSnapshot:input_type -> DeleteSnapshotRequest + 10, // 11: Driver.ListSnapshots:input_type -> google.protobuf.Empty + 10, // 12: Driver.Register:input_type -> google.protobuf.Empty + 10, // 13: Driver.Unregister:input_type -> google.protobuf.Empty + 10, // 14: Driver.ForwardGuestAgent:input_type -> google.protobuf.Empty + 10, // 15: Driver.GuestAgentConn:input_type -> google.protobuf.Empty + 2, // 16: Driver.SetConfig:input_type -> SetConfigRequest + 10, // 17: Driver.GetInfo:input_type -> google.protobuf.Empty + 10, // 18: Driver.Validate:output_type -> google.protobuf.Empty + 10, // 19: Driver.Initialize:output_type -> google.protobuf.Empty + 10, // 20: Driver.CreateDisk:output_type -> google.protobuf.Empty + 1, // 21: Driver.Start:output_type -> StartResponse + 10, // 22: Driver.Stop:output_type -> google.protobuf.Empty + 10, // 23: Driver.RunGUI:output_type -> google.protobuf.Empty + 10, // 24: Driver.ChangeDisplayPassword:output_type -> google.protobuf.Empty + 4, // 25: Driver.GetDisplayConnection:output_type -> GetDisplayConnectionResponse + 10, // 26: Driver.CreateSnapshot:output_type -> google.protobuf.Empty + 10, // 27: Driver.ApplySnapshot:output_type -> google.protobuf.Empty + 10, // 28: Driver.DeleteSnapshot:output_type -> google.protobuf.Empty + 8, // 29: Driver.ListSnapshots:output_type -> ListSnapshotsResponse + 10, // 30: Driver.Register:output_type -> google.protobuf.Empty + 10, // 31: Driver.Unregister:output_type -> google.protobuf.Empty + 9, // 32: Driver.ForwardGuestAgent:output_type -> ForwardGuestAgentResponse + 10, // 33: Driver.GuestAgentConn:output_type -> google.protobuf.Empty + 10, // 34: Driver.SetConfig:output_type -> google.protobuf.Empty + 0, // 35: Driver.GetInfo:output_type -> InfoResponse + 18, // [18:36] is the sub-list for method output_type + 0, // [0:18] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_pkg_driver_external_driver_proto_init() } +func file_pkg_driver_external_driver_proto_init() { + if File_pkg_driver_external_driver_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_pkg_driver_external_driver_proto_rawDesc), len(file_pkg_driver_external_driver_proto_rawDesc)), + NumEnums: 0, + NumMessages: 10, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_pkg_driver_external_driver_proto_goTypes, + DependencyIndexes: file_pkg_driver_external_driver_proto_depIdxs, + MessageInfos: file_pkg_driver_external_driver_proto_msgTypes, + }.Build() + File_pkg_driver_external_driver_proto = out.File + file_pkg_driver_external_driver_proto_goTypes = nil + file_pkg_driver_external_driver_proto_depIdxs = nil +} diff --git a/pkg/driver/external/driver.proto b/pkg/driver/external/driver.proto new file mode 100644 index 00000000000..82c77feec64 --- /dev/null +++ b/pkg/driver/external/driver.proto @@ -0,0 +1,74 @@ +syntax = "proto3"; + +option go_package = "github.com/lima-vm/lima/pkg/driver/external"; + +import "google/protobuf/empty.proto"; + +service Driver { + rpc Validate(google.protobuf.Empty) returns (google.protobuf.Empty); + rpc Initialize(google.protobuf.Empty) returns (google.protobuf.Empty); + rpc CreateDisk(google.protobuf.Empty) returns (google.protobuf.Empty); + rpc Start(google.protobuf.Empty) returns (stream StartResponse); + rpc Stop(google.protobuf.Empty) returns (google.protobuf.Empty); + + rpc RunGUI(google.protobuf.Empty) returns (google.protobuf.Empty); + rpc ChangeDisplayPassword(ChangeDisplayPasswordRequest) returns (google.protobuf.Empty); + rpc GetDisplayConnection(google.protobuf.Empty) returns (GetDisplayConnectionResponse); + + rpc CreateSnapshot(CreateSnapshotRequest) returns (google.protobuf.Empty); + rpc ApplySnapshot(ApplySnapshotRequest) returns (google.protobuf.Empty); + rpc DeleteSnapshot(DeleteSnapshotRequest) returns (google.protobuf.Empty); + rpc ListSnapshots(google.protobuf.Empty) returns (ListSnapshotsResponse); + + rpc Register(google.protobuf.Empty) returns (google.protobuf.Empty); + rpc Unregister(google.protobuf.Empty) returns (google.protobuf.Empty); + + rpc ForwardGuestAgent(google.protobuf.Empty) returns (ForwardGuestAgentResponse); + rpc GuestAgentConn(google.protobuf.Empty) returns (google.protobuf.Empty); + + rpc SetConfig(SetConfigRequest) returns (google.protobuf.Empty); + + rpc GetInfo(google.protobuf.Empty) returns (InfoResponse); +} + +message InfoResponse{ + bytes info_json = 1; +} + +message StartResponse { + bool success = 1; + string error = 2; +} + +message SetConfigRequest { + bytes instance_config_json = 1; + int64 ssh_local_port = 3; +} + +message ChangeDisplayPasswordRequest { + string password = 1; +} + +message GetDisplayConnectionResponse { + string connection = 1; +} + +message CreateSnapshotRequest { + string tag = 1; +} + +message ApplySnapshotRequest { + string tag = 1; +} + +message DeleteSnapshotRequest { + string tag = 1; +} + +message ListSnapshotsResponse { + string snapshots = 1; +} + +message ForwardGuestAgentResponse { + bool should_forward = 1; +} \ No newline at end of file diff --git a/pkg/driver/external/driver_grpc.pb.go b/pkg/driver/external/driver_grpc.pb.go new file mode 100644 index 00000000000..5e1dcde983c --- /dev/null +++ b/pkg/driver/external/driver_grpc.pb.go @@ -0,0 +1,772 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v5.29.3 +// source: pkg/driver/external/driver.proto + +package external + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + Driver_Validate_FullMethodName = "/Driver/Validate" + Driver_Initialize_FullMethodName = "/Driver/Initialize" + Driver_CreateDisk_FullMethodName = "/Driver/CreateDisk" + Driver_Start_FullMethodName = "/Driver/Start" + Driver_Stop_FullMethodName = "/Driver/Stop" + Driver_RunGUI_FullMethodName = "/Driver/RunGUI" + Driver_ChangeDisplayPassword_FullMethodName = "/Driver/ChangeDisplayPassword" + Driver_GetDisplayConnection_FullMethodName = "/Driver/GetDisplayConnection" + Driver_CreateSnapshot_FullMethodName = "/Driver/CreateSnapshot" + Driver_ApplySnapshot_FullMethodName = "/Driver/ApplySnapshot" + Driver_DeleteSnapshot_FullMethodName = "/Driver/DeleteSnapshot" + Driver_ListSnapshots_FullMethodName = "/Driver/ListSnapshots" + Driver_Register_FullMethodName = "/Driver/Register" + Driver_Unregister_FullMethodName = "/Driver/Unregister" + Driver_ForwardGuestAgent_FullMethodName = "/Driver/ForwardGuestAgent" + Driver_GuestAgentConn_FullMethodName = "/Driver/GuestAgentConn" + Driver_SetConfig_FullMethodName = "/Driver/SetConfig" + Driver_GetInfo_FullMethodName = "/Driver/GetInfo" +) + +// DriverClient is the client API for Driver service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type DriverClient interface { + Validate(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) + Initialize(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) + CreateDisk(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) + Start(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[StartResponse], error) + Stop(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) + RunGUI(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) + ChangeDisplayPassword(ctx context.Context, in *ChangeDisplayPasswordRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + GetDisplayConnection(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetDisplayConnectionResponse, error) + CreateSnapshot(ctx context.Context, in *CreateSnapshotRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + ApplySnapshot(ctx context.Context, in *ApplySnapshotRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + DeleteSnapshot(ctx context.Context, in *DeleteSnapshotRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + ListSnapshots(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ListSnapshotsResponse, error) + Register(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) + Unregister(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) + ForwardGuestAgent(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ForwardGuestAgentResponse, error) + GuestAgentConn(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) + SetConfig(ctx context.Context, in *SetConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) + GetInfo(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*InfoResponse, error) +} + +type driverClient struct { + cc grpc.ClientConnInterface +} + +func NewDriverClient(cc grpc.ClientConnInterface) DriverClient { + return &driverClient{cc} +} + +func (c *driverClient) Validate(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Driver_Validate_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) Initialize(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Driver_Initialize_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) CreateDisk(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Driver_CreateDisk_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) Start(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[StartResponse], error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + stream, err := c.cc.NewStream(ctx, &Driver_ServiceDesc.Streams[0], Driver_Start_FullMethodName, cOpts...) + if err != nil { + return nil, err + } + x := &grpc.GenericClientStream[emptypb.Empty, StartResponse]{ClientStream: stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Driver_StartClient = grpc.ServerStreamingClient[StartResponse] + +func (c *driverClient) Stop(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Driver_Stop_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) RunGUI(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Driver_RunGUI_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) ChangeDisplayPassword(ctx context.Context, in *ChangeDisplayPasswordRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Driver_ChangeDisplayPassword_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) GetDisplayConnection(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*GetDisplayConnectionResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetDisplayConnectionResponse) + err := c.cc.Invoke(ctx, Driver_GetDisplayConnection_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) CreateSnapshot(ctx context.Context, in *CreateSnapshotRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Driver_CreateSnapshot_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) ApplySnapshot(ctx context.Context, in *ApplySnapshotRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Driver_ApplySnapshot_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) DeleteSnapshot(ctx context.Context, in *DeleteSnapshotRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Driver_DeleteSnapshot_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) ListSnapshots(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ListSnapshotsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListSnapshotsResponse) + err := c.cc.Invoke(ctx, Driver_ListSnapshots_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) Register(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Driver_Register_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) Unregister(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Driver_Unregister_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) ForwardGuestAgent(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*ForwardGuestAgentResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ForwardGuestAgentResponse) + err := c.cc.Invoke(ctx, Driver_ForwardGuestAgent_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) GuestAgentConn(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Driver_GuestAgentConn_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) SetConfig(ctx context.Context, in *SetConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, Driver_SetConfig_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *driverClient) GetInfo(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*InfoResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(InfoResponse) + err := c.cc.Invoke(ctx, Driver_GetInfo_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// DriverServer is the server API for Driver service. +// All implementations must embed UnimplementedDriverServer +// for forward compatibility. +type DriverServer interface { + Validate(context.Context, *emptypb.Empty) (*emptypb.Empty, error) + Initialize(context.Context, *emptypb.Empty) (*emptypb.Empty, error) + CreateDisk(context.Context, *emptypb.Empty) (*emptypb.Empty, error) + Start(*emptypb.Empty, grpc.ServerStreamingServer[StartResponse]) error + Stop(context.Context, *emptypb.Empty) (*emptypb.Empty, error) + RunGUI(context.Context, *emptypb.Empty) (*emptypb.Empty, error) + ChangeDisplayPassword(context.Context, *ChangeDisplayPasswordRequest) (*emptypb.Empty, error) + GetDisplayConnection(context.Context, *emptypb.Empty) (*GetDisplayConnectionResponse, error) + CreateSnapshot(context.Context, *CreateSnapshotRequest) (*emptypb.Empty, error) + ApplySnapshot(context.Context, *ApplySnapshotRequest) (*emptypb.Empty, error) + DeleteSnapshot(context.Context, *DeleteSnapshotRequest) (*emptypb.Empty, error) + ListSnapshots(context.Context, *emptypb.Empty) (*ListSnapshotsResponse, error) + Register(context.Context, *emptypb.Empty) (*emptypb.Empty, error) + Unregister(context.Context, *emptypb.Empty) (*emptypb.Empty, error) + ForwardGuestAgent(context.Context, *emptypb.Empty) (*ForwardGuestAgentResponse, error) + GuestAgentConn(context.Context, *emptypb.Empty) (*emptypb.Empty, error) + SetConfig(context.Context, *SetConfigRequest) (*emptypb.Empty, error) + GetInfo(context.Context, *emptypb.Empty) (*InfoResponse, error) + mustEmbedUnimplementedDriverServer() +} + +// UnimplementedDriverServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedDriverServer struct{} + +func (UnimplementedDriverServer) Validate(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Validate not implemented") +} +func (UnimplementedDriverServer) Initialize(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Initialize not implemented") +} +func (UnimplementedDriverServer) CreateDisk(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateDisk not implemented") +} +func (UnimplementedDriverServer) Start(*emptypb.Empty, grpc.ServerStreamingServer[StartResponse]) error { + return status.Errorf(codes.Unimplemented, "method Start not implemented") +} +func (UnimplementedDriverServer) Stop(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Stop not implemented") +} +func (UnimplementedDriverServer) RunGUI(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method RunGUI not implemented") +} +func (UnimplementedDriverServer) ChangeDisplayPassword(context.Context, *ChangeDisplayPasswordRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method ChangeDisplayPassword not implemented") +} +func (UnimplementedDriverServer) GetDisplayConnection(context.Context, *emptypb.Empty) (*GetDisplayConnectionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetDisplayConnection not implemented") +} +func (UnimplementedDriverServer) CreateSnapshot(context.Context, *CreateSnapshotRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateSnapshot not implemented") +} +func (UnimplementedDriverServer) ApplySnapshot(context.Context, *ApplySnapshotRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method ApplySnapshot not implemented") +} +func (UnimplementedDriverServer) DeleteSnapshot(context.Context, *DeleteSnapshotRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteSnapshot not implemented") +} +func (UnimplementedDriverServer) ListSnapshots(context.Context, *emptypb.Empty) (*ListSnapshotsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListSnapshots not implemented") +} +func (UnimplementedDriverServer) Register(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Register not implemented") +} +func (UnimplementedDriverServer) Unregister(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Unregister not implemented") +} +func (UnimplementedDriverServer) ForwardGuestAgent(context.Context, *emptypb.Empty) (*ForwardGuestAgentResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ForwardGuestAgent not implemented") +} +func (UnimplementedDriverServer) GuestAgentConn(context.Context, *emptypb.Empty) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method GuestAgentConn not implemented") +} +func (UnimplementedDriverServer) SetConfig(context.Context, *SetConfigRequest) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method SetConfig not implemented") +} +func (UnimplementedDriverServer) GetInfo(context.Context, *emptypb.Empty) (*InfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented") +} +func (UnimplementedDriverServer) mustEmbedUnimplementedDriverServer() {} +func (UnimplementedDriverServer) testEmbeddedByValue() {} + +// UnsafeDriverServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to DriverServer will +// result in compilation errors. +type UnsafeDriverServer interface { + mustEmbedUnimplementedDriverServer() +} + +func RegisterDriverServer(s grpc.ServiceRegistrar, srv DriverServer) { + // If the following call pancis, it indicates UnimplementedDriverServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Driver_ServiceDesc, srv) +} + +func _Driver_Validate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).Validate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_Validate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).Validate(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_Initialize_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).Initialize(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_Initialize_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).Initialize(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_CreateDisk_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).CreateDisk(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_CreateDisk_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).CreateDisk(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_Start_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(emptypb.Empty) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(DriverServer).Start(m, &grpc.GenericServerStream[emptypb.Empty, StartResponse]{ServerStream: stream}) +} + +// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name. +type Driver_StartServer = grpc.ServerStreamingServer[StartResponse] + +func _Driver_Stop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).Stop(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_Stop_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).Stop(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_RunGUI_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).RunGUI(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_RunGUI_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).RunGUI(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_ChangeDisplayPassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ChangeDisplayPasswordRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).ChangeDisplayPassword(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_ChangeDisplayPassword_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).ChangeDisplayPassword(ctx, req.(*ChangeDisplayPasswordRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_GetDisplayConnection_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).GetDisplayConnection(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_GetDisplayConnection_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).GetDisplayConnection(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_CreateSnapshot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateSnapshotRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).CreateSnapshot(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_CreateSnapshot_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).CreateSnapshot(ctx, req.(*CreateSnapshotRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_ApplySnapshot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ApplySnapshotRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).ApplySnapshot(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_ApplySnapshot_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).ApplySnapshot(ctx, req.(*ApplySnapshotRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_DeleteSnapshot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteSnapshotRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).DeleteSnapshot(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_DeleteSnapshot_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).DeleteSnapshot(ctx, req.(*DeleteSnapshotRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_ListSnapshots_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).ListSnapshots(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_ListSnapshots_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).ListSnapshots(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).Register(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_Register_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).Register(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_Unregister_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).Unregister(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_Unregister_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).Unregister(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_ForwardGuestAgent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).ForwardGuestAgent(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_ForwardGuestAgent_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).ForwardGuestAgent(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_GuestAgentConn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).GuestAgentConn(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_GuestAgentConn_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).GuestAgentConn(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_SetConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SetConfigRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).SetConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_SetConfig_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).SetConfig(ctx, req.(*SetConfigRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Driver_GetInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(emptypb.Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DriverServer).GetInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Driver_GetInfo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DriverServer).GetInfo(ctx, req.(*emptypb.Empty)) + } + return interceptor(ctx, in, info, handler) +} + +// Driver_ServiceDesc is the grpc.ServiceDesc for Driver service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Driver_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "Driver", + HandlerType: (*DriverServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Validate", + Handler: _Driver_Validate_Handler, + }, + { + MethodName: "Initialize", + Handler: _Driver_Initialize_Handler, + }, + { + MethodName: "CreateDisk", + Handler: _Driver_CreateDisk_Handler, + }, + { + MethodName: "Stop", + Handler: _Driver_Stop_Handler, + }, + { + MethodName: "RunGUI", + Handler: _Driver_RunGUI_Handler, + }, + { + MethodName: "ChangeDisplayPassword", + Handler: _Driver_ChangeDisplayPassword_Handler, + }, + { + MethodName: "GetDisplayConnection", + Handler: _Driver_GetDisplayConnection_Handler, + }, + { + MethodName: "CreateSnapshot", + Handler: _Driver_CreateSnapshot_Handler, + }, + { + MethodName: "ApplySnapshot", + Handler: _Driver_ApplySnapshot_Handler, + }, + { + MethodName: "DeleteSnapshot", + Handler: _Driver_DeleteSnapshot_Handler, + }, + { + MethodName: "ListSnapshots", + Handler: _Driver_ListSnapshots_Handler, + }, + { + MethodName: "Register", + Handler: _Driver_Register_Handler, + }, + { + MethodName: "Unregister", + Handler: _Driver_Unregister_Handler, + }, + { + MethodName: "ForwardGuestAgent", + Handler: _Driver_ForwardGuestAgent_Handler, + }, + { + MethodName: "GuestAgentConn", + Handler: _Driver_GuestAgentConn_Handler, + }, + { + MethodName: "SetConfig", + Handler: _Driver_SetConfig_Handler, + }, + { + MethodName: "GetInfo", + Handler: _Driver_GetInfo_Handler, + }, + }, + Streams: []grpc.StreamDesc{ + { + StreamName: "Start", + Handler: _Driver_Start_Handler, + ServerStreams: true, + }, + }, + Metadata: "pkg/driver/external/driver.proto", +} diff --git a/pkg/driver/external/server/methods.go b/pkg/driver/external/server/methods.go new file mode 100644 index 00000000000..4b31a076f95 --- /dev/null +++ b/pkg/driver/external/server/methods.go @@ -0,0 +1,265 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package server + +import ( + "context" + "encoding/json" + "net" + "path/filepath" + + "github.com/sirupsen/logrus" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/emptypb" + + "github.com/lima-vm/lima/pkg/bicopy" + pb "github.com/lima-vm/lima/pkg/driver/external" + "github.com/lima-vm/lima/pkg/store" + "github.com/lima-vm/lima/pkg/store/filenames" +) + +func (s *DriverServer) Start(_ *emptypb.Empty, stream pb.Driver_StartServer) error { + s.logger.Debug("Received Start request") + errChan, err := s.driver.Start(stream.Context()) + if err != nil { + s.logger.Errorf("Start failed: %v", err) + return status.Errorf(codes.Internal, "failed to start driver: %v", err) + } + + for { + select { + case err, ok := <-errChan: + if !ok { + s.logger.Debug("Start error channel closed") + if err := stream.Send(&pb.StartResponse{Success: true}); err != nil { + s.logger.Errorf("Failed to send success response: %v", err) + return status.Errorf(codes.Internal, "failed to send success response: %v", err) + } + return nil + } + if err != nil { + s.logger.Errorf("Error during Start: %v", err) + if err := stream.Send(&pb.StartResponse{Error: err.Error(), Success: false}); err != nil { + s.logger.Errorf("Failed to send error response: %v", err) + return status.Errorf(codes.Internal, "failed to send error response: %v", err) + } + } + case <-stream.Context().Done(): + s.logger.Debug("Stream context done, stopping Start") + return nil + } + } +} + +func (s *DriverServer) SetConfig(_ context.Context, req *pb.SetConfigRequest) (*emptypb.Empty, error) { + s.logger.Debugf("Received SetConfig request") + var inst store.Instance + + if err := inst.UnmarshalJSON(req.InstanceConfigJson); err != nil { + s.logger.Errorf("Failed to unmarshal InstanceConfigJson: %v", err) + return &emptypb.Empty{}, err + } + + _ = s.driver.Configure(&inst, int(req.SshLocalPort)) + + return &emptypb.Empty{}, nil +} + +func (s *DriverServer) GuestAgentConn(ctx context.Context, _ *emptypb.Empty) (*emptypb.Empty, error) { + s.logger.Debug("Received GuestAgentConn request") + conn, connType, err := s.driver.GuestAgentConn(ctx) + if err != nil { + s.logger.Errorf("GuestAgentConn failed: %v", err) + return nil, err + } + + if connType != "unix" { + proxySocketPath := filepath.Join(s.driver.Info().InstanceDir, filenames.GuestAgentSock) + + listener, err := net.Listen("unix", proxySocketPath) + if err != nil { + logrus.Errorf("Failed to create proxy socket: %v", err) + return nil, err + } + + go func() { + defer listener.Close() + defer conn.Close() + + proxyConn, err := listener.Accept() + if err != nil { + logrus.Errorf("Failed to accept proxy connection: %v", err) + return + } + + bicopy.Bicopy(conn, proxyConn, nil) + }() + } + + return &emptypb.Empty{}, nil +} + +func (s *DriverServer) GetInfo(_ context.Context, _ *emptypb.Empty) (*pb.InfoResponse, error) { + s.logger.Debug("Received GetInfo request") + info := s.driver.Info() + + infoJSON, err := json.Marshal(info) + if err != nil { + s.logger.Errorf("Failed to marshal driver info: %v", err) + return nil, status.Errorf(codes.Internal, "failed to marshal driver info: %v", err) + } + + return &pb.InfoResponse{ + InfoJson: infoJSON, + }, nil +} + +func (s *DriverServer) Validate(_ context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) { + s.logger.Debugf("Received Validate request") + err := s.driver.Validate() + if err != nil { + s.logger.Errorf("Validation failed: %v", err) + return empty, err + } + s.logger.Debug("Validation succeeded") + return empty, nil +} + +func (s *DriverServer) Initialize(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) { + s.logger.Debug("Received Initialize request") + err := s.driver.Initialize(ctx) + if err != nil { + s.logger.Errorf("Initialization failed: %v", err) + return empty, err + } + s.logger.Debug("Initialization succeeded") + return empty, nil +} + +func (s *DriverServer) CreateDisk(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) { + s.logger.Debug("Received CreateDisk request") + err := s.driver.CreateDisk(ctx) + if err != nil { + s.logger.Errorf("CreateDisk failed: %v", err) + return empty, err + } + s.logger.Debug("CreateDisk succeeded") + return empty, nil +} + +func (s *DriverServer) Stop(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) { + s.logger.Debug("Received Stop request") + err := s.driver.Stop(ctx) + if err != nil { + s.logger.Errorf("Stop failed: %v", err) + return empty, err + } + s.logger.Debug("Stop succeeded") + return empty, nil +} + +func (s *DriverServer) RunGUI(_ context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) { + s.logger.Debug("Received RunGUI request") + err := s.driver.RunGUI() + if err != nil { + s.logger.Errorf("RunGUI failed: %v", err) + return empty, err + } + s.logger.Debug("RunGUI succeeded") + return empty, nil +} + +func (s *DriverServer) ChangeDisplayPassword(ctx context.Context, req *pb.ChangeDisplayPasswordRequest) (*emptypb.Empty, error) { + s.logger.Debug("Received ChangeDisplayPassword request") + err := s.driver.ChangeDisplayPassword(ctx, req.Password) + if err != nil { + s.logger.Errorf("ChangeDisplayPassword failed: %v", err) + return &emptypb.Empty{}, err + } + s.logger.Debug("ChangeDisplayPassword succeeded") + return &emptypb.Empty{}, nil +} + +func (s *DriverServer) GetDisplayConnection(ctx context.Context, _ *emptypb.Empty) (*pb.GetDisplayConnectionResponse, error) { + s.logger.Debug("Received GetDisplayConnection request") + conn, err := s.driver.DisplayConnection(ctx) + if err != nil { + s.logger.Errorf("GetDisplayConnection failed: %v", err) + return nil, err + } + s.logger.Debug("GetDisplayConnection succeeded") + return &pb.GetDisplayConnectionResponse{Connection: conn}, nil +} + +func (s *DriverServer) CreateSnapshot(ctx context.Context, req *pb.CreateSnapshotRequest) (*emptypb.Empty, error) { + s.logger.Debugf("Received CreateSnapshot request with tag: %s", req.Tag) + err := s.driver.CreateSnapshot(ctx, req.Tag) + if err != nil { + s.logger.Errorf("CreateSnapshot failed: %v", err) + return &emptypb.Empty{}, err + } + s.logger.Debug("CreateSnapshot succeeded") + return &emptypb.Empty{}, nil +} + +func (s *DriverServer) ApplySnapshot(ctx context.Context, req *pb.ApplySnapshotRequest) (*emptypb.Empty, error) { + s.logger.Debugf("Received ApplySnapshot request with tag: %s", req.Tag) + err := s.driver.ApplySnapshot(ctx, req.Tag) + if err != nil { + s.logger.Errorf("ApplySnapshot failed: %v", err) + return &emptypb.Empty{}, err + } + s.logger.Debug("ApplySnapshot succeeded") + return &emptypb.Empty{}, nil +} + +func (s *DriverServer) DeleteSnapshot(ctx context.Context, req *pb.DeleteSnapshotRequest) (*emptypb.Empty, error) { + s.logger.Debugf("Received DeleteSnapshot request with tag: %s", req.Tag) + err := s.driver.DeleteSnapshot(ctx, req.Tag) + if err != nil { + s.logger.Errorf("DeleteSnapshot failed: %v", err) + return &emptypb.Empty{}, err + } + s.logger.Debug("DeleteSnapshot succeeded") + return &emptypb.Empty{}, nil +} + +func (s *DriverServer) ListSnapshots(ctx context.Context, _ *emptypb.Empty) (*pb.ListSnapshotsResponse, error) { + s.logger.Debug("Received ListSnapshots request") + snapshots, err := s.driver.ListSnapshots(ctx) + if err != nil { + s.logger.Errorf("ListSnapshots failed: %v", err) + return nil, err + } + s.logger.Debug("ListSnapshots succeeded") + return &pb.ListSnapshotsResponse{Snapshots: snapshots}, nil +} + +func (s *DriverServer) Register(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) { + s.logger.Debug("Received Register request") + err := s.driver.Register(ctx) + if err != nil { + s.logger.Errorf("Register failed: %v", err) + return empty, err + } + s.logger.Debug("Register succeeded") + return empty, nil +} + +func (s *DriverServer) Unregister(ctx context.Context, empty *emptypb.Empty) (*emptypb.Empty, error) { + s.logger.Debug("Received Unregister request") + err := s.driver.Unregister(ctx) + if err != nil { + s.logger.Errorf("Unregister failed: %v", err) + return empty, err + } + s.logger.Debug("Unregister succeeded") + return empty, nil +} + +func (s *DriverServer) ForwardGuestAgent(_ context.Context, _ *emptypb.Empty) (*pb.ForwardGuestAgentResponse, error) { + s.logger.Debug("Received ForwardGuestAgent request") + return &pb.ForwardGuestAgentResponse{ShouldForward: s.driver.ForwardGuestAgent()}, nil +} diff --git a/pkg/driver/external/server/server.go b/pkg/driver/external/server/server.go new file mode 100644 index 00000000000..f8138b3a19f --- /dev/null +++ b/pkg/driver/external/server/server.go @@ -0,0 +1,213 @@ +// SPDX-FileCopyrightText: Copyright The Lima Authors +// SPDX-License-Identifier: Apache-2.0 + +package server + +import ( + "bufio" + "context" + "encoding/json" + "errors" + "fmt" + "net" + "os" + "os/exec" + "os/signal" + "path/filepath" + "syscall" + "time" + + "github.com/sirupsen/logrus" + "google.golang.org/grpc" + "google.golang.org/grpc/keepalive" + + "github.com/lima-vm/lima/pkg/driver" + pb "github.com/lima-vm/lima/pkg/driver/external" + "github.com/lima-vm/lima/pkg/driver/external/client" + "github.com/lima-vm/lima/pkg/registry" + "github.com/lima-vm/lima/pkg/store" + "github.com/lima-vm/lima/pkg/store/filenames" +) + +type DriverServer struct { + pb.UnimplementedDriverServer + driver driver.Driver + logger *logrus.Logger +} + +func Serve(driver driver.Driver) { + logger := logrus.New() + logger.SetLevel(logrus.DebugLevel) + + socketPath := filepath.Join(os.TempDir(), fmt.Sprintf("lima-driver-%s-%d.sock", driver.Info().DriverName, os.Getpid())) + + defer func() { + if err := os.Remove(socketPath); err != nil && !os.IsNotExist(err) { + logger.Warnf("Failed to remove socket file: %v", err) + } + }() + + if err := os.Remove(socketPath); err != nil && !os.IsNotExist(err) { + logger.Fatalf("Failed to remove existing socket file: %v", err) + } + + listener, err := net.Listen("unix", socketPath) + if err != nil { + logger.Fatalf("Failed to listen on Unix socket: %v", err) + } + defer listener.Close() + + output := map[string]string{"socketPath": socketPath} + if err := json.NewEncoder(os.Stdout).Encode(output); err != nil { + logger.Fatalf("Failed to encode socket path as JSON: %v", err) + } + + kaProps := keepalive.ServerParameters{ + Time: 10 * time.Second, + Timeout: 20 * time.Second, + } + + kaPolicy := keepalive.EnforcementPolicy{ + MinTime: 10 * time.Second, + PermitWithoutStream: true, + } + + server := grpc.NewServer( + grpc.KeepaliveParams(kaProps), + grpc.KeepaliveEnforcementPolicy(kaPolicy), + ) + + pb.RegisterDriverServer(server, &DriverServer{ + driver: driver, + logger: logger, + }) + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) + + go func() { + <-sigs + logger.Info("Received shutdown signal, stopping server...") + server.GracefulStop() + }() + + logger.Infof("Starting external driver server for %s", driver.Info().DriverName) + logger.Infof("Server starting on Unix socket: %s", socketPath) + if err := server.Serve(listener); err != nil { + logger.Fatalf("Failed to serve: %v", err) + } +} + +func Start(extDriver *registry.ExternalDriver, instName string) error { + extDriver.Logger.Debugf("Starting external driver at %s", extDriver.Path) + if instName == "" { + return errors.New("instance name cannot be empty") + } + extDriver.InstanceName = instName + + ctx, cancel := context.WithCancel(context.Background()) + cmd := exec.CommandContext(ctx, extDriver.Path) + + stdout, err := cmd.StdoutPipe() + if err != nil { + cancel() + return fmt.Errorf("failed to create stdout pipe for external driver: %w", err) + } + + instanceDir, err := store.InstanceDir(extDriver.InstanceName) + if err != nil { + cancel() + return fmt.Errorf("failed to determine instance directory: %w", err) + } + logPath := filepath.Join(instanceDir, filenames.ExternalDriverStderrLog) + logFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644) + if err != nil { + cancel() + return fmt.Errorf("failed to open external driver log file: %w", err) + } + + cmd.Stderr = logFile + + if err := cmd.Start(); err != nil { + cancel() + return fmt.Errorf("failed to start external driver: %w", err) + } + + driverLogger := extDriver.Logger.WithField("driver", extDriver.Name) + + scanner := bufio.NewScanner(stdout) + var socketPath string + if scanner.Scan() { + var output map[string]string + if err := json.Unmarshal(scanner.Bytes(), &output); err != nil { + cancel() + if err := cmd.Process.Kill(); err != nil { + driverLogger.Errorf("Failed to kill external driver process: %v", err) + } + return fmt.Errorf("failed to parse socket path JSON: %w", err) + } + socketPath = output["socketPath"] + } else { + cancel() + if err := cmd.Process.Kill(); err != nil { + driverLogger.Errorf("Failed to kill external driver process: %v", err) + } + return errors.New("failed to read socket path from driver") + } + extDriver.SocketPath = socketPath + + driverClient, err := client.NewDriverClient(extDriver.SocketPath, extDriver.Logger) + if err != nil { + cancel() + if err := cmd.Process.Kill(); err != nil { + driverLogger.Errorf("Failed to kill external driver process after client creation failure: %v", err) + } + return fmt.Errorf("failed to create driver client: %w", err) + } + + extDriver.Command = cmd + extDriver.Client = driverClient + extDriver.Ctx = ctx + extDriver.CancelFunc = cancel + + driverLogger.Debugf("External driver %s started successfully", extDriver.Name) + return nil +} + +func Stop(extDriver *registry.ExternalDriver) error { + if extDriver.Command == nil { + return fmt.Errorf("external driver %s is not running", extDriver.Name) + } + + extDriver.Logger.Debugf("Stopping external driver %s", extDriver.Name) + if extDriver.CancelFunc != nil { + extDriver.CancelFunc() + } + if err := extDriver.Command.Process.Kill(); err != nil { + extDriver.Logger.Errorf("Failed to kill external driver process: %v", err) + } + if err := os.Remove(extDriver.SocketPath); err != nil && !os.IsNotExist(err) { + extDriver.Logger.Warnf("Failed to remove socket file: %v", err) + } + + extDriver.Command = nil + extDriver.Client = nil + extDriver.Ctx = nil + extDriver.CancelFunc = nil + + extDriver.Logger.Debugf("External driver %s stopped successfully", extDriver.Name) + return nil +} + +func StopAllExternalDrivers() { + for name, driver := range registry.ExternalDrivers { + if driver.Command != nil && driver.Command.Process != nil { + if err := Stop(driver); err != nil { + logrus.Errorf("Failed to stop external driver %s: %v", name, err) + } else { + logrus.Debugf("External driver %s stopped successfully", name) + } + } + delete(registry.ExternalDrivers, name) + } +} diff --git a/pkg/driverutil/instance.go b/pkg/driverutil/instance.go index e835dfa2cf5..969cf10c375 100644 --- a/pkg/driverutil/instance.go +++ b/pkg/driverutil/instance.go @@ -9,6 +9,7 @@ import ( "github.com/sirupsen/logrus" "github.com/lima-vm/lima/pkg/driver" + "github.com/lima-vm/lima/pkg/driver/external/server" "github.com/lima-vm/lima/pkg/registry" "github.com/lima-vm/lima/pkg/store" ) @@ -16,11 +17,27 @@ import ( // CreateConfiguredDriver creates a driver.ConfiguredDriver for the given instance. func CreateConfiguredDriver(inst *store.Instance, sshLocalPort int) (*driver.ConfiguredDriver, error) { limaDriver := inst.Config.VMType - _, intDriver, exists := registry.Get(*limaDriver) + extDriver, intDriver, exists := registry.Get(*limaDriver) if !exists { return nil, fmt.Errorf("unknown or unsupported VM type: %s", *limaDriver) } - logrus.Infof("Using internal driver %q", intDriver.Info().DriverName) + if extDriver != nil { + extDriver.Logger.Debugf("Using external driver %q", extDriver.Name) + if extDriver.Client == nil || extDriver.Command == nil { + logrus.Debugf("Starting new instance of external driver %q", extDriver.Name) + if err := server.Start(extDriver, inst.Name); err != nil { + extDriver.Logger.Errorf("Failed to start external driver %q: %v", extDriver.Name, err) + return nil, err + } + } else { + logrus.Debugf("Reusing existing external driver %q instance", extDriver.Name) + extDriver.InstanceName = inst.Name + } + + return extDriver.Client.Configure(inst, sshLocalPort), nil + } + + logrus.Debugf("Using internal driver %q", intDriver.Info().DriverName) return intDriver.Configure(inst, sshLocalPort), nil } diff --git a/pkg/instance/start.go b/pkg/instance/start.go index c318ca219a7..ec3b81bdf45 100644 --- a/pkg/instance/start.go +++ b/pkg/instance/start.go @@ -30,6 +30,7 @@ import ( "github.com/lima-vm/lima/pkg/imgutil/proxyimgutil" "github.com/lima-vm/lima/pkg/limayaml" "github.com/lima-vm/lima/pkg/osutil" + "github.com/lima-vm/lima/pkg/registry" "github.com/lima-vm/lima/pkg/store" "github.com/lima-vm/lima/pkg/store/filenames" "github.com/lima-vm/lima/pkg/usrlocalsharelima" @@ -186,7 +187,7 @@ func Start(ctx context.Context, inst *store.Instance, limactl string, launchHost if _, err := os.Stat(haPIDPath); !errors.Is(err, os.ErrNotExist) { return fmt.Errorf("instance %q seems running (hint: remove %q if the instance is not actually running)", inst.Name, haPIDPath) } - logrus.Infof("Starting the instance %q with VM driver %q", inst.Name, inst.VMType) + logrus.Infof("Starting the instance %q with %s VM driver %q", inst.Name, registry.CheckInternalOrExternal(inst.VMType), inst.VMType) haSockPath := filepath.Join(inst.Dir, filenames.HostAgentSock) diff --git a/pkg/networks/usernet/client.go b/pkg/networks/usernet/client.go index 8bc478cb0f2..4ed790321e0 100644 --- a/pkg/networks/usernet/client.go +++ b/pkg/networks/usernet/client.go @@ -43,6 +43,9 @@ func (c *Client) ConfigureDriver(ctx context.Context, inst *store.Instance, sshL return err } hosts := inst.Config.HostResolver.Hosts + if hosts == nil { + hosts = make(map[string]string) + } hosts[fmt.Sprintf("%s.internal", inst.Hostname)] = ipAddress err = c.AddDNSHosts(hosts) return err diff --git a/pkg/registry/registry.go b/pkg/registry/registry.go index c71f3ed34ab..7d8582d1e32 100644 --- a/pkg/registry/registry.go +++ b/pkg/registry/registry.go @@ -9,16 +9,19 @@ import ( "os" "os/exec" "path/filepath" + "runtime" "strings" "github.com/sirupsen/logrus" "github.com/lima-vm/lima/pkg/driver" + "github.com/lima-vm/lima/pkg/driver/external/client" "github.com/lima-vm/lima/pkg/usrlocalsharelima" ) const ( Internal = "internal" + External = "external" ) type ExternalDriver struct { @@ -26,6 +29,7 @@ type ExternalDriver struct { InstanceName string Command *exec.Cmd SocketPath string + Client *client.DriverClient // Client is the gRPC client for the external driver Path string Ctx context.Context Logger *logrus.Logger @@ -53,6 +57,18 @@ func List() map[string]string { return vmTypes } +func CheckInternalOrExternal(name string) string { + extDriver, _, exists := Get(name) + if !exists { + return "" + } + if extDriver != nil { + return External + } + + return Internal +} + func Get(name string) (*ExternalDriver, driver.Driver, bool) { if err := discoverDrivers(); err != nil { logrus.Warnf("Error discovering drivers: %v", err) @@ -155,16 +171,26 @@ func discoverDriversInDir(dir string) error { func registerDriverFile(path string) { base := filepath.Base(path) - if !strings.HasPrefix(base, "lima-driver-") { + name := "" + if runtime.GOOS == "windows" { + if strings.HasPrefix(base, "lima-driver-") && strings.HasSuffix(base, ".exe") { + name = strings.TrimSuffix(strings.TrimPrefix(base, "lima-driver-"), ".exe") + } + } else { + if strings.HasPrefix(base, "lima-driver-") { + name = strings.TrimPrefix(base, "lima-driver-") + } + } + if name == "" { return } - - name := strings.TrimPrefix(base, "lima-driver-") - registerExternalDriver(name, path) } func isExecutable(mode os.FileMode) bool { + if runtime.GOOS == "windows" { + return true + } return mode&0o111 != 0 } diff --git a/pkg/store/filenames/filenames.go b/pkg/store/filenames/filenames.go index cc9a0fc9f33..a8459d77888 100644 --- a/pkg/store/filenames/filenames.go +++ b/pkg/store/filenames/filenames.go @@ -31,39 +31,40 @@ const ( // Filenames that may appear under an instance directory const ( - LimaYAML = "lima.yaml" - LimaVersion = "lima-version" // Lima version used to create instance - CIDataISO = "cidata.iso" - CIDataISODir = "cidata" - CloudConfig = "cloud-config.yaml" - BaseDisk = "basedisk" - DiffDisk = "diffdisk" - Kernel = "kernel" - KernelCmdline = "kernel.cmdline" - Initrd = "initrd" - QMPSock = "qmp.sock" - SerialLog = "serial.log" // default serial (ttyS0, but ttyAMA0 on qemu-system-{arm,aarch64}) - SerialSock = "serial.sock" - SerialPCILog = "serialp.log" // pci serial (ttyS0 on qemu-system-{arm,aarch64}) - SerialPCISock = "serialp.sock" - SerialVirtioLog = "serialv.log" // virtio serial - SerialVirtioSock = "serialv.sock" - SSHSock = "ssh.sock" - SSHConfig = "ssh.config" - VhostSock = "virtiofsd-%d.sock" - VNCDisplayFile = "vncdisplay" - VNCPasswordFile = "vncpassword" - GuestAgentSock = "ga.sock" - VirtioPort = "io.lima-vm.guest_agent.0" - HostAgentPID = "ha.pid" - HostAgentSock = "ha.sock" - HostAgentStdoutLog = "ha.stdout.log" - HostAgentStderrLog = "ha.stderr.log" - VzIdentifier = "vz-identifier" - VzEfi = "vz-efi" // efi variable store - QemuEfiCodeFD = "qemu-efi-code.fd" // efi code; not always created - QemuEfiFullFD = "qemu-efi-full.fd" // concatenated efi vars and code; not always created - AnsibleInventoryYAML = "ansible-inventory.yaml" + LimaYAML = "lima.yaml" + LimaVersion = "lima-version" // Lima version used to create instance + CIDataISO = "cidata.iso" + CIDataISODir = "cidata" + CloudConfig = "cloud-config.yaml" + BaseDisk = "basedisk" + DiffDisk = "diffdisk" + Kernel = "kernel" + KernelCmdline = "kernel.cmdline" + Initrd = "initrd" + QMPSock = "qmp.sock" + SerialLog = "serial.log" // default serial (ttyS0, but ttyAMA0 on qemu-system-{arm,aarch64}) + SerialSock = "serial.sock" + SerialPCILog = "serialp.log" // pci serial (ttyS0 on qemu-system-{arm,aarch64}) + SerialPCISock = "serialp.sock" + SerialVirtioLog = "serialv.log" // virtio serial + SerialVirtioSock = "serialv.sock" + SSHSock = "ssh.sock" + SSHConfig = "ssh.config" + VhostSock = "virtiofsd-%d.sock" + VNCDisplayFile = "vncdisplay" + VNCPasswordFile = "vncpassword" + GuestAgentSock = "ga.sock" + VirtioPort = "io.lima-vm.guest_agent.0" + HostAgentPID = "ha.pid" + HostAgentSock = "ha.sock" + HostAgentStdoutLog = "ha.stdout.log" + HostAgentStderrLog = "ha.stderr.log" + ExternalDriverStderrLog = "driver.stderr.log" + VzIdentifier = "vz-identifier" + VzEfi = "vz-efi" // efi variable store + QemuEfiCodeFD = "qemu-efi-code.fd" // efi code; not always created + QemuEfiFullFD = "qemu-efi-full.fd" // concatenated efi vars and code; not always created + AnsibleInventoryYAML = "ansible-inventory.yaml" // SocketDir is the default location for forwarded sockets with a relative paths in HostSocket. SocketDir = "sock" diff --git a/pkg/store/instance.go b/pkg/store/instance.go index 19b4b2270dc..5031b1c145c 100644 --- a/pkg/store/instance.go +++ b/pkg/store/instance.go @@ -5,6 +5,7 @@ package store import ( "context" + "encoding/json" "errors" "fmt" "io" @@ -438,3 +439,38 @@ func (inst *Instance) Unprotect() error { inst.Protected = false return nil } + +func (inst *Instance) MarshalJSON() ([]byte, error) { + type Alias Instance + errorsAsStrings := make([]string, len(inst.Errors)) + for i, err := range inst.Errors { + if err != nil { + errorsAsStrings[i] = err.Error() + } + } + return json.Marshal(&struct { + *Alias + Errors []string `json:"errors,omitempty"` + }{ + Alias: (*Alias)(inst), + Errors: errorsAsStrings, + }) +} + +func (inst *Instance) UnmarshalJSON(data []byte) error { + type Alias Instance + aux := &struct { + *Alias + Errors []string `json:"errors,omitempty"` + }{ + Alias: (*Alias)(inst), + } + if err := json.Unmarshal(data, &aux); err != nil { + return err + } + inst.Errors = nil + for _, msg := range aux.Errors { + inst.Errors = append(inst.Errors, errors.New(msg)) + } + return nil +}