From 7b262634d71ba753a8cfc37e99135d8576de7ee6 Mon Sep 17 00:00:00 2001 From: Harsh Rawat Date: Fri, 1 May 2026 23:36:35 +0530 Subject: [PATCH] [live-migration] add snapshot protos for shim state As part of the live migration workflow, we need to checkpoint the current state of the shim on the source side and restore it on the destination side. This change introduces the well-typed proto definitions for the shim state used during Save/Restore. A new `internal/controller//save` package is added per controller, each owning its own versioned `Payload` message: - migration: top-level envelope carrying VM and per-pod payloads as google.protobuf.Any so each controller is independently versioned. - vm: VM-level state (host compute system, resources, etc.). - pod: sandbox/pod-level state. - network: network endpoints/namespaces attached to the sandbox. - process: process/exec state inside containers. - linuxcontainer: Linux container state. - device/scsi, device/vpci, device/plan9: per-device attachment state. Also registers the new `internal/controller` prefix in Protobuild.toml (and drops the now-unused `internal/vmservice` prefix) so the generated .pb.go files are produced with the correct import paths. Signed-off-by: Harsh Rawat --- Protobuild.toml | 2 +- .../controller/device/plan9/save/constants.go | 16 + .../device/plan9/save/payload.pb.go | 540 ++++++++++++++ .../device/plan9/save/payload.proto | 95 +++ .../controller/device/scsi/save/constants.go | 16 + .../controller/device/scsi/save/payload.pb.go | 698 ++++++++++++++++++ .../controller/device/scsi/save/payload.proto | 126 ++++ .../controller/device/vpci/save/constants.go | 16 + .../controller/device/vpci/save/payload.pb.go | 292 ++++++++ .../controller/device/vpci/save/payload.proto | 50 ++ .../linuxcontainer/save/constants.go | 17 + .../linuxcontainer/save/payload.pb.go | 458 ++++++++++++ .../linuxcontainer/save/payload.proto | 99 +++ .../controller/migration/save/constants.go | 17 + .../controller/migration/save/payload.pb.go | 152 ++++ .../controller/migration/save/payload.proto | 21 + internal/controller/network/save/constants.go | 15 + .../controller/network/save/payload.pb.go | 319 ++++++++ .../controller/network/save/payload.proto | 59 ++ internal/controller/pod/save/constants.go | 17 + internal/controller/pod/save/payload.pb.go | 178 +++++ internal/controller/pod/save/payload.proto | 32 + internal/controller/process/save/constants.go | 15 + .../controller/process/save/payload.pb.go | 311 ++++++++ .../controller/process/save/payload.proto | 65 ++ internal/controller/vm/save/constants.go | 17 + internal/controller/vm/save/payload.pb.go | 468 ++++++++++++ internal/controller/vm/save/payload.proto | 105 +++ 28 files changed, 4215 insertions(+), 1 deletion(-) create mode 100644 internal/controller/device/plan9/save/constants.go create mode 100644 internal/controller/device/plan9/save/payload.pb.go create mode 100644 internal/controller/device/plan9/save/payload.proto create mode 100644 internal/controller/device/scsi/save/constants.go create mode 100644 internal/controller/device/scsi/save/payload.pb.go create mode 100644 internal/controller/device/scsi/save/payload.proto create mode 100644 internal/controller/device/vpci/save/constants.go create mode 100644 internal/controller/device/vpci/save/payload.pb.go create mode 100644 internal/controller/device/vpci/save/payload.proto create mode 100644 internal/controller/linuxcontainer/save/constants.go create mode 100644 internal/controller/linuxcontainer/save/payload.pb.go create mode 100644 internal/controller/linuxcontainer/save/payload.proto create mode 100644 internal/controller/migration/save/constants.go create mode 100644 internal/controller/migration/save/payload.pb.go create mode 100644 internal/controller/migration/save/payload.proto create mode 100644 internal/controller/network/save/constants.go create mode 100644 internal/controller/network/save/payload.pb.go create mode 100644 internal/controller/network/save/payload.proto create mode 100644 internal/controller/pod/save/constants.go create mode 100644 internal/controller/pod/save/payload.pb.go create mode 100644 internal/controller/pod/save/payload.proto create mode 100644 internal/controller/process/save/constants.go create mode 100644 internal/controller/process/save/payload.pb.go create mode 100644 internal/controller/process/save/payload.proto create mode 100644 internal/controller/vm/save/constants.go create mode 100644 internal/controller/vm/save/payload.pb.go create mode 100644 internal/controller/vm/save/payload.proto diff --git a/Protobuild.toml b/Protobuild.toml index 48d04ea350..d1fdaa6f11 100644 --- a/Protobuild.toml +++ b/Protobuild.toml @@ -19,8 +19,8 @@ prefixes = [ "github.com/Microsoft/hcsshim/internal/shimdiag", "github.com/Microsoft/hcsshim/internal/extendedtask", "github.com/Microsoft/hcsshim/internal/computeagent", + "github.com/Microsoft/hcsshim/internal/controller", "github.com/Microsoft/hcsshim/internal/ncproxyttrpc", - "github.com/Microsoft/hcsshim/internal/vmservice", "github.com/Microsoft/hcsshim/pkg/migration", ] generators = ["go", "go-ttrpc"] diff --git a/internal/controller/device/plan9/save/constants.go b/internal/controller/device/plan9/save/constants.go new file mode 100644 index 0000000000..cb6f042a77 --- /dev/null +++ b/internal/controller/device/plan9/save/constants.go @@ -0,0 +1,16 @@ +//go:build windows && lcow + +// Package save defines the wire format owned by the Plan9 sub-controller +// for live migration. The [Payload] message is self-contained and carries the +// Plan9 sub-controller's serialized state (LCOW 9P file shares and their +// in-guest mounts) across shims. +package save + +// SchemaVersion is the on-the-wire compatibility version stamped into +// [Payload.SchemaVersion]. Bump on any breaking change to payload.proto. +const SchemaVersion uint32 = 1 + +// TypeURL identifies a Plan9 [Payload] when wrapped in an [anypb.Any]. It is +// opaque to clients and only meaningful between two shims that agree on +// [SchemaVersion]. +const TypeURL = "type.microsoft.com/hcsshim.controller.plan9.save.v1.Payload" diff --git a/internal/controller/device/plan9/save/payload.pb.go b/internal/controller/device/plan9/save/payload.pb.go new file mode 100644 index 0000000000..10d84eafcf --- /dev/null +++ b/internal/controller/device/plan9/save/payload.pb.go @@ -0,0 +1,540 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v5.26.0 +// source: github.com/Microsoft/hcsshim/internal/controller/device/plan9/save/payload.proto + +package save + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + 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 ShareStage int32 + +const ( + ShareStage_SHARE_STAGE_RESERVED ShareStage = 0 + ShareStage_SHARE_STAGE_ADDED ShareStage = 1 + ShareStage_SHARE_STAGE_INVALID ShareStage = 2 + ShareStage_SHARE_STAGE_REMOVED ShareStage = 3 +) + +// Enum value maps for ShareStage. +var ( + ShareStage_name = map[int32]string{ + 0: "SHARE_STAGE_RESERVED", + 1: "SHARE_STAGE_ADDED", + 2: "SHARE_STAGE_INVALID", + 3: "SHARE_STAGE_REMOVED", + } + ShareStage_value = map[string]int32{ + "SHARE_STAGE_RESERVED": 0, + "SHARE_STAGE_ADDED": 1, + "SHARE_STAGE_INVALID": 2, + "SHARE_STAGE_REMOVED": 3, + } +) + +func (x ShareStage) Enum() *ShareStage { + p := new(ShareStage) + *p = x + return p +} + +func (x ShareStage) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ShareStage) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_enumTypes[0].Descriptor() +} + +func (ShareStage) Type() protoreflect.EnumType { + return &file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_enumTypes[0] +} + +func (x ShareStage) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ShareStage.Descriptor instead. +func (ShareStage) EnumDescriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDescGZIP(), []int{0} +} + +type MountStage int32 + +const ( + MountStage_MOUNT_STAGE_RESERVED MountStage = 0 + MountStage_MOUNT_STAGE_MOUNTED MountStage = 1 + MountStage_MOUNT_STAGE_INVALID MountStage = 2 + MountStage_MOUNT_STAGE_UNMOUNTED MountStage = 3 +) + +// Enum value maps for MountStage. +var ( + MountStage_name = map[int32]string{ + 0: "MOUNT_STAGE_RESERVED", + 1: "MOUNT_STAGE_MOUNTED", + 2: "MOUNT_STAGE_INVALID", + 3: "MOUNT_STAGE_UNMOUNTED", + } + MountStage_value = map[string]int32{ + "MOUNT_STAGE_RESERVED": 0, + "MOUNT_STAGE_MOUNTED": 1, + "MOUNT_STAGE_INVALID": 2, + "MOUNT_STAGE_UNMOUNTED": 3, + } +) + +func (x MountStage) Enum() *MountStage { + p := new(MountStage) + *p = x + return p +} + +func (x MountStage) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (MountStage) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_enumTypes[1].Descriptor() +} + +func (MountStage) Type() protoreflect.EnumType { + return &file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_enumTypes[1] +} + +func (x MountStage) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use MountStage.Descriptor instead. +func (MountStage) EnumDescriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDescGZIP(), []int{1} +} + +// Payload is the migration payload owned by the Plan9 sub-controller. It is +// the top-level message wrapped in an [anypb.Any] when handed off between +// source and destination shims. +type Payload struct { + state protoimpl.MessageState `protogen:"open.v1"` + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + SchemaVersion uint32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"` + // no_writable_file_shares mirrors the controller's policy flag. + NoWritableFileShares bool `protobuf:"varint,2,opt,name=no_writable_file_shares,json=noWritableFileShares,proto3" json:"no_writable_file_shares,omitempty"` + // name_counter is the monotonic counter used to allocate unique share + // names. It is preserved verbatim so post-restore allocations do not + // collide with already-restored shares. + NameCounter uint64 `protobuf:"varint,3,opt,name=name_counter,json=nameCounter,proto3" json:"name_counter,omitempty"` + // shares is keyed by host path. + Shares map[string]*ShareState `protobuf:"bytes,4,rep,name=shares,proto3" json:"shares,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + // reservations is keyed by reservation GUID and maps to the host path of + // the reserved share. The string keys here are the values referenced by + // container controllers' plan9_reservation_ids. + Reservations map[string]string `protobuf:"bytes,5,rep,name=reservations,proto3" json:"reservations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Payload) Reset() { + *x = Payload{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Payload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Payload) ProtoMessage() {} + +func (x *Payload) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_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 Payload.ProtoReflect.Descriptor instead. +func (*Payload) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDescGZIP(), []int{0} +} + +func (x *Payload) GetSchemaVersion() uint32 { + if x != nil { + return x.SchemaVersion + } + return 0 +} + +func (x *Payload) GetNoWritableFileShares() bool { + if x != nil { + return x.NoWritableFileShares + } + return false +} + +func (x *Payload) GetNameCounter() uint64 { + if x != nil { + return x.NameCounter + } + return 0 +} + +func (x *Payload) GetShares() map[string]*ShareState { + if x != nil { + return x.Shares + } + return nil +} + +func (x *Payload) GetReservations() map[string]string { + if x != nil { + return x.Reservations + } + return nil +} + +// ShareState is a single 9P share tracked by the controller, keyed by host +// path in [Payload.shares]. +type ShareState struct { + state protoimpl.MessageState `protogen:"open.v1"` + // name is the generated 9P share name. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // config is the share configuration. + Config *ShareConfig `protobuf:"bytes,2,opt,name=config,proto3" json:"config,omitempty"` + // state is the share lifecycle stage. + State ShareStage `protobuf:"varint,3,opt,name=state,proto3,enum=hcsshim.controller.plan9.save.v1.ShareStage" json:"state,omitempty"` + // mount carries the in-guest mount metadata for this share, when present. + Mount *MountState `protobuf:"bytes,4,opt,name=mount,proto3" json:"mount,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ShareState) Reset() { + *x = ShareState{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ShareState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ShareState) ProtoMessage() {} + +func (x *ShareState) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_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 ShareState.ProtoReflect.Descriptor instead. +func (*ShareState) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDescGZIP(), []int{1} +} + +func (x *ShareState) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *ShareState) GetConfig() *ShareConfig { + if x != nil { + return x.Config + } + return nil +} + +func (x *ShareState) GetState() ShareStage { + if x != nil { + return x.State + } + return ShareStage_SHARE_STAGE_RESERVED +} + +func (x *ShareState) GetMount() *MountState { + if x != nil { + return x.Mount + } + return nil +} + +// ShareConfig is the host-side configuration of a 9P share. +type ShareConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + // read_only exposes the share read-only. + ReadOnly bool `protobuf:"varint,1,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` + // restrict enables single-file mapping mode in which only files listed in + // allowed_names are visible through the share. + Restrict bool `protobuf:"varint,2,opt,name=restrict,proto3" json:"restrict,omitempty"` + // allowed_names is the whitelist of file names used when restrict is true. + AllowedNames []string `protobuf:"bytes,3,rep,name=allowed_names,json=allowedNames,proto3" json:"allowed_names,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ShareConfig) Reset() { + *x = ShareConfig{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ShareConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ShareConfig) ProtoMessage() {} + +func (x *ShareConfig) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_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 ShareConfig.ProtoReflect.Descriptor instead. +func (*ShareConfig) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDescGZIP(), []int{2} +} + +func (x *ShareConfig) GetReadOnly() bool { + if x != nil { + return x.ReadOnly + } + return false +} + +func (x *ShareConfig) GetRestrict() bool { + if x != nil { + return x.Restrict + } + return false +} + +func (x *ShareConfig) GetAllowedNames() []string { + if x != nil { + return x.AllowedNames + } + return nil +} + +// MountState is the in-guest mount metadata for a 9P share. +type MountState struct { + state protoimpl.MessageState `protogen:"open.v1"` + // read_only mounts the share read-only inside the guest. + ReadOnly bool `protobuf:"varint,1,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` + // state is the mount lifecycle stage. + State MountStage `protobuf:"varint,2,opt,name=state,proto3,enum=hcsshim.controller.plan9.save.v1.MountStage" json:"state,omitempty"` + // ref_count is the number of live holders of this mount. + RefCount uint32 `protobuf:"varint,3,opt,name=ref_count,json=refCount,proto3" json:"ref_count,omitempty"` + // guest_path is the mount point inside the guest. + GuestPath string `protobuf:"bytes,4,opt,name=guest_path,json=guestPath,proto3" json:"guest_path,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MountState) Reset() { + *x = MountState{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MountState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MountState) ProtoMessage() {} + +func (x *MountState) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_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 MountState.ProtoReflect.Descriptor instead. +func (*MountState) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDescGZIP(), []int{3} +} + +func (x *MountState) GetReadOnly() bool { + if x != nil { + return x.ReadOnly + } + return false +} + +func (x *MountState) GetState() MountStage { + if x != nil { + return x.State + } + return MountStage_MOUNT_STAGE_RESERVED +} + +func (x *MountState) GetRefCount() uint32 { + if x != nil { + return x.RefCount + } + return 0 +} + +func (x *MountState) GetGuestPath() string { + if x != nil { + return x.GuestPath + } + return "" +} + +var File_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto protoreflect.FileDescriptor + +const file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDesc = "" + + "\n" + + "Pgithub.com/Microsoft/hcsshim/internal/controller/device/plan9/save/payload.proto\x12 hcsshim.controller.plan9.save.v1\"\xe4\x03\n" + + "\aPayload\x12%\n" + + "\x0eschema_version\x18\x01 \x01(\rR\rschemaVersion\x125\n" + + "\x17no_writable_file_shares\x18\x02 \x01(\bR\x14noWritableFileShares\x12!\n" + + "\fname_counter\x18\x03 \x01(\x04R\vnameCounter\x12M\n" + + "\x06shares\x18\x04 \x03(\v25.hcsshim.controller.plan9.save.v1.Payload.SharesEntryR\x06shares\x12_\n" + + "\freservations\x18\x05 \x03(\v2;.hcsshim.controller.plan9.save.v1.Payload.ReservationsEntryR\freservations\x1ag\n" + + "\vSharesEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12B\n" + + "\x05value\x18\x02 \x01(\v2,.hcsshim.controller.plan9.save.v1.ShareStateR\x05value:\x028\x01\x1a?\n" + + "\x11ReservationsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xef\x01\n" + + "\n" + + "ShareState\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12E\n" + + "\x06config\x18\x02 \x01(\v2-.hcsshim.controller.plan9.save.v1.ShareConfigR\x06config\x12B\n" + + "\x05state\x18\x03 \x01(\x0e2,.hcsshim.controller.plan9.save.v1.ShareStageR\x05state\x12B\n" + + "\x05mount\x18\x04 \x01(\v2,.hcsshim.controller.plan9.save.v1.MountStateR\x05mount\"k\n" + + "\vShareConfig\x12\x1b\n" + + "\tread_only\x18\x01 \x01(\bR\breadOnly\x12\x1a\n" + + "\brestrict\x18\x02 \x01(\bR\brestrict\x12#\n" + + "\rallowed_names\x18\x03 \x03(\tR\fallowedNames\"\xa9\x01\n" + + "\n" + + "MountState\x12\x1b\n" + + "\tread_only\x18\x01 \x01(\bR\breadOnly\x12B\n" + + "\x05state\x18\x02 \x01(\x0e2,.hcsshim.controller.plan9.save.v1.MountStageR\x05state\x12\x1b\n" + + "\tref_count\x18\x03 \x01(\rR\brefCount\x12\x1d\n" + + "\n" + + "guest_path\x18\x04 \x01(\tR\tguestPath*o\n" + + "\n" + + "ShareStage\x12\x18\n" + + "\x14SHARE_STAGE_RESERVED\x10\x00\x12\x15\n" + + "\x11SHARE_STAGE_ADDED\x10\x01\x12\x17\n" + + "\x13SHARE_STAGE_INVALID\x10\x02\x12\x17\n" + + "\x13SHARE_STAGE_REMOVED\x10\x03*s\n" + + "\n" + + "MountStage\x12\x18\n" + + "\x14MOUNT_STAGE_RESERVED\x10\x00\x12\x17\n" + + "\x13MOUNT_STAGE_MOUNTED\x10\x01\x12\x17\n" + + "\x13MOUNT_STAGE_INVALID\x10\x02\x12\x19\n" + + "\x15MOUNT_STAGE_UNMOUNTED\x10\x03BIZGgithub.com/Microsoft/hcsshim/internal/controller/device/plan9/save;saveb\x06proto3" + +var ( + file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDescOnce sync.Once + file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDescData []byte +) + +func file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDescGZIP() []byte { + file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDescOnce.Do(func() { + file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDesc))) + }) + return file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDescData +} + +var file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_goTypes = []any{ + (ShareStage)(0), // 0: hcsshim.controller.plan9.save.v1.ShareStage + (MountStage)(0), // 1: hcsshim.controller.plan9.save.v1.MountStage + (*Payload)(nil), // 2: hcsshim.controller.plan9.save.v1.Payload + (*ShareState)(nil), // 3: hcsshim.controller.plan9.save.v1.ShareState + (*ShareConfig)(nil), // 4: hcsshim.controller.plan9.save.v1.ShareConfig + (*MountState)(nil), // 5: hcsshim.controller.plan9.save.v1.MountState + nil, // 6: hcsshim.controller.plan9.save.v1.Payload.SharesEntry + nil, // 7: hcsshim.controller.plan9.save.v1.Payload.ReservationsEntry +} +var file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_depIdxs = []int32{ + 6, // 0: hcsshim.controller.plan9.save.v1.Payload.shares:type_name -> hcsshim.controller.plan9.save.v1.Payload.SharesEntry + 7, // 1: hcsshim.controller.plan9.save.v1.Payload.reservations:type_name -> hcsshim.controller.plan9.save.v1.Payload.ReservationsEntry + 4, // 2: hcsshim.controller.plan9.save.v1.ShareState.config:type_name -> hcsshim.controller.plan9.save.v1.ShareConfig + 0, // 3: hcsshim.controller.plan9.save.v1.ShareState.state:type_name -> hcsshim.controller.plan9.save.v1.ShareStage + 5, // 4: hcsshim.controller.plan9.save.v1.ShareState.mount:type_name -> hcsshim.controller.plan9.save.v1.MountState + 1, // 5: hcsshim.controller.plan9.save.v1.MountState.state:type_name -> hcsshim.controller.plan9.save.v1.MountStage + 3, // 6: hcsshim.controller.plan9.save.v1.Payload.SharesEntry.value:type_name -> hcsshim.controller.plan9.save.v1.ShareState + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { + file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_init() +} +func file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_init() { + if File_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_rawDesc)), + NumEnums: 2, + NumMessages: 6, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_goTypes, + DependencyIndexes: file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_depIdxs, + EnumInfos: file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_enumTypes, + MessageInfos: file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_msgTypes, + }.Build() + File_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto = out.File + file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_goTypes = nil + file_github_com_Microsoft_hcsshim_internal_controller_device_plan9_save_payload_proto_depIdxs = nil +} diff --git a/internal/controller/device/plan9/save/payload.proto b/internal/controller/device/plan9/save/payload.proto new file mode 100644 index 0000000000..46997f324d --- /dev/null +++ b/internal/controller/device/plan9/save/payload.proto @@ -0,0 +1,95 @@ +syntax = "proto3"; + +package hcsshim.controller.plan9.save.v1; + +option go_package = "github.com/Microsoft/hcsshim/internal/controller/device/plan9/save;save"; + +// ============================================================================= +// Lifecycle stage enums +// +// Each enum mirrors a Go State type used by the Plan9 sub-controller. The +// zero value matches the Go iota-zero value (Reserved). +// ============================================================================= + +enum ShareStage { + SHARE_STAGE_RESERVED = 0; + SHARE_STAGE_ADDED = 1; + SHARE_STAGE_INVALID = 2; + SHARE_STAGE_REMOVED = 3; +} + +enum MountStage { + MOUNT_STAGE_RESERVED = 0; + MOUNT_STAGE_MOUNTED = 1; + MOUNT_STAGE_INVALID = 2; + MOUNT_STAGE_UNMOUNTED = 3; +} + +// Payload is the migration payload owned by the Plan9 sub-controller. It is +// the top-level message wrapped in an [anypb.Any] when handed off between +// source and destination shims. +message Payload { + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + uint32 schema_version = 1; + + // no_writable_file_shares mirrors the controller's policy flag. + bool no_writable_file_shares = 2; + + // name_counter is the monotonic counter used to allocate unique share + // names. It is preserved verbatim so post-restore allocations do not + // collide with already-restored shares. + uint64 name_counter = 3; + + // shares is keyed by host path. + map shares = 4; + + // reservations is keyed by reservation GUID and maps to the host path of + // the reserved share. The string keys here are the values referenced by + // container controllers' plan9_reservation_ids. + map reservations = 5; +} + +// ShareState is a single 9P share tracked by the controller, keyed by host +// path in [Payload.shares]. +message ShareState { + // name is the generated 9P share name. + string name = 1; + + // config is the share configuration. + ShareConfig config = 2; + + // state is the share lifecycle stage. + ShareStage state = 3; + + // mount carries the in-guest mount metadata for this share, when present. + MountState mount = 4; +} + +// ShareConfig is the host-side configuration of a 9P share. +message ShareConfig { + // read_only exposes the share read-only. + bool read_only = 1; + + // restrict enables single-file mapping mode in which only files listed in + // allowed_names are visible through the share. + bool restrict = 2; + + // allowed_names is the whitelist of file names used when restrict is true. + repeated string allowed_names = 3; +} + +// MountState is the in-guest mount metadata for a 9P share. +message MountState { + // read_only mounts the share read-only inside the guest. + bool read_only = 1; + + // state is the mount lifecycle stage. + MountStage state = 2; + + // ref_count is the number of live holders of this mount. + uint32 ref_count = 3; + + // guest_path is the mount point inside the guest. + string guest_path = 4; +} diff --git a/internal/controller/device/scsi/save/constants.go b/internal/controller/device/scsi/save/constants.go new file mode 100644 index 0000000000..ff9b32ec78 --- /dev/null +++ b/internal/controller/device/scsi/save/constants.go @@ -0,0 +1,16 @@ +//go:build windows && (lcow || wcow) + +// Package save defines the wire format owned by the SCSI sub-controller +// for live migration. The [Payload] message is self-contained and carries the +// SCSI sub-controller's serialized state (attached disks, in-guest mounts +// and outstanding reservations) across shims. +package save + +// SchemaVersion is the on-the-wire compatibility version stamped into +// [Payload.SchemaVersion]. Bump on any breaking change to payload.proto. +const SchemaVersion uint32 = 1 + +// TypeURL identifies a SCSI [Payload] when wrapped in an [anypb.Any]. It is +// opaque to clients and only meaningful between two shims that agree on +// [SchemaVersion]. +const TypeURL = "type.microsoft.com/hcsshim.controller.scsi.save.v1.Payload" diff --git a/internal/controller/device/scsi/save/payload.pb.go b/internal/controller/device/scsi/save/payload.pb.go new file mode 100644 index 0000000000..8f2f75e09f --- /dev/null +++ b/internal/controller/device/scsi/save/payload.pb.go @@ -0,0 +1,698 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v5.26.0 +// source: github.com/Microsoft/hcsshim/internal/controller/device/scsi/save/payload.proto + +package save + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + 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 DiskStage int32 + +const ( + DiskStage_DISK_STAGE_RESERVED DiskStage = 0 + DiskStage_DISK_STAGE_ATTACHED DiskStage = 1 + DiskStage_DISK_STAGE_EJECTED DiskStage = 2 + DiskStage_DISK_STAGE_DETACHED DiskStage = 3 +) + +// Enum value maps for DiskStage. +var ( + DiskStage_name = map[int32]string{ + 0: "DISK_STAGE_RESERVED", + 1: "DISK_STAGE_ATTACHED", + 2: "DISK_STAGE_EJECTED", + 3: "DISK_STAGE_DETACHED", + } + DiskStage_value = map[string]int32{ + "DISK_STAGE_RESERVED": 0, + "DISK_STAGE_ATTACHED": 1, + "DISK_STAGE_EJECTED": 2, + "DISK_STAGE_DETACHED": 3, + } +) + +func (x DiskStage) Enum() *DiskStage { + p := new(DiskStage) + *p = x + return p +} + +func (x DiskStage) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (DiskStage) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_enumTypes[0].Descriptor() +} + +func (DiskStage) Type() protoreflect.EnumType { + return &file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_enumTypes[0] +} + +func (x DiskStage) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use DiskStage.Descriptor instead. +func (DiskStage) EnumDescriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDescGZIP(), []int{0} +} + +type MountStage int32 + +const ( + MountStage_MOUNT_STAGE_RESERVED MountStage = 0 + MountStage_MOUNT_STAGE_MOUNTED MountStage = 1 + MountStage_MOUNT_STAGE_UNMOUNTED MountStage = 2 +) + +// Enum value maps for MountStage. +var ( + MountStage_name = map[int32]string{ + 0: "MOUNT_STAGE_RESERVED", + 1: "MOUNT_STAGE_MOUNTED", + 2: "MOUNT_STAGE_UNMOUNTED", + } + MountStage_value = map[string]int32{ + "MOUNT_STAGE_RESERVED": 0, + "MOUNT_STAGE_MOUNTED": 1, + "MOUNT_STAGE_UNMOUNTED": 2, + } +) + +func (x MountStage) Enum() *MountStage { + p := new(MountStage) + *p = x + return p +} + +func (x MountStage) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (MountStage) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_enumTypes[1].Descriptor() +} + +func (MountStage) Type() protoreflect.EnumType { + return &file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_enumTypes[1] +} + +func (x MountStage) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use MountStage.Descriptor instead. +func (MountStage) EnumDescriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDescGZIP(), []int{1} +} + +// Payload is the migration payload owned by the SCSI sub-controller. It is the +// top-level message wrapped in an [anypb.Any] when handed off between source +// and destination shims. +type Payload struct { + state protoimpl.MessageState `protogen:"open.v1"` + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + SchemaVersion uint32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"` + // num_controllers is the number of SCSI controllers attached to the VM. + // The destination uses it to size its slot table on restore. + NumControllers uint32 `protobuf:"varint,2,opt,name=num_controllers,json=numControllers,proto3" json:"num_controllers,omitempty"` + // disks is keyed by controller slot index (controller*64 + lun). + Disks map[uint32]*DiskState `protobuf:"bytes,3,rep,name=disks,proto3" json:"disks,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + // reservations is keyed by reservation GUID and tracks outstanding handles + // that containers hold against SCSI mounts. The string keys here are the + // values referenced by container controllers' scsi_reservation_ids. + Reservations map[string]*Reservation `protobuf:"bytes,4,rep,name=reservations,proto3" json:"reservations,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Payload) Reset() { + *x = Payload{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Payload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Payload) ProtoMessage() {} + +func (x *Payload) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_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 Payload.ProtoReflect.Descriptor instead. +func (*Payload) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDescGZIP(), []int{0} +} + +func (x *Payload) GetSchemaVersion() uint32 { + if x != nil { + return x.SchemaVersion + } + return 0 +} + +func (x *Payload) GetNumControllers() uint32 { + if x != nil { + return x.NumControllers + } + return 0 +} + +func (x *Payload) GetDisks() map[uint32]*DiskState { + if x != nil { + return x.Disks + } + return nil +} + +func (x *Payload) GetReservations() map[string]*Reservation { + if x != nil { + return x.Reservations + } + return nil +} + +// DiskState is a single SCSI-attached disk, keyed by controller slot index +// in [Payload.disks]. +type DiskState struct { + state protoimpl.MessageState `protogen:"open.v1"` + // config is the disk attachment configuration. + Config *DiskConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` + // state is the disk lifecycle stage. + State DiskStage `protobuf:"varint,2,opt,name=state,proto3,enum=hcsshim.controller.scsi.save.v1.DiskStage" json:"state,omitempty"` + // mounts is keyed by partition number on the disk. + Mounts map[uint64]*MountState `protobuf:"bytes,3,rep,name=mounts,proto3" json:"mounts,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DiskState) Reset() { + *x = DiskState{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DiskState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DiskState) ProtoMessage() {} + +func (x *DiskState) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_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 DiskState.ProtoReflect.Descriptor instead. +func (*DiskState) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDescGZIP(), []int{1} +} + +func (x *DiskState) GetConfig() *DiskConfig { + if x != nil { + return x.Config + } + return nil +} + +func (x *DiskState) GetState() DiskStage { + if x != nil { + return x.State + } + return DiskStage_DISK_STAGE_RESERVED +} + +func (x *DiskState) GetMounts() map[uint64]*MountState { + if x != nil { + return x.Mounts + } + return nil +} + +// DiskConfig is the host-side configuration of a SCSI-attached disk. +type DiskConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + // host_path is the path to the disk on the host. + HostPath string `protobuf:"bytes,1,opt,name=host_path,json=hostPath,proto3" json:"host_path,omitempty"` + // read_only attaches the disk read-only. + ReadOnly bool `protobuf:"varint,2,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` + // type is the disk kind: "VirtualDisk", "PassThru" or + // "ExtensibleVirtualDisk". + Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` + // evd_type is the extensible-virtual-disk provider name. Only meaningful + // when type == "ExtensibleVirtualDisk". + EvdType string `protobuf:"bytes,4,opt,name=evd_type,json=evdType,proto3" json:"evd_type,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DiskConfig) Reset() { + *x = DiskConfig{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DiskConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DiskConfig) ProtoMessage() {} + +func (x *DiskConfig) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_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 DiskConfig.ProtoReflect.Descriptor instead. +func (*DiskConfig) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDescGZIP(), []int{2} +} + +func (x *DiskConfig) GetHostPath() string { + if x != nil { + return x.HostPath + } + return "" +} + +func (x *DiskConfig) GetReadOnly() bool { + if x != nil { + return x.ReadOnly + } + return false +} + +func (x *DiskConfig) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +func (x *DiskConfig) GetEvdType() string { + if x != nil { + return x.EvdType + } + return "" +} + +// MountState is the in-guest mount metadata for a single partition of a +// SCSI-attached disk. +type MountState struct { + state protoimpl.MessageState `protogen:"open.v1"` + // config is the mount configuration. + Config *MountConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` + // state is the mount lifecycle stage. + State MountStage `protobuf:"varint,2,opt,name=state,proto3,enum=hcsshim.controller.scsi.save.v1.MountStage" json:"state,omitempty"` + // ref_count is the number of live holders of this mount. + RefCount uint32 `protobuf:"varint,3,opt,name=ref_count,json=refCount,proto3" json:"ref_count,omitempty"` + // guest_path is where the partition is mounted inside the guest. + GuestPath string `protobuf:"bytes,4,opt,name=guest_path,json=guestPath,proto3" json:"guest_path,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MountState) Reset() { + *x = MountState{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MountState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MountState) ProtoMessage() {} + +func (x *MountState) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_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 MountState.ProtoReflect.Descriptor instead. +func (*MountState) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDescGZIP(), []int{3} +} + +func (x *MountState) GetConfig() *MountConfig { + if x != nil { + return x.Config + } + return nil +} + +func (x *MountState) GetState() MountStage { + if x != nil { + return x.State + } + return MountStage_MOUNT_STAGE_RESERVED +} + +func (x *MountState) GetRefCount() uint32 { + if x != nil { + return x.RefCount + } + return 0 +} + +func (x *MountState) GetGuestPath() string { + if x != nil { + return x.GuestPath + } + return "" +} + +// MountConfig is the guest-side configuration of a SCSI partition mount. +type MountConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + // read_only mounts the partition read-only inside the guest. + ReadOnly bool `protobuf:"varint,1,opt,name=read_only,json=readOnly,proto3" json:"read_only,omitempty"` + // encrypted wraps the partition with dm-crypt before mounting. + Encrypted bool `protobuf:"varint,2,opt,name=encrypted,proto3" json:"encrypted,omitempty"` + // options are extra mount options passed to the guest (e.g. "noatime"). + Options []string `protobuf:"bytes,3,rep,name=options,proto3" json:"options,omitempty"` + // ensure_filesystem formats the partition if it has no filesystem. + EnsureFilesystem bool `protobuf:"varint,4,opt,name=ensure_filesystem,json=ensureFilesystem,proto3" json:"ensure_filesystem,omitempty"` + // filesystem is the filesystem type to use when formatting or mounting + // (e.g. "ext4"). + Filesystem string `protobuf:"bytes,5,opt,name=filesystem,proto3" json:"filesystem,omitempty"` + // block_dev exposes the partition as a raw block device instead of + // mounting it. + BlockDev bool `protobuf:"varint,6,opt,name=block_dev,json=blockDev,proto3" json:"block_dev,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MountConfig) Reset() { + *x = MountConfig{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MountConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MountConfig) ProtoMessage() {} + +func (x *MountConfig) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_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 MountConfig.ProtoReflect.Descriptor instead. +func (*MountConfig) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDescGZIP(), []int{4} +} + +func (x *MountConfig) GetReadOnly() bool { + if x != nil { + return x.ReadOnly + } + return false +} + +func (x *MountConfig) GetEncrypted() bool { + if x != nil { + return x.Encrypted + } + return false +} + +func (x *MountConfig) GetOptions() []string { + if x != nil { + return x.Options + } + return nil +} + +func (x *MountConfig) GetEnsureFilesystem() bool { + if x != nil { + return x.EnsureFilesystem + } + return false +} + +func (x *MountConfig) GetFilesystem() string { + if x != nil { + return x.Filesystem + } + return "" +} + +func (x *MountConfig) GetBlockDev() bool { + if x != nil { + return x.BlockDev + } + return false +} + +// Reservation is an outstanding handle a container holds against a SCSI +// partition mount. +type Reservation struct { + state protoimpl.MessageState `protogen:"open.v1"` + // slot is the controllerSlot index (controller*64 + lun). + Slot uint32 `protobuf:"varint,1,opt,name=slot,proto3" json:"slot,omitempty"` + // partition is the partition number on the disk that this reservation + // refers to. + Partition uint64 `protobuf:"varint,2,opt,name=partition,proto3" json:"partition,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Reservation) Reset() { + *x = Reservation{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Reservation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Reservation) ProtoMessage() {} + +func (x *Reservation) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_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 Reservation.ProtoReflect.Descriptor instead. +func (*Reservation) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDescGZIP(), []int{5} +} + +func (x *Reservation) GetSlot() uint32 { + if x != nil { + return x.Slot + } + return 0 +} + +func (x *Reservation) GetPartition() uint64 { + if x != nil { + return x.Partition + } + return 0 +} + +var File_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto protoreflect.FileDescriptor + +const file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDesc = "" + + "\n" + + "Ogithub.com/Microsoft/hcsshim/internal/controller/device/scsi/save/payload.proto\x12\x1fhcsshim.controller.scsi.save.v1\"\xd9\x03\n" + + "\aPayload\x12%\n" + + "\x0eschema_version\x18\x01 \x01(\rR\rschemaVersion\x12'\n" + + "\x0fnum_controllers\x18\x02 \x01(\rR\x0enumControllers\x12I\n" + + "\x05disks\x18\x03 \x03(\v23.hcsshim.controller.scsi.save.v1.Payload.DisksEntryR\x05disks\x12^\n" + + "\freservations\x18\x04 \x03(\v2:.hcsshim.controller.scsi.save.v1.Payload.ReservationsEntryR\freservations\x1ad\n" + + "\n" + + "DisksEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\rR\x03key\x12@\n" + + "\x05value\x18\x02 \x01(\v2*.hcsshim.controller.scsi.save.v1.DiskStateR\x05value:\x028\x01\x1am\n" + + "\x11ReservationsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12B\n" + + "\x05value\x18\x02 \x01(\v2,.hcsshim.controller.scsi.save.v1.ReservationR\x05value:\x028\x01\"\xca\x02\n" + + "\tDiskState\x12C\n" + + "\x06config\x18\x01 \x01(\v2+.hcsshim.controller.scsi.save.v1.DiskConfigR\x06config\x12@\n" + + "\x05state\x18\x02 \x01(\x0e2*.hcsshim.controller.scsi.save.v1.DiskStageR\x05state\x12N\n" + + "\x06mounts\x18\x03 \x03(\v26.hcsshim.controller.scsi.save.v1.DiskState.MountsEntryR\x06mounts\x1af\n" + + "\vMountsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\x04R\x03key\x12A\n" + + "\x05value\x18\x02 \x01(\v2+.hcsshim.controller.scsi.save.v1.MountStateR\x05value:\x028\x01\"u\n" + + "\n" + + "DiskConfig\x12\x1b\n" + + "\thost_path\x18\x01 \x01(\tR\bhostPath\x12\x1b\n" + + "\tread_only\x18\x02 \x01(\bR\breadOnly\x12\x12\n" + + "\x04type\x18\x03 \x01(\tR\x04type\x12\x19\n" + + "\bevd_type\x18\x04 \x01(\tR\aevdType\"\xd1\x01\n" + + "\n" + + "MountState\x12D\n" + + "\x06config\x18\x01 \x01(\v2,.hcsshim.controller.scsi.save.v1.MountConfigR\x06config\x12A\n" + + "\x05state\x18\x02 \x01(\x0e2+.hcsshim.controller.scsi.save.v1.MountStageR\x05state\x12\x1b\n" + + "\tref_count\x18\x03 \x01(\rR\brefCount\x12\x1d\n" + + "\n" + + "guest_path\x18\x04 \x01(\tR\tguestPath\"\xcc\x01\n" + + "\vMountConfig\x12\x1b\n" + + "\tread_only\x18\x01 \x01(\bR\breadOnly\x12\x1c\n" + + "\tencrypted\x18\x02 \x01(\bR\tencrypted\x12\x18\n" + + "\aoptions\x18\x03 \x03(\tR\aoptions\x12+\n" + + "\x11ensure_filesystem\x18\x04 \x01(\bR\x10ensureFilesystem\x12\x1e\n" + + "\n" + + "filesystem\x18\x05 \x01(\tR\n" + + "filesystem\x12\x1b\n" + + "\tblock_dev\x18\x06 \x01(\bR\bblockDev\"?\n" + + "\vReservation\x12\x12\n" + + "\x04slot\x18\x01 \x01(\rR\x04slot\x12\x1c\n" + + "\tpartition\x18\x02 \x01(\x04R\tpartition*n\n" + + "\tDiskStage\x12\x17\n" + + "\x13DISK_STAGE_RESERVED\x10\x00\x12\x17\n" + + "\x13DISK_STAGE_ATTACHED\x10\x01\x12\x16\n" + + "\x12DISK_STAGE_EJECTED\x10\x02\x12\x17\n" + + "\x13DISK_STAGE_DETACHED\x10\x03*Z\n" + + "\n" + + "MountStage\x12\x18\n" + + "\x14MOUNT_STAGE_RESERVED\x10\x00\x12\x17\n" + + "\x13MOUNT_STAGE_MOUNTED\x10\x01\x12\x19\n" + + "\x15MOUNT_STAGE_UNMOUNTED\x10\x02BHZFgithub.com/Microsoft/hcsshim/internal/controller/device/scsi/save;saveb\x06proto3" + +var ( + file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDescOnce sync.Once + file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDescData []byte +) + +func file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDescGZIP() []byte { + file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDescOnce.Do(func() { + file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDesc))) + }) + return file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDescData +} + +var file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_goTypes = []any{ + (DiskStage)(0), // 0: hcsshim.controller.scsi.save.v1.DiskStage + (MountStage)(0), // 1: hcsshim.controller.scsi.save.v1.MountStage + (*Payload)(nil), // 2: hcsshim.controller.scsi.save.v1.Payload + (*DiskState)(nil), // 3: hcsshim.controller.scsi.save.v1.DiskState + (*DiskConfig)(nil), // 4: hcsshim.controller.scsi.save.v1.DiskConfig + (*MountState)(nil), // 5: hcsshim.controller.scsi.save.v1.MountState + (*MountConfig)(nil), // 6: hcsshim.controller.scsi.save.v1.MountConfig + (*Reservation)(nil), // 7: hcsshim.controller.scsi.save.v1.Reservation + nil, // 8: hcsshim.controller.scsi.save.v1.Payload.DisksEntry + nil, // 9: hcsshim.controller.scsi.save.v1.Payload.ReservationsEntry + nil, // 10: hcsshim.controller.scsi.save.v1.DiskState.MountsEntry +} +var file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_depIdxs = []int32{ + 8, // 0: hcsshim.controller.scsi.save.v1.Payload.disks:type_name -> hcsshim.controller.scsi.save.v1.Payload.DisksEntry + 9, // 1: hcsshim.controller.scsi.save.v1.Payload.reservations:type_name -> hcsshim.controller.scsi.save.v1.Payload.ReservationsEntry + 4, // 2: hcsshim.controller.scsi.save.v1.DiskState.config:type_name -> hcsshim.controller.scsi.save.v1.DiskConfig + 0, // 3: hcsshim.controller.scsi.save.v1.DiskState.state:type_name -> hcsshim.controller.scsi.save.v1.DiskStage + 10, // 4: hcsshim.controller.scsi.save.v1.DiskState.mounts:type_name -> hcsshim.controller.scsi.save.v1.DiskState.MountsEntry + 6, // 5: hcsshim.controller.scsi.save.v1.MountState.config:type_name -> hcsshim.controller.scsi.save.v1.MountConfig + 1, // 6: hcsshim.controller.scsi.save.v1.MountState.state:type_name -> hcsshim.controller.scsi.save.v1.MountStage + 3, // 7: hcsshim.controller.scsi.save.v1.Payload.DisksEntry.value:type_name -> hcsshim.controller.scsi.save.v1.DiskState + 7, // 8: hcsshim.controller.scsi.save.v1.Payload.ReservationsEntry.value:type_name -> hcsshim.controller.scsi.save.v1.Reservation + 5, // 9: hcsshim.controller.scsi.save.v1.DiskState.MountsEntry.value:type_name -> hcsshim.controller.scsi.save.v1.MountState + 10, // [10:10] is the sub-list for method output_type + 10, // [10:10] is the sub-list for method input_type + 10, // [10:10] is the sub-list for extension type_name + 10, // [10:10] is the sub-list for extension extendee + 0, // [0:10] is the sub-list for field type_name +} + +func init() { + file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_init() +} +func file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_init() { + if File_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_rawDesc)), + NumEnums: 2, + NumMessages: 9, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_goTypes, + DependencyIndexes: file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_depIdxs, + EnumInfos: file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_enumTypes, + MessageInfos: file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_msgTypes, + }.Build() + File_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto = out.File + file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_goTypes = nil + file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_depIdxs = nil +} diff --git a/internal/controller/device/scsi/save/payload.proto b/internal/controller/device/scsi/save/payload.proto new file mode 100644 index 0000000000..2e9c820a13 --- /dev/null +++ b/internal/controller/device/scsi/save/payload.proto @@ -0,0 +1,126 @@ +syntax = "proto3"; + +package hcsshim.controller.scsi.save.v1; + +option go_package = "github.com/Microsoft/hcsshim/internal/controller/device/scsi/save;save"; + +// ============================================================================= +// Lifecycle stage enums +// +// Each enum mirrors a Go State type used by the SCSI sub-controller. The zero +// value matches the Go iota-zero value (Reserved). +// ============================================================================= + +enum DiskStage { + DISK_STAGE_RESERVED = 0; + DISK_STAGE_ATTACHED = 1; + DISK_STAGE_EJECTED = 2; + DISK_STAGE_DETACHED = 3; +} + +enum MountStage { + MOUNT_STAGE_RESERVED = 0; + MOUNT_STAGE_MOUNTED = 1; + MOUNT_STAGE_UNMOUNTED = 2; +} + +// Payload is the migration payload owned by the SCSI sub-controller. It is the +// top-level message wrapped in an [anypb.Any] when handed off between source +// and destination shims. +message Payload { + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + uint32 schema_version = 1; + + // num_controllers is the number of SCSI controllers attached to the VM. + // The destination uses it to size its slot table on restore. + uint32 num_controllers = 2; + + // disks is keyed by controller slot index (controller*64 + lun). + map disks = 3; + + // reservations is keyed by reservation GUID and tracks outstanding handles + // that containers hold against SCSI mounts. The string keys here are the + // values referenced by container controllers' scsi_reservation_ids. + map reservations = 4; +} + +// DiskState is a single SCSI-attached disk, keyed by controller slot index +// in [Payload.disks]. +message DiskState { + // config is the disk attachment configuration. + DiskConfig config = 1; + + // state is the disk lifecycle stage. + DiskStage state = 2; + + // mounts is keyed by partition number on the disk. + map mounts = 3; +} + +// DiskConfig is the host-side configuration of a SCSI-attached disk. +message DiskConfig { + // host_path is the path to the disk on the host. + string host_path = 1; + + // read_only attaches the disk read-only. + bool read_only = 2; + + // type is the disk kind: "VirtualDisk", "PassThru" or + // "ExtensibleVirtualDisk". + string type = 3; + + // evd_type is the extensible-virtual-disk provider name. Only meaningful + // when type == "ExtensibleVirtualDisk". + string evd_type = 4; +} + +// MountState is the in-guest mount metadata for a single partition of a +// SCSI-attached disk. +message MountState { + // config is the mount configuration. + MountConfig config = 1; + + // state is the mount lifecycle stage. + MountStage state = 2; + + // ref_count is the number of live holders of this mount. + uint32 ref_count = 3; + + // guest_path is where the partition is mounted inside the guest. + string guest_path = 4; +} + +// MountConfig is the guest-side configuration of a SCSI partition mount. +message MountConfig { + // read_only mounts the partition read-only inside the guest. + bool read_only = 1; + + // encrypted wraps the partition with dm-crypt before mounting. + bool encrypted = 2; + + // options are extra mount options passed to the guest (e.g. "noatime"). + repeated string options = 3; + + // ensure_filesystem formats the partition if it has no filesystem. + bool ensure_filesystem = 4; + + // filesystem is the filesystem type to use when formatting or mounting + // (e.g. "ext4"). + string filesystem = 5; + + // block_dev exposes the partition as a raw block device instead of + // mounting it. + bool block_dev = 6; +} + +// Reservation is an outstanding handle a container holds against a SCSI +// partition mount. +message Reservation { + // slot is the controllerSlot index (controller*64 + lun). + uint32 slot = 1; + + // partition is the partition number on the disk that this reservation + // refers to. + uint64 partition = 2; +} diff --git a/internal/controller/device/vpci/save/constants.go b/internal/controller/device/vpci/save/constants.go new file mode 100644 index 0000000000..d0ec55ebbd --- /dev/null +++ b/internal/controller/device/vpci/save/constants.go @@ -0,0 +1,16 @@ +//go:build windows && (lcow || wcow) + +// Package save defines the wire format owned by the VPCI sub-controller for +// live migration. The [Payload] message is self-contained and carries the +// VPCI sub-controller's serialized state (assigned VPCI devices and their +// reference counts) across shims. +package save + +// SchemaVersion is the on-the-wire compatibility version stamped into +// [Payload.SchemaVersion]. Bump on any breaking change to payload.proto. +const SchemaVersion uint32 = 1 + +// TypeURL identifies a VPCI [Payload] when wrapped in an [anypb.Any]. It is +// opaque to clients and only meaningful between two shims that agree on +// [SchemaVersion]. +const TypeURL = "type.microsoft.com/hcsshim.controller.vpci.save.v1.Payload" diff --git a/internal/controller/device/vpci/save/payload.pb.go b/internal/controller/device/vpci/save/payload.pb.go new file mode 100644 index 0000000000..691e02a359 --- /dev/null +++ b/internal/controller/device/vpci/save/payload.pb.go @@ -0,0 +1,292 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v5.26.0 +// source: github.com/Microsoft/hcsshim/internal/controller/device/vpci/save/payload.proto + +package save + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + 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 DeviceStage int32 + +const ( + DeviceStage_DEVICE_STAGE_RESERVED DeviceStage = 0 + DeviceStage_DEVICE_STAGE_ASSIGNED DeviceStage = 1 + DeviceStage_DEVICE_STAGE_READY DeviceStage = 2 + DeviceStage_DEVICE_STAGE_ASSIGNED_INVALID DeviceStage = 3 + DeviceStage_DEVICE_STAGE_REMOVED DeviceStage = 4 +) + +// Enum value maps for DeviceStage. +var ( + DeviceStage_name = map[int32]string{ + 0: "DEVICE_STAGE_RESERVED", + 1: "DEVICE_STAGE_ASSIGNED", + 2: "DEVICE_STAGE_READY", + 3: "DEVICE_STAGE_ASSIGNED_INVALID", + 4: "DEVICE_STAGE_REMOVED", + } + DeviceStage_value = map[string]int32{ + "DEVICE_STAGE_RESERVED": 0, + "DEVICE_STAGE_ASSIGNED": 1, + "DEVICE_STAGE_READY": 2, + "DEVICE_STAGE_ASSIGNED_INVALID": 3, + "DEVICE_STAGE_REMOVED": 4, + } +) + +func (x DeviceStage) Enum() *DeviceStage { + p := new(DeviceStage) + *p = x + return p +} + +func (x DeviceStage) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (DeviceStage) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_enumTypes[0].Descriptor() +} + +func (DeviceStage) Type() protoreflect.EnumType { + return &file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_enumTypes[0] +} + +func (x DeviceStage) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use DeviceStage.Descriptor instead. +func (DeviceStage) EnumDescriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_rawDescGZIP(), []int{0} +} + +// Payload is the migration payload owned by the VPCI sub-controller. It is the +// top-level message wrapped in an [anypb.Any] when handed off between source +// and destination shims. +type Payload struct { + state protoimpl.MessageState `protogen:"open.v1"` + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + SchemaVersion uint32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"` + // devices is keyed by VMBus channel GUID and contains every VPCI device + // currently tracked by the controller. The string keys here are the + // values referenced by container controllers' vpci_vmbus_guids. + Devices map[string]*DeviceState `protobuf:"bytes,2,rep,name=devices,proto3" json:"devices,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Payload) Reset() { + *x = Payload{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Payload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Payload) ProtoMessage() {} + +func (x *Payload) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_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 Payload.ProtoReflect.Descriptor instead. +func (*Payload) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_rawDescGZIP(), []int{0} +} + +func (x *Payload) GetSchemaVersion() uint32 { + if x != nil { + return x.SchemaVersion + } + return 0 +} + +func (x *Payload) GetDevices() map[string]*DeviceState { + if x != nil { + return x.Devices + } + return nil +} + +// DeviceState is a single VPCI device tracked by the controller, keyed by +// VMBus channel GUID in [Payload.devices]. +type DeviceState struct { + state protoimpl.MessageState `protogen:"open.v1"` + // device_instance_id is the host PnP device instance ID. + DeviceInstanceID string `protobuf:"bytes,1,opt,name=device_instance_id,json=deviceInstanceId,proto3" json:"device_instance_id,omitempty"` + // virtual_function_index is the SR-IOV virtual-function index. + VirtualFunctionIndex uint32 `protobuf:"varint,2,opt,name=virtual_function_index,json=virtualFunctionIndex,proto3" json:"virtual_function_index,omitempty"` + // state is the device lifecycle stage. + State DeviceStage `protobuf:"varint,3,opt,name=state,proto3,enum=hcsshim.controller.vpci.save.v1.DeviceStage" json:"state,omitempty"` + // ref_count is the number of containers currently sharing this device. + RefCount uint32 `protobuf:"varint,4,opt,name=ref_count,json=refCount,proto3" json:"ref_count,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeviceState) Reset() { + *x = DeviceState{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeviceState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeviceState) ProtoMessage() {} + +func (x *DeviceState) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_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 DeviceState.ProtoReflect.Descriptor instead. +func (*DeviceState) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_rawDescGZIP(), []int{1} +} + +func (x *DeviceState) GetDeviceInstanceID() string { + if x != nil { + return x.DeviceInstanceID + } + return "" +} + +func (x *DeviceState) GetVirtualFunctionIndex() uint32 { + if x != nil { + return x.VirtualFunctionIndex + } + return 0 +} + +func (x *DeviceState) GetState() DeviceStage { + if x != nil { + return x.State + } + return DeviceStage_DEVICE_STAGE_RESERVED +} + +func (x *DeviceState) GetRefCount() uint32 { + if x != nil { + return x.RefCount + } + return 0 +} + +var File_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto protoreflect.FileDescriptor + +const file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_rawDesc = "" + + "\n" + + "Ogithub.com/Microsoft/hcsshim/internal/controller/device/vpci/save/payload.proto\x12\x1fhcsshim.controller.vpci.save.v1\"\xeb\x01\n" + + "\aPayload\x12%\n" + + "\x0eschema_version\x18\x01 \x01(\rR\rschemaVersion\x12O\n" + + "\adevices\x18\x02 \x03(\v25.hcsshim.controller.vpci.save.v1.Payload.DevicesEntryR\adevices\x1ah\n" + + "\fDevicesEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12B\n" + + "\x05value\x18\x02 \x01(\v2,.hcsshim.controller.vpci.save.v1.DeviceStateR\x05value:\x028\x01\"\xd2\x01\n" + + "\vDeviceState\x12,\n" + + "\x12device_instance_id\x18\x01 \x01(\tR\x10deviceInstanceId\x124\n" + + "\x16virtual_function_index\x18\x02 \x01(\rR\x14virtualFunctionIndex\x12B\n" + + "\x05state\x18\x03 \x01(\x0e2,.hcsshim.controller.vpci.save.v1.DeviceStageR\x05state\x12\x1b\n" + + "\tref_count\x18\x04 \x01(\rR\brefCount*\x98\x01\n" + + "\vDeviceStage\x12\x19\n" + + "\x15DEVICE_STAGE_RESERVED\x10\x00\x12\x19\n" + + "\x15DEVICE_STAGE_ASSIGNED\x10\x01\x12\x16\n" + + "\x12DEVICE_STAGE_READY\x10\x02\x12!\n" + + "\x1dDEVICE_STAGE_ASSIGNED_INVALID\x10\x03\x12\x18\n" + + "\x14DEVICE_STAGE_REMOVED\x10\x04BHZFgithub.com/Microsoft/hcsshim/internal/controller/device/vpci/save;saveb\x06proto3" + +var ( + file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_rawDescOnce sync.Once + file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_rawDescData []byte +) + +func file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_rawDescGZIP() []byte { + file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_rawDescOnce.Do(func() { + file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_rawDesc))) + }) + return file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_rawDescData +} + +var file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_goTypes = []any{ + (DeviceStage)(0), // 0: hcsshim.controller.vpci.save.v1.DeviceStage + (*Payload)(nil), // 1: hcsshim.controller.vpci.save.v1.Payload + (*DeviceState)(nil), // 2: hcsshim.controller.vpci.save.v1.DeviceState + nil, // 3: hcsshim.controller.vpci.save.v1.Payload.DevicesEntry +} +var file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_depIdxs = []int32{ + 3, // 0: hcsshim.controller.vpci.save.v1.Payload.devices:type_name -> hcsshim.controller.vpci.save.v1.Payload.DevicesEntry + 0, // 1: hcsshim.controller.vpci.save.v1.DeviceState.state:type_name -> hcsshim.controller.vpci.save.v1.DeviceStage + 2, // 2: hcsshim.controller.vpci.save.v1.Payload.DevicesEntry.value:type_name -> hcsshim.controller.vpci.save.v1.DeviceState + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { + file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_init() +} +func file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_init() { + if File_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_rawDesc)), + NumEnums: 1, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_goTypes, + DependencyIndexes: file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_depIdxs, + EnumInfos: file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_enumTypes, + MessageInfos: file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_msgTypes, + }.Build() + File_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto = out.File + file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_goTypes = nil + file_github_com_Microsoft_hcsshim_internal_controller_device_vpci_save_payload_proto_depIdxs = nil +} diff --git a/internal/controller/device/vpci/save/payload.proto b/internal/controller/device/vpci/save/payload.proto new file mode 100644 index 0000000000..2c387e018d --- /dev/null +++ b/internal/controller/device/vpci/save/payload.proto @@ -0,0 +1,50 @@ +syntax = "proto3"; + +package hcsshim.controller.vpci.save.v1; + +option go_package = "github.com/Microsoft/hcsshim/internal/controller/device/vpci/save;save"; + +// ============================================================================= +// Lifecycle stage enum +// +// DeviceStage mirrors the Go State type used by the VPCI sub-controller. The +// zero value matches the Go iota-zero value (Reserved). +// ============================================================================= + +enum DeviceStage { + DEVICE_STAGE_RESERVED = 0; + DEVICE_STAGE_ASSIGNED = 1; + DEVICE_STAGE_READY = 2; + DEVICE_STAGE_ASSIGNED_INVALID = 3; + DEVICE_STAGE_REMOVED = 4; +} + +// Payload is the migration payload owned by the VPCI sub-controller. It is the +// top-level message wrapped in an [anypb.Any] when handed off between source +// and destination shims. +message Payload { + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + uint32 schema_version = 1; + + // devices is keyed by VMBus channel GUID and contains every VPCI device + // currently tracked by the controller. The string keys here are the + // values referenced by container controllers' vpci_vmbus_guids. + map devices = 2; +} + +// DeviceState is a single VPCI device tracked by the controller, keyed by +// VMBus channel GUID in [Payload.devices]. +message DeviceState { + // device_instance_id is the host PnP device instance ID. + string device_instance_id = 1; + + // virtual_function_index is the SR-IOV virtual-function index. + uint32 virtual_function_index = 2; + + // state is the device lifecycle stage. + DeviceStage state = 3; + + // ref_count is the number of containers currently sharing this device. + uint32 ref_count = 4; +} diff --git a/internal/controller/linuxcontainer/save/constants.go b/internal/controller/linuxcontainer/save/constants.go new file mode 100644 index 0000000000..98c70898a7 --- /dev/null +++ b/internal/controller/linuxcontainer/save/constants.go @@ -0,0 +1,17 @@ +//go:build windows && lcow + +// Package save defines the wire format owned by the linuxcontainer +// controller for live migration. The [Payload] envelope carries the +// container's bookkeeping plus child process states as opaque [anypb.Any] +// payloads; this package owns the envelope itself, not the inner process +// controller schema. +package save + +// SchemaVersion is the on-the-wire compatibility version stamped into +// [Payload.SchemaVersion]. Bump on any breaking change to payload.proto. +const SchemaVersion uint32 = 1 + +// TypeURL identifies a linuxcontainer [Payload] when wrapped in an +// [anypb.Any]. It is opaque to clients and only meaningful between two +// shims that agree on [SchemaVersion]. +const TypeURL = "type.microsoft.com/hcsshim.controller.linuxcontainer.save.v1.Payload" diff --git a/internal/controller/linuxcontainer/save/payload.pb.go b/internal/controller/linuxcontainer/save/payload.pb.go new file mode 100644 index 0000000000..27915e1b4f --- /dev/null +++ b/internal/controller/linuxcontainer/save/payload.pb.go @@ -0,0 +1,458 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v5.26.0 +// source: github.com/Microsoft/hcsshim/internal/controller/linuxcontainer/save/payload.proto + +package save + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + durationpb "google.golang.org/protobuf/types/known/durationpb" + 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 Stage int32 + +const ( + Stage_STAGE_NOT_CREATED Stage = 0 + Stage_STAGE_CREATED Stage = 1 + Stage_STAGE_RUNNING Stage = 2 + Stage_STAGE_STOPPED Stage = 3 + Stage_STAGE_INVALID Stage = 4 + Stage_STAGE_MIGRATING Stage = 5 +) + +// Enum value maps for Stage. +var ( + Stage_name = map[int32]string{ + 0: "STAGE_NOT_CREATED", + 1: "STAGE_CREATED", + 2: "STAGE_RUNNING", + 3: "STAGE_STOPPED", + 4: "STAGE_INVALID", + 5: "STAGE_MIGRATING", + } + Stage_value = map[string]int32{ + "STAGE_NOT_CREATED": 0, + "STAGE_CREATED": 1, + "STAGE_RUNNING": 2, + "STAGE_STOPPED": 3, + "STAGE_INVALID": 4, + "STAGE_MIGRATING": 5, + } +) + +func (x Stage) Enum() *Stage { + p := new(Stage) + *p = x + return p +} + +func (x Stage) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Stage) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_enumTypes[0].Descriptor() +} + +func (Stage) Type() protoreflect.EnumType { + return &file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_enumTypes[0] +} + +func (x Stage) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Stage.Descriptor instead. +func (Stage) EnumDescriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_rawDescGZIP(), []int{0} +} + +// Payload is the migration payload owned by a single linuxcontainer +// controller. It is the top-level message wrapped in an [anypb.Any] when +// handed off between source and destination shims. Embedded process states +// are carried as opaque [anypb.Any] payloads so the process controller fully +// owns its own schema and versioning. +type Payload struct { + state protoimpl.MessageState `protogen:"open.v1"` + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + SchemaVersion uint32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"` + // container_id is the host/shim container identifier. + ContainerID string `protobuf:"bytes,2,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` + // gcs_container_id is the container identifier as known by the GCS inside + // the guest, which may differ from container_id. + GcsContainerID string `protobuf:"bytes,3,opt,name=gcs_container_id,json=gcsContainerId,proto3" json:"gcs_container_id,omitempty"` + // state is the container lifecycle stage. + State Stage `protobuf:"varint,4,opt,name=state,proto3,enum=hcsshim.controller.linuxcontainer.save.v1.Stage" json:"state,omitempty"` + // io_retry_timeout is how long IO relay reconnects are retried for this + // container. + IoRetryTimeout *durationpb.Duration `protobuf:"bytes,5,opt,name=io_retry_timeout,json=ioRetryTimeout,proto3" json:"io_retry_timeout,omitempty"` + // layers is the container's rootfs composition. + Layers *Layers `protobuf:"bytes,6,opt,name=layers,proto3" json:"layers,omitempty"` + // scsi_reservation_ids are the SCSI reservation GUIDs the container owns. + // Each value is a key into the SCSI sub-controller's reservations map. + ScsiReservationIds []string `protobuf:"bytes,7,rep,name=scsi_reservation_ids,json=scsiReservationIds,proto3" json:"scsi_reservation_ids,omitempty"` + // plan9_reservation_ids are the Plan9 reservation GUIDs the container + // owns. Each value is a key into the Plan9 sub-controller's reservations + // map. + Plan9ReservationIds []string `protobuf:"bytes,8,rep,name=plan9_reservation_ids,json=plan9ReservationIds,proto3" json:"plan9_reservation_ids,omitempty"` + // vpci_vmbus_guids are the VMBus GUIDs of VPCI devices the container + // uses. Each value is a key into the VPCI sub-controller's devices map. + VpciVmbusGuids []string `protobuf:"bytes,9,rep,name=vpci_vmbus_guids,json=vpciVmbusGuids,proto3" json:"vpci_vmbus_guids,omitempty"` + // processes is keyed by exec ID and contains the init process (with an + // empty exec ID) plus any additional exec'd processes. Each value is an + // opaque process-controller [Payload] envelope. + Processes map[string]*anypb.Any `protobuf:"bytes,10,rep,name=processes,proto3" json:"processes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Payload) Reset() { + *x = Payload{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Payload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Payload) ProtoMessage() {} + +func (x *Payload) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_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 Payload.ProtoReflect.Descriptor instead. +func (*Payload) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_rawDescGZIP(), []int{0} +} + +func (x *Payload) GetSchemaVersion() uint32 { + if x != nil { + return x.SchemaVersion + } + return 0 +} + +func (x *Payload) GetContainerID() string { + if x != nil { + return x.ContainerID + } + return "" +} + +func (x *Payload) GetGcsContainerID() string { + if x != nil { + return x.GcsContainerID + } + return "" +} + +func (x *Payload) GetState() Stage { + if x != nil { + return x.State + } + return Stage_STAGE_NOT_CREATED +} + +func (x *Payload) GetIoRetryTimeout() *durationpb.Duration { + if x != nil { + return x.IoRetryTimeout + } + return nil +} + +func (x *Payload) GetLayers() *Layers { + if x != nil { + return x.Layers + } + return nil +} + +func (x *Payload) GetScsiReservationIds() []string { + if x != nil { + return x.ScsiReservationIds + } + return nil +} + +func (x *Payload) GetPlan9ReservationIds() []string { + if x != nil { + return x.Plan9ReservationIds + } + return nil +} + +func (x *Payload) GetVpciVmbusGuids() []string { + if x != nil { + return x.VpciVmbusGuids + } + return nil +} + +func (x *Payload) GetProcesses() map[string]*anypb.Any { + if x != nil { + return x.Processes + } + return nil +} + +// Layers describes the container's rootfs composition. +type Layers struct { + state protoimpl.MessageState `protogen:"open.v1"` + // ro_layers are the read-only layer reservations in stack order. + RoLayers []*LayerReservation `protobuf:"bytes,1,rep,name=ro_layers,json=roLayers,proto3" json:"ro_layers,omitempty"` + // scratch is the writable scratch layer reservation. + Scratch *LayerReservation `protobuf:"bytes,2,opt,name=scratch,proto3" json:"scratch,omitempty"` + // layers_combined is true when the layers were merged into a single + // rootfs (e.g. overlay) rather than mounted individually. + LayersCombined bool `protobuf:"varint,3,opt,name=layers_combined,json=layersCombined,proto3" json:"layers_combined,omitempty"` + // rootfs_path is the final guest path of the container rootfs: the + // merged mount when layers_combined is true, otherwise the topmost + // layer mount. + RootfsPath string `protobuf:"bytes,4,opt,name=rootfs_path,json=rootfsPath,proto3" json:"rootfs_path,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Layers) Reset() { + *x = Layers{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Layers) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Layers) ProtoMessage() {} + +func (x *Layers) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_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 Layers.ProtoReflect.Descriptor instead. +func (*Layers) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_rawDescGZIP(), []int{1} +} + +func (x *Layers) GetRoLayers() []*LayerReservation { + if x != nil { + return x.RoLayers + } + return nil +} + +func (x *Layers) GetScratch() *LayerReservation { + if x != nil { + return x.Scratch + } + return nil +} + +func (x *Layers) GetLayersCombined() bool { + if x != nil { + return x.LayersCombined + } + return false +} + +func (x *Layers) GetRootfsPath() string { + if x != nil { + return x.RootfsPath + } + return "" +} + +// LayerReservation references a single SCSI-backed layer mounted into the +// guest. +type LayerReservation struct { + state protoimpl.MessageState `protogen:"open.v1"` + // reservation_id is a SCSI reservation GUID; layers are SCSI-backed VHDs + // and this is a key into the SCSI sub-controller's reservations map. + ReservationID string `protobuf:"bytes,1,opt,name=reservation_id,json=reservationId,proto3" json:"reservation_id,omitempty"` + // guest_path is the path inside the guest where this layer is mounted. + GuestPath string `protobuf:"bytes,2,opt,name=guest_path,json=guestPath,proto3" json:"guest_path,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LayerReservation) Reset() { + *x = LayerReservation{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LayerReservation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LayerReservation) ProtoMessage() {} + +func (x *LayerReservation) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_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 LayerReservation.ProtoReflect.Descriptor instead. +func (*LayerReservation) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_rawDescGZIP(), []int{2} +} + +func (x *LayerReservation) GetReservationID() string { + if x != nil { + return x.ReservationID + } + return "" +} + +func (x *LayerReservation) GetGuestPath() string { + if x != nil { + return x.GuestPath + } + return "" +} + +var File_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto protoreflect.FileDescriptor + +const file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_rawDesc = "" + + "\n" + + "Rgithub.com/Microsoft/hcsshim/internal/controller/linuxcontainer/save/payload.proto\x12)hcsshim.controller.linuxcontainer.save.v1\x1a\x19google/protobuf/any.proto\x1a\x1egoogle/protobuf/duration.proto\"\x9a\x05\n" + + "\aPayload\x12%\n" + + "\x0eschema_version\x18\x01 \x01(\rR\rschemaVersion\x12!\n" + + "\fcontainer_id\x18\x02 \x01(\tR\vcontainerId\x12(\n" + + "\x10gcs_container_id\x18\x03 \x01(\tR\x0egcsContainerId\x12F\n" + + "\x05state\x18\x04 \x01(\x0e20.hcsshim.controller.linuxcontainer.save.v1.StageR\x05state\x12C\n" + + "\x10io_retry_timeout\x18\x05 \x01(\v2\x19.google.protobuf.DurationR\x0eioRetryTimeout\x12I\n" + + "\x06layers\x18\x06 \x01(\v21.hcsshim.controller.linuxcontainer.save.v1.LayersR\x06layers\x120\n" + + "\x14scsi_reservation_ids\x18\a \x03(\tR\x12scsiReservationIds\x122\n" + + "\x15plan9_reservation_ids\x18\b \x03(\tR\x13plan9ReservationIds\x12(\n" + + "\x10vpci_vmbus_guids\x18\t \x03(\tR\x0evpciVmbusGuids\x12_\n" + + "\tprocesses\x18\n" + + " \x03(\v2A.hcsshim.controller.linuxcontainer.save.v1.Payload.ProcessesEntryR\tprocesses\x1aR\n" + + "\x0eProcessesEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12*\n" + + "\x05value\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x05value:\x028\x01\"\x83\x02\n" + + "\x06Layers\x12X\n" + + "\tro_layers\x18\x01 \x03(\v2;.hcsshim.controller.linuxcontainer.save.v1.LayerReservationR\broLayers\x12U\n" + + "\ascratch\x18\x02 \x01(\v2;.hcsshim.controller.linuxcontainer.save.v1.LayerReservationR\ascratch\x12'\n" + + "\x0flayers_combined\x18\x03 \x01(\bR\x0elayersCombined\x12\x1f\n" + + "\vrootfs_path\x18\x04 \x01(\tR\n" + + "rootfsPath\"X\n" + + "\x10LayerReservation\x12%\n" + + "\x0ereservation_id\x18\x01 \x01(\tR\rreservationId\x12\x1d\n" + + "\n" + + "guest_path\x18\x02 \x01(\tR\tguestPath*\x7f\n" + + "\x05Stage\x12\x15\n" + + "\x11STAGE_NOT_CREATED\x10\x00\x12\x11\n" + + "\rSTAGE_CREATED\x10\x01\x12\x11\n" + + "\rSTAGE_RUNNING\x10\x02\x12\x11\n" + + "\rSTAGE_STOPPED\x10\x03\x12\x11\n" + + "\rSTAGE_INVALID\x10\x04\x12\x13\n" + + "\x0fSTAGE_MIGRATING\x10\x05BKZIgithub.com/Microsoft/hcsshim/internal/controller/linuxcontainer/save;saveb\x06proto3" + +var ( + file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_rawDescOnce sync.Once + file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_rawDescData []byte +) + +func file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_rawDescGZIP() []byte { + file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_rawDescOnce.Do(func() { + file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_rawDesc))) + }) + return file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_rawDescData +} + +var file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_goTypes = []any{ + (Stage)(0), // 0: hcsshim.controller.linuxcontainer.save.v1.Stage + (*Payload)(nil), // 1: hcsshim.controller.linuxcontainer.save.v1.Payload + (*Layers)(nil), // 2: hcsshim.controller.linuxcontainer.save.v1.Layers + (*LayerReservation)(nil), // 3: hcsshim.controller.linuxcontainer.save.v1.LayerReservation + nil, // 4: hcsshim.controller.linuxcontainer.save.v1.Payload.ProcessesEntry + (*durationpb.Duration)(nil), // 5: google.protobuf.Duration + (*anypb.Any)(nil), // 6: google.protobuf.Any +} +var file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_depIdxs = []int32{ + 0, // 0: hcsshim.controller.linuxcontainer.save.v1.Payload.state:type_name -> hcsshim.controller.linuxcontainer.save.v1.Stage + 5, // 1: hcsshim.controller.linuxcontainer.save.v1.Payload.io_retry_timeout:type_name -> google.protobuf.Duration + 2, // 2: hcsshim.controller.linuxcontainer.save.v1.Payload.layers:type_name -> hcsshim.controller.linuxcontainer.save.v1.Layers + 4, // 3: hcsshim.controller.linuxcontainer.save.v1.Payload.processes:type_name -> hcsshim.controller.linuxcontainer.save.v1.Payload.ProcessesEntry + 3, // 4: hcsshim.controller.linuxcontainer.save.v1.Layers.ro_layers:type_name -> hcsshim.controller.linuxcontainer.save.v1.LayerReservation + 3, // 5: hcsshim.controller.linuxcontainer.save.v1.Layers.scratch:type_name -> hcsshim.controller.linuxcontainer.save.v1.LayerReservation + 6, // 6: hcsshim.controller.linuxcontainer.save.v1.Payload.ProcessesEntry.value:type_name -> google.protobuf.Any + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { + file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_init() +} +func file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_init() { + if File_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_rawDesc)), + NumEnums: 1, + NumMessages: 4, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_goTypes, + DependencyIndexes: file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_depIdxs, + EnumInfos: file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_enumTypes, + MessageInfos: file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_msgTypes, + }.Build() + File_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto = out.File + file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_goTypes = nil + file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_depIdxs = nil +} diff --git a/internal/controller/linuxcontainer/save/payload.proto b/internal/controller/linuxcontainer/save/payload.proto new file mode 100644 index 0000000000..21f8953986 --- /dev/null +++ b/internal/controller/linuxcontainer/save/payload.proto @@ -0,0 +1,99 @@ +syntax = "proto3"; + +package hcsshim.controller.linuxcontainer.save.v1; + +option go_package = "github.com/Microsoft/hcsshim/internal/controller/linuxcontainer/save;save"; + +import "google/protobuf/any.proto"; +import "google/protobuf/duration.proto"; + +// ============================================================================= +// Lifecycle stage enum +// +// Stage mirrors the Go State type used by the linuxcontainer controller. The +// zero value matches the Go iota-zero value (NotCreated). +// ============================================================================= + +enum Stage { + STAGE_NOT_CREATED = 0; + STAGE_CREATED = 1; + STAGE_RUNNING = 2; + STAGE_STOPPED = 3; + STAGE_INVALID = 4; + STAGE_MIGRATING = 5; +} + +// Payload is the migration payload owned by a single linuxcontainer +// controller. It is the top-level message wrapped in an [anypb.Any] when +// handed off between source and destination shims. Embedded process states +// are carried as opaque [anypb.Any] payloads so the process controller fully +// owns its own schema and versioning. +message Payload { + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + uint32 schema_version = 1; + + // container_id is the host/shim container identifier. + string container_id = 2; + + // gcs_container_id is the container identifier as known by the GCS inside + // the guest, which may differ from container_id. + string gcs_container_id = 3; + + // state is the container lifecycle stage. + Stage state = 4; + + // io_retry_timeout is how long IO relay reconnects are retried for this + // container. + google.protobuf.Duration io_retry_timeout = 5; + + // layers is the container's rootfs composition. + Layers layers = 6; + + // scsi_reservation_ids are the SCSI reservation GUIDs the container owns. + // Each value is a key into the SCSI sub-controller's reservations map. + repeated string scsi_reservation_ids = 7; + + // plan9_reservation_ids are the Plan9 reservation GUIDs the container + // owns. Each value is a key into the Plan9 sub-controller's reservations + // map. + repeated string plan9_reservation_ids = 8; + + // vpci_vmbus_guids are the VMBus GUIDs of VPCI devices the container + // uses. Each value is a key into the VPCI sub-controller's devices map. + repeated string vpci_vmbus_guids = 9; + + // processes is keyed by exec ID and contains the init process (with an + // empty exec ID) plus any additional exec'd processes. Each value is an + // opaque process-controller [Payload] envelope. + map processes = 10; +} + +// Layers describes the container's rootfs composition. +message Layers { + // ro_layers are the read-only layer reservations in stack order. + repeated LayerReservation ro_layers = 1; + + // scratch is the writable scratch layer reservation. + LayerReservation scratch = 2; + + // layers_combined is true when the layers were merged into a single + // rootfs (e.g. overlay) rather than mounted individually. + bool layers_combined = 3; + + // rootfs_path is the final guest path of the container rootfs: the + // merged mount when layers_combined is true, otherwise the topmost + // layer mount. + string rootfs_path = 4; +} + +// LayerReservation references a single SCSI-backed layer mounted into the +// guest. +message LayerReservation { + // reservation_id is a SCSI reservation GUID; layers are SCSI-backed VHDs + // and this is a key into the SCSI sub-controller's reservations map. + string reservation_id = 1; + + // guest_path is the path inside the guest where this layer is mounted. + string guest_path = 2; +} diff --git a/internal/controller/migration/save/constants.go b/internal/controller/migration/save/constants.go new file mode 100644 index 0000000000..d920b26cea --- /dev/null +++ b/internal/controller/migration/save/constants.go @@ -0,0 +1,17 @@ +//go:build windows && lcow + +// Package save defines the top-level sandbox-level wire format used to hand +// off an LCOW sandbox between shims during live migration. The [Payload] +// envelope only carries opaque [anypb.Any] payloads owned by the VM +// controller and each pod controller; this package owns the envelope itself, +// not the inner controller schemas. +package save + +// SchemaVersion is the on-the-wire compatibility version stamped into +// [Payload.SchemaVersion]. Bump on any breaking change to payload.proto. +const SchemaVersion uint32 = 1 + +// TypeURL identifies a sandbox-level [Payload] when wrapped in an [anypb.Any]. +// It is opaque to clients and only meaningful between two shims that agree +// on [SchemaVersion]. +const TypeURL = "type.microsoft.com/hcsshim.controller.migration.save.v1.Payload" diff --git a/internal/controller/migration/save/payload.pb.go b/internal/controller/migration/save/payload.pb.go new file mode 100644 index 0000000000..8bc2316136 --- /dev/null +++ b/internal/controller/migration/save/payload.pb.go @@ -0,0 +1,152 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v5.26.0 +// source: github.com/Microsoft/hcsshim/internal/controller/migration/save/payload.proto + +package save + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + 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) +) + +// Payload is the top-level migration envelope exchanged between +// source and destination shims. The VM and pod payloads are carried as +// opaque [Any]s so each owning controller is independently versioned. +type Payload struct { + state protoimpl.MessageState `protogen:"open.v1"` + // schema_version is bumped whenever this envelope's semantics change. + SchemaVersion uint32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"` + // vm is the VM controller's [Payload] envelope. + Vm *anypb.Any `protobuf:"bytes,2,opt,name=vm,proto3" json:"vm,omitempty"` + // pods holds one [Payload] envelope per pod controller in the sandbox. + Pods []*anypb.Any `protobuf:"bytes,3,rep,name=pods,proto3" json:"pods,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Payload) Reset() { + *x = Payload{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Payload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Payload) ProtoMessage() {} + +func (x *Payload) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_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 Payload.ProtoReflect.Descriptor instead. +func (*Payload) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_rawDescGZIP(), []int{0} +} + +func (x *Payload) GetSchemaVersion() uint32 { + if x != nil { + return x.SchemaVersion + } + return 0 +} + +func (x *Payload) GetVm() *anypb.Any { + if x != nil { + return x.Vm + } + return nil +} + +func (x *Payload) GetPods() []*anypb.Any { + if x != nil { + return x.Pods + } + return nil +} + +var File_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto protoreflect.FileDescriptor + +const file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_rawDesc = "" + + "\n" + + "Mgithub.com/Microsoft/hcsshim/internal/controller/migration/save/payload.proto\x12$hcsshim.controller.migration.save.v1\x1a\x19google/protobuf/any.proto\"\x80\x01\n" + + "\aPayload\x12%\n" + + "\x0eschema_version\x18\x01 \x01(\rR\rschemaVersion\x12$\n" + + "\x02vm\x18\x02 \x01(\v2\x14.google.protobuf.AnyR\x02vm\x12(\n" + + "\x04pods\x18\x03 \x03(\v2\x14.google.protobuf.AnyR\x04podsBFZDgithub.com/Microsoft/hcsshim/internal/controller/migration/save;saveb\x06proto3" + +var ( + file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_rawDescOnce sync.Once + file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_rawDescData []byte +) + +func file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_rawDescGZIP() []byte { + file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_rawDescOnce.Do(func() { + file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_rawDesc))) + }) + return file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_rawDescData +} + +var file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_goTypes = []any{ + (*Payload)(nil), // 0: hcsshim.controller.migration.save.v1.Payload + (*anypb.Any)(nil), // 1: google.protobuf.Any +} +var file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_depIdxs = []int32{ + 1, // 0: hcsshim.controller.migration.save.v1.Payload.vm:type_name -> google.protobuf.Any + 1, // 1: hcsshim.controller.migration.save.v1.Payload.pods:type_name -> google.protobuf.Any + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { + file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_init() +} +func file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_init() { + if File_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_goTypes, + DependencyIndexes: file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_depIdxs, + MessageInfos: file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_msgTypes, + }.Build() + File_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto = out.File + file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_goTypes = nil + file_github_com_Microsoft_hcsshim_internal_controller_migration_save_payload_proto_depIdxs = nil +} diff --git a/internal/controller/migration/save/payload.proto b/internal/controller/migration/save/payload.proto new file mode 100644 index 0000000000..45ad736732 --- /dev/null +++ b/internal/controller/migration/save/payload.proto @@ -0,0 +1,21 @@ +syntax = "proto3"; + +package hcsshim.controller.migration.save.v1; + +option go_package = "github.com/Microsoft/hcsshim/internal/controller/migration/save;save"; + +import "google/protobuf/any.proto"; + +// Payload is the top-level migration envelope exchanged between +// source and destination shims. The VM and pod payloads are carried as +// opaque [Any]s so each owning controller is independently versioned. +message Payload { + // schema_version is bumped whenever this envelope's semantics change. + uint32 schema_version = 1; + + // vm is the VM controller's [Payload] envelope. + google.protobuf.Any vm = 2; + + // pods holds one [Payload] envelope per pod controller in the sandbox. + repeated google.protobuf.Any pods = 3; +} diff --git a/internal/controller/network/save/constants.go b/internal/controller/network/save/constants.go new file mode 100644 index 0000000000..0b18b8a79b --- /dev/null +++ b/internal/controller/network/save/constants.go @@ -0,0 +1,15 @@ +//go:build windows && (lcow || wcow) + +// Package save defines the wire format owned by the network controller for +// live migration. The [Payload] message is self-contained and carries the +// network controller's serialized state across shims. +package save + +// SchemaVersion is the on-the-wire compatibility version stamped into +// [Payload.SchemaVersion]. Bump on any breaking change to payload.proto. +const SchemaVersion uint32 = 1 + +// TypeURL identifies a network [Payload] when wrapped in an [anypb.Any]. It is +// opaque to clients and only meaningful between two shims that agree on +// [SchemaVersion]. +const TypeURL = "type.microsoft.com/hcsshim.controller.network.save.v1.Payload" diff --git a/internal/controller/network/save/payload.pb.go b/internal/controller/network/save/payload.pb.go new file mode 100644 index 0000000000..46e5e45f36 --- /dev/null +++ b/internal/controller/network/save/payload.pb.go @@ -0,0 +1,319 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v5.26.0 +// source: github.com/Microsoft/hcsshim/internal/controller/network/save/payload.proto + +package save + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + 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 Stage int32 + +const ( + Stage_STAGE_NOT_CONFIGURED Stage = 0 + Stage_STAGE_CONFIGURED Stage = 1 + Stage_STAGE_INVALID Stage = 2 + Stage_STAGE_TORN_DOWN Stage = 3 +) + +// Enum value maps for Stage. +var ( + Stage_name = map[int32]string{ + 0: "STAGE_NOT_CONFIGURED", + 1: "STAGE_CONFIGURED", + 2: "STAGE_INVALID", + 3: "STAGE_TORN_DOWN", + } + Stage_value = map[string]int32{ + "STAGE_NOT_CONFIGURED": 0, + "STAGE_CONFIGURED": 1, + "STAGE_INVALID": 2, + "STAGE_TORN_DOWN": 3, + } +) + +func (x Stage) Enum() *Stage { + p := new(Stage) + *p = x + return p +} + +func (x Stage) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Stage) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_enumTypes[0].Descriptor() +} + +func (Stage) Type() protoreflect.EnumType { + return &file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_enumTypes[0] +} + +func (x Stage) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Stage.Descriptor instead. +func (Stage) EnumDescriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_rawDescGZIP(), []int{0} +} + +// Payload is the migration payload owned by the network controller. It is the +// top-level message wrapped in an [anypb.Any] when handed off between source +// and destination shims. +type Payload struct { + state protoimpl.MessageState `protogen:"open.v1"` + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + SchemaVersion uint32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"` + // namespace_id is the HCN compartment / namespace GUID. + NamespaceID string `protobuf:"bytes,2,opt,name=namespace_id,json=namespaceId,proto3" json:"namespace_id,omitempty"` + // policy_based_routing records whether PBR was enabled when the network + // was set up. + PolicyBasedRouting bool `protobuf:"varint,3,opt,name=policy_based_routing,json=policyBasedRouting,proto3" json:"policy_based_routing,omitempty"` + // is_namespace_supported_by_guest records whether the guest GCS supports + // namespace-based endpoint attach. + IsNamespaceSupportedByGuest bool `protobuf:"varint,4,opt,name=is_namespace_supported_by_guest,json=isNamespaceSupportedByGuest,proto3" json:"is_namespace_supported_by_guest,omitempty"` + // state is the network lifecycle stage. + State Stage `protobuf:"varint,5,opt,name=state,proto3,enum=hcsshim.controller.network.save.v1.Stage" json:"state,omitempty"` + // vm_endpoints is keyed by per-VM vNIC GUID and contains the HNS endpoint + // bound to each NIC slot of the VM. + VmEndpoints map[string]*EndpointBinding `protobuf:"bytes,6,rep,name=vm_endpoints,json=vmEndpoints,proto3" json:"vm_endpoints,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Payload) Reset() { + *x = Payload{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Payload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Payload) ProtoMessage() {} + +func (x *Payload) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_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 Payload.ProtoReflect.Descriptor instead. +func (*Payload) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_rawDescGZIP(), []int{0} +} + +func (x *Payload) GetSchemaVersion() uint32 { + if x != nil { + return x.SchemaVersion + } + return 0 +} + +func (x *Payload) GetNamespaceID() string { + if x != nil { + return x.NamespaceID + } + return "" +} + +func (x *Payload) GetPolicyBasedRouting() bool { + if x != nil { + return x.PolicyBasedRouting + } + return false +} + +func (x *Payload) GetIsNamespaceSupportedByGuest() bool { + if x != nil { + return x.IsNamespaceSupportedByGuest + } + return false +} + +func (x *Payload) GetState() Stage { + if x != nil { + return x.State + } + return Stage_STAGE_NOT_CONFIGURED +} + +func (x *Payload) GetVmEndpoints() map[string]*EndpointBinding { + if x != nil { + return x.VmEndpoints + } + return nil +} + +// EndpointBinding describes a single HNS/HCN endpoint bound to a VM vNIC. +type EndpointBinding struct { + state protoimpl.MessageState `protogen:"open.v1"` + // endpoint_id is the HNS/HCN HostComputeEndpoint identifier bound to the + // NIC. + EndpointID string `protobuf:"bytes,1,opt,name=endpoint_id,json=endpointId,proto3" json:"endpoint_id,omitempty"` + // mac_address is the endpoint's MAC address. + MacAddress string `protobuf:"bytes,2,opt,name=mac_address,json=macAddress,proto3" json:"mac_address,omitempty"` + // endpoint_name is the endpoint's HNS/HCN name. + EndpointName string `protobuf:"bytes,3,opt,name=endpoint_name,json=endpointName,proto3" json:"endpoint_name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *EndpointBinding) Reset() { + *x = EndpointBinding{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *EndpointBinding) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EndpointBinding) ProtoMessage() {} + +func (x *EndpointBinding) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_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 EndpointBinding.ProtoReflect.Descriptor instead. +func (*EndpointBinding) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_rawDescGZIP(), []int{1} +} + +func (x *EndpointBinding) GetEndpointID() string { + if x != nil { + return x.EndpointID + } + return "" +} + +func (x *EndpointBinding) GetMacAddress() string { + if x != nil { + return x.MacAddress + } + return "" +} + +func (x *EndpointBinding) GetEndpointName() string { + if x != nil { + return x.EndpointName + } + return "" +} + +var File_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto protoreflect.FileDescriptor + +const file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_rawDesc = "" + + "\n" + + "Kgithub.com/Microsoft/hcsshim/internal/controller/network/save/payload.proto\x12\"hcsshim.controller.network.save.v1\"\xe2\x03\n" + + "\aPayload\x12%\n" + + "\x0eschema_version\x18\x01 \x01(\rR\rschemaVersion\x12!\n" + + "\fnamespace_id\x18\x02 \x01(\tR\vnamespaceId\x120\n" + + "\x14policy_based_routing\x18\x03 \x01(\bR\x12policyBasedRouting\x12D\n" + + "\x1fis_namespace_supported_by_guest\x18\x04 \x01(\bR\x1bisNamespaceSupportedByGuest\x12?\n" + + "\x05state\x18\x05 \x01(\x0e2).hcsshim.controller.network.save.v1.StageR\x05state\x12_\n" + + "\fvm_endpoints\x18\x06 \x03(\v2<.hcsshim.controller.network.save.v1.Payload.VmEndpointsEntryR\vvmEndpoints\x1as\n" + + "\x10VmEndpointsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12I\n" + + "\x05value\x18\x02 \x01(\v23.hcsshim.controller.network.save.v1.EndpointBindingR\x05value:\x028\x01\"x\n" + + "\x0fEndpointBinding\x12\x1f\n" + + "\vendpoint_id\x18\x01 \x01(\tR\n" + + "endpointId\x12\x1f\n" + + "\vmac_address\x18\x02 \x01(\tR\n" + + "macAddress\x12#\n" + + "\rendpoint_name\x18\x03 \x01(\tR\fendpointName*_\n" + + "\x05Stage\x12\x18\n" + + "\x14STAGE_NOT_CONFIGURED\x10\x00\x12\x14\n" + + "\x10STAGE_CONFIGURED\x10\x01\x12\x11\n" + + "\rSTAGE_INVALID\x10\x02\x12\x13\n" + + "\x0fSTAGE_TORN_DOWN\x10\x03BDZBgithub.com/Microsoft/hcsshim/internal/controller/network/save;saveb\x06proto3" + +var ( + file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_rawDescOnce sync.Once + file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_rawDescData []byte +) + +func file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_rawDescGZIP() []byte { + file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_rawDescOnce.Do(func() { + file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_rawDesc))) + }) + return file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_rawDescData +} + +var file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_goTypes = []any{ + (Stage)(0), // 0: hcsshim.controller.network.save.v1.Stage + (*Payload)(nil), // 1: hcsshim.controller.network.save.v1.Payload + (*EndpointBinding)(nil), // 2: hcsshim.controller.network.save.v1.EndpointBinding + nil, // 3: hcsshim.controller.network.save.v1.Payload.VmEndpointsEntry +} +var file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_depIdxs = []int32{ + 0, // 0: hcsshim.controller.network.save.v1.Payload.state:type_name -> hcsshim.controller.network.save.v1.Stage + 3, // 1: hcsshim.controller.network.save.v1.Payload.vm_endpoints:type_name -> hcsshim.controller.network.save.v1.Payload.VmEndpointsEntry + 2, // 2: hcsshim.controller.network.save.v1.Payload.VmEndpointsEntry.value:type_name -> hcsshim.controller.network.save.v1.EndpointBinding + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_init() } +func file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_init() { + if File_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_rawDesc)), + NumEnums: 1, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_goTypes, + DependencyIndexes: file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_depIdxs, + EnumInfos: file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_enumTypes, + MessageInfos: file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_msgTypes, + }.Build() + File_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto = out.File + file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_goTypes = nil + file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_depIdxs = nil +} diff --git a/internal/controller/network/save/payload.proto b/internal/controller/network/save/payload.proto new file mode 100644 index 0000000000..04d8f08ddd --- /dev/null +++ b/internal/controller/network/save/payload.proto @@ -0,0 +1,59 @@ +syntax = "proto3"; + +package hcsshim.controller.network.save.v1; + +option go_package = "github.com/Microsoft/hcsshim/internal/controller/network/save;save"; + +// ============================================================================= +// Lifecycle stage enum +// +// Stage mirrors the Go State type used by the network controller. The zero +// value matches the Go iota-zero value (NotConfigured). +// ============================================================================= + +enum Stage { + STAGE_NOT_CONFIGURED = 0; + STAGE_CONFIGURED = 1; + STAGE_INVALID = 2; + STAGE_TORN_DOWN = 3; +} + +// Payload is the migration payload owned by the network controller. It is the +// top-level message wrapped in an [anypb.Any] when handed off between source +// and destination shims. +message Payload { + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + uint32 schema_version = 1; + + // namespace_id is the HCN compartment / namespace GUID. + string namespace_id = 2; + + // policy_based_routing records whether PBR was enabled when the network + // was set up. + bool policy_based_routing = 3; + + // is_namespace_supported_by_guest records whether the guest GCS supports + // namespace-based endpoint attach. + bool is_namespace_supported_by_guest = 4; + + // state is the network lifecycle stage. + Stage state = 5; + + // vm_endpoints is keyed by per-VM vNIC GUID and contains the HNS endpoint + // bound to each NIC slot of the VM. + map vm_endpoints = 6; +} + +// EndpointBinding describes a single HNS/HCN endpoint bound to a VM vNIC. +message EndpointBinding { + // endpoint_id is the HNS/HCN HostComputeEndpoint identifier bound to the + // NIC. + string endpoint_id = 1; + + // mac_address is the endpoint's MAC address. + string mac_address = 2; + + // endpoint_name is the endpoint's HNS/HCN name. + string endpoint_name = 3; +} diff --git a/internal/controller/pod/save/constants.go b/internal/controller/pod/save/constants.go new file mode 100644 index 0000000000..3aebf592e5 --- /dev/null +++ b/internal/controller/pod/save/constants.go @@ -0,0 +1,17 @@ +//go:build windows && lcow + +// Package save defines the wire format owned by the pod controller for +// live migration. The [Payload] envelope carries pod-level fields plus the +// child controller states (network and containers) as opaque [anypb.Any] +// payloads; this package owns the envelope itself, not the inner +// controller schemas. +package save + +// SchemaVersion is the on-the-wire compatibility version stamped into +// [Payload.SchemaVersion]. Bump on any breaking change to payload.proto. +const SchemaVersion uint32 = 1 + +// TypeURL identifies a pod [Payload] when wrapped in an [anypb.Any]. It is +// opaque to clients and only meaningful between two shims that agree on +// [SchemaVersion]. +const TypeURL = "type.microsoft.com/hcsshim.controller.pod.save.v1.Payload" diff --git a/internal/controller/pod/save/payload.pb.go b/internal/controller/pod/save/payload.pb.go new file mode 100644 index 0000000000..16cd47b318 --- /dev/null +++ b/internal/controller/pod/save/payload.pb.go @@ -0,0 +1,178 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v5.26.0 +// source: github.com/Microsoft/hcsshim/internal/controller/pod/save/payload.proto + +package save + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + 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) +) + +// Payload is the migration payload owned by a single pod controller. It is +// the top-level message wrapped in an [anypb.Any] when handed off between +// source and destination shims. The embedded network controller and +// container controllers are carried as opaque [anypb.Any] payloads so each +// child controller fully owns its own schema. +type Payload struct { + state protoimpl.MessageState `protogen:"open.v1"` + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + SchemaVersion uint32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"` + // pod_id is the pod identifier. + PodID string `protobuf:"bytes,2,opt,name=pod_id,json=podId,proto3" json:"pod_id,omitempty"` + // gcs_pod_id is the pod identifier as known by the GCS inside the + // guest, which may differ from pod_id. + GcsPodID string `protobuf:"bytes,3,opt,name=gcs_pod_id,json=gcsPodId,proto3" json:"gcs_pod_id,omitempty"` + // network is the per-pod network controller's [anypb.Any] envelope. + Network *anypb.Any `protobuf:"bytes,4,opt,name=network,proto3" json:"network,omitempty"` + // containers is the set of container [Payload] envelopes for this pod, + // one [anypb.Any] per container controller. + Containers []*anypb.Any `protobuf:"bytes,5,rep,name=containers,proto3" json:"containers,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Payload) Reset() { + *x = Payload{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Payload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Payload) ProtoMessage() {} + +func (x *Payload) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_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 Payload.ProtoReflect.Descriptor instead. +func (*Payload) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_rawDescGZIP(), []int{0} +} + +func (x *Payload) GetSchemaVersion() uint32 { + if x != nil { + return x.SchemaVersion + } + return 0 +} + +func (x *Payload) GetPodID() string { + if x != nil { + return x.PodID + } + return "" +} + +func (x *Payload) GetGcsPodID() string { + if x != nil { + return x.GcsPodID + } + return "" +} + +func (x *Payload) GetNetwork() *anypb.Any { + if x != nil { + return x.Network + } + return nil +} + +func (x *Payload) GetContainers() []*anypb.Any { + if x != nil { + return x.Containers + } + return nil +} + +var File_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto protoreflect.FileDescriptor + +const file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_rawDesc = "" + + "\n" + + "Ggithub.com/Microsoft/hcsshim/internal/controller/pod/save/payload.proto\x12\x1ehcsshim.controller.pod.save.v1\x1a\x19google/protobuf/any.proto\"\xcb\x01\n" + + "\aPayload\x12%\n" + + "\x0eschema_version\x18\x01 \x01(\rR\rschemaVersion\x12\x15\n" + + "\x06pod_id\x18\x02 \x01(\tR\x05podId\x12\x1c\n" + + "\n" + + "gcs_pod_id\x18\x03 \x01(\tR\bgcsPodId\x12.\n" + + "\anetwork\x18\x04 \x01(\v2\x14.google.protobuf.AnyR\anetwork\x124\n" + + "\n" + + "containers\x18\x05 \x03(\v2\x14.google.protobuf.AnyR\n" + + "containersB@Z>github.com/Microsoft/hcsshim/internal/controller/pod/save;saveb\x06proto3" + +var ( + file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_rawDescOnce sync.Once + file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_rawDescData []byte +) + +func file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_rawDescGZIP() []byte { + file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_rawDescOnce.Do(func() { + file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_rawDesc))) + }) + return file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_rawDescData +} + +var file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_goTypes = []any{ + (*Payload)(nil), // 0: hcsshim.controller.pod.save.v1.Payload + (*anypb.Any)(nil), // 1: google.protobuf.Any +} +var file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_depIdxs = []int32{ + 1, // 0: hcsshim.controller.pod.save.v1.Payload.network:type_name -> google.protobuf.Any + 1, // 1: hcsshim.controller.pod.save.v1.Payload.containers:type_name -> google.protobuf.Any + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_init() } +func file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_init() { + if File_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_rawDesc)), + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_goTypes, + DependencyIndexes: file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_depIdxs, + MessageInfos: file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_msgTypes, + }.Build() + File_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto = out.File + file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_goTypes = nil + file_github_com_Microsoft_hcsshim_internal_controller_pod_save_payload_proto_depIdxs = nil +} diff --git a/internal/controller/pod/save/payload.proto b/internal/controller/pod/save/payload.proto new file mode 100644 index 0000000000..fda6e9e22c --- /dev/null +++ b/internal/controller/pod/save/payload.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +package hcsshim.controller.pod.save.v1; + +option go_package = "github.com/Microsoft/hcsshim/internal/controller/pod/save;save"; + +import "google/protobuf/any.proto"; + +// Payload is the migration payload owned by a single pod controller. It is +// the top-level message wrapped in an [anypb.Any] when handed off between +// source and destination shims. The embedded network controller and +// container controllers are carried as opaque [anypb.Any] payloads so each +// child controller fully owns its own schema. +message Payload { + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + uint32 schema_version = 1; + + // pod_id is the pod identifier. + string pod_id = 2; + + // gcs_pod_id is the pod identifier as known by the GCS inside the + // guest, which may differ from pod_id. + string gcs_pod_id = 3; + + // network is the per-pod network controller's [anypb.Any] envelope. + google.protobuf.Any network = 4; + + // containers is the set of container [Payload] envelopes for this pod, + // one [anypb.Any] per container controller. + repeated google.protobuf.Any containers = 5; +} diff --git a/internal/controller/process/save/constants.go b/internal/controller/process/save/constants.go new file mode 100644 index 0000000000..322792d314 --- /dev/null +++ b/internal/controller/process/save/constants.go @@ -0,0 +1,15 @@ +//go:build windows && (lcow || wcow) + +// Package save defines the wire format owned by the process controller for +// live migration. The [Payload] message is self-contained and carries the +// process controller's serialized state across shims. +package save + +// SchemaVersion is the on-the-wire compatibility version stamped into +// [Payload.SchemaVersion]. Bump on any breaking change to payload.proto. +const SchemaVersion uint32 = 1 + +// TypeURL identifies a process [Payload] when wrapped in an [anypb.Any]. It +// is opaque to clients and only meaningful between two shims that agree on +// [SchemaVersion]. +const TypeURL = "type.microsoft.com/hcsshim.controller.process.save.v1.Payload" diff --git a/internal/controller/process/save/payload.pb.go b/internal/controller/process/save/payload.pb.go new file mode 100644 index 0000000000..d4430ceb0a --- /dev/null +++ b/internal/controller/process/save/payload.pb.go @@ -0,0 +1,311 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v5.26.0 +// source: github.com/Microsoft/hcsshim/internal/controller/process/save/payload.proto + +package save + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + durationpb "google.golang.org/protobuf/types/known/durationpb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + 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 Stage int32 + +const ( + Stage_STAGE_NOT_CREATED Stage = 0 + Stage_STAGE_CREATED Stage = 1 + Stage_STAGE_RUNNING Stage = 2 + Stage_STAGE_TERMINATED Stage = 3 +) + +// Enum value maps for Stage. +var ( + Stage_name = map[int32]string{ + 0: "STAGE_NOT_CREATED", + 1: "STAGE_CREATED", + 2: "STAGE_RUNNING", + 3: "STAGE_TERMINATED", + } + Stage_value = map[string]int32{ + "STAGE_NOT_CREATED": 0, + "STAGE_CREATED": 1, + "STAGE_RUNNING": 2, + "STAGE_TERMINATED": 3, + } +) + +func (x Stage) Enum() *Stage { + p := new(Stage) + *p = x + return p +} + +func (x Stage) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Stage) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_enumTypes[0].Descriptor() +} + +func (Stage) Type() protoreflect.EnumType { + return &file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_enumTypes[0] +} + +func (x Stage) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Stage.Descriptor instead. +func (Stage) EnumDescriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_rawDescGZIP(), []int{0} +} + +// Payload is the migration payload owned by a single process controller. It is +// the top-level message wrapped in an [anypb.Any] when handed off between +// source and destination shims. +type Payload struct { + state protoimpl.MessageState `protogen:"open.v1"` + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + SchemaVersion uint32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"` + // exec_id is the process's exec identifier; empty for the container's + // init process. + ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"` + // state is the process lifecycle stage. + State Stage `protobuf:"varint,3,opt,name=state,proto3,enum=hcsshim.controller.process.save.v1.Stage" json:"state,omitempty"` + // pid is the process ID inside the guest. + Pid int32 `protobuf:"varint,4,opt,name=pid,proto3" json:"pid,omitempty"` + // bundle is the OCI bundle path on the host. + Bundle string `protobuf:"bytes,5,opt,name=bundle,proto3" json:"bundle,omitempty"` + // oci_process_spec_json is the JSON-encoded OCI process specification. + OciProcessSpecJson []byte `protobuf:"bytes,6,opt,name=oci_process_spec_json,json=ociProcessSpecJson,proto3" json:"oci_process_spec_json,omitempty"` + // exited_at is the time the process exited; zero while still running. + ExitedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=exited_at,json=exitedAt,proto3" json:"exited_at,omitempty"` + // exit_code is the process exit code; only meaningful once the process + // has terminated. + ExitCode uint32 `protobuf:"varint,8,opt,name=exit_code,json=exitCode,proto3" json:"exit_code,omitempty"` + // io_retry_timeout is how long IO relay reconnects are retried for this + // process. + IoRetryTimeout *durationpb.Duration `protobuf:"bytes,9,opt,name=io_retry_timeout,json=ioRetryTimeout,proto3" json:"io_retry_timeout,omitempty"` + // stdin_port, stdout_port and stderr_port are the vsock ports used by + // the GCS<->shim IO relay for this process. The destination reattaches + // IO on the same ports so streams do not need to be renegotiated. + StdinPort uint32 `protobuf:"varint,10,opt,name=stdin_port,json=stdinPort,proto3" json:"stdin_port,omitempty"` + StdoutPort uint32 `protobuf:"varint,11,opt,name=stdout_port,json=stdoutPort,proto3" json:"stdout_port,omitempty"` + StderrPort uint32 `protobuf:"varint,12,opt,name=stderr_port,json=stderrPort,proto3" json:"stderr_port,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Payload) Reset() { + *x = Payload{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Payload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Payload) ProtoMessage() {} + +func (x *Payload) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_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 Payload.ProtoReflect.Descriptor instead. +func (*Payload) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_rawDescGZIP(), []int{0} +} + +func (x *Payload) GetSchemaVersion() uint32 { + if x != nil { + return x.SchemaVersion + } + return 0 +} + +func (x *Payload) GetExecID() string { + if x != nil { + return x.ExecID + } + return "" +} + +func (x *Payload) GetState() Stage { + if x != nil { + return x.State + } + return Stage_STAGE_NOT_CREATED +} + +func (x *Payload) GetPid() int32 { + if x != nil { + return x.Pid + } + return 0 +} + +func (x *Payload) GetBundle() string { + if x != nil { + return x.Bundle + } + return "" +} + +func (x *Payload) GetOciProcessSpecJson() []byte { + if x != nil { + return x.OciProcessSpecJson + } + return nil +} + +func (x *Payload) GetExitedAt() *timestamppb.Timestamp { + if x != nil { + return x.ExitedAt + } + return nil +} + +func (x *Payload) GetExitCode() uint32 { + if x != nil { + return x.ExitCode + } + return 0 +} + +func (x *Payload) GetIoRetryTimeout() *durationpb.Duration { + if x != nil { + return x.IoRetryTimeout + } + return nil +} + +func (x *Payload) GetStdinPort() uint32 { + if x != nil { + return x.StdinPort + } + return 0 +} + +func (x *Payload) GetStdoutPort() uint32 { + if x != nil { + return x.StdoutPort + } + return 0 +} + +func (x *Payload) GetStderrPort() uint32 { + if x != nil { + return x.StderrPort + } + return 0 +} + +var File_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto protoreflect.FileDescriptor + +const file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_rawDesc = "" + + "\n" + + "Kgithub.com/Microsoft/hcsshim/internal/controller/process/save/payload.proto\x12\"hcsshim.controller.process.save.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xe3\x03\n" + + "\aPayload\x12%\n" + + "\x0eschema_version\x18\x01 \x01(\rR\rschemaVersion\x12\x17\n" + + "\aexec_id\x18\x02 \x01(\tR\x06execId\x12?\n" + + "\x05state\x18\x03 \x01(\x0e2).hcsshim.controller.process.save.v1.StageR\x05state\x12\x10\n" + + "\x03pid\x18\x04 \x01(\x05R\x03pid\x12\x16\n" + + "\x06bundle\x18\x05 \x01(\tR\x06bundle\x121\n" + + "\x15oci_process_spec_json\x18\x06 \x01(\fR\x12ociProcessSpecJson\x127\n" + + "\texited_at\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\bexitedAt\x12\x1b\n" + + "\texit_code\x18\b \x01(\rR\bexitCode\x12C\n" + + "\x10io_retry_timeout\x18\t \x01(\v2\x19.google.protobuf.DurationR\x0eioRetryTimeout\x12\x1d\n" + + "\n" + + "stdin_port\x18\n" + + " \x01(\rR\tstdinPort\x12\x1f\n" + + "\vstdout_port\x18\v \x01(\rR\n" + + "stdoutPort\x12\x1f\n" + + "\vstderr_port\x18\f \x01(\rR\n" + + "stderrPort*Z\n" + + "\x05Stage\x12\x15\n" + + "\x11STAGE_NOT_CREATED\x10\x00\x12\x11\n" + + "\rSTAGE_CREATED\x10\x01\x12\x11\n" + + "\rSTAGE_RUNNING\x10\x02\x12\x14\n" + + "\x10STAGE_TERMINATED\x10\x03BDZBgithub.com/Microsoft/hcsshim/internal/controller/process/save;saveb\x06proto3" + +var ( + file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_rawDescOnce sync.Once + file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_rawDescData []byte +) + +func file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_rawDescGZIP() []byte { + file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_rawDescOnce.Do(func() { + file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_rawDesc))) + }) + return file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_rawDescData +} + +var file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_goTypes = []any{ + (Stage)(0), // 0: hcsshim.controller.process.save.v1.Stage + (*Payload)(nil), // 1: hcsshim.controller.process.save.v1.Payload + (*timestamppb.Timestamp)(nil), // 2: google.protobuf.Timestamp + (*durationpb.Duration)(nil), // 3: google.protobuf.Duration +} +var file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_depIdxs = []int32{ + 0, // 0: hcsshim.controller.process.save.v1.Payload.state:type_name -> hcsshim.controller.process.save.v1.Stage + 2, // 1: hcsshim.controller.process.save.v1.Payload.exited_at:type_name -> google.protobuf.Timestamp + 3, // 2: hcsshim.controller.process.save.v1.Payload.io_retry_timeout:type_name -> google.protobuf.Duration + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_init() } +func file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_init() { + if File_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_rawDesc)), + NumEnums: 1, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_goTypes, + DependencyIndexes: file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_depIdxs, + EnumInfos: file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_enumTypes, + MessageInfos: file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_msgTypes, + }.Build() + File_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto = out.File + file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_goTypes = nil + file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_depIdxs = nil +} diff --git a/internal/controller/process/save/payload.proto b/internal/controller/process/save/payload.proto new file mode 100644 index 0000000000..21b20ad5f5 --- /dev/null +++ b/internal/controller/process/save/payload.proto @@ -0,0 +1,65 @@ +syntax = "proto3"; + +package hcsshim.controller.process.save.v1; + +option go_package = "github.com/Microsoft/hcsshim/internal/controller/process/save;save"; + +import "google/protobuf/duration.proto"; +import "google/protobuf/timestamp.proto"; + +// ============================================================================= +// Lifecycle stage enum +// +// Stage mirrors the Go State type used by the process controller. The zero +// value matches the Go iota-zero value (NotCreated). +// ============================================================================= + +enum Stage { + STAGE_NOT_CREATED = 0; + STAGE_CREATED = 1; + STAGE_RUNNING = 2; + STAGE_TERMINATED = 3; +} + +// Payload is the migration payload owned by a single process controller. It is +// the top-level message wrapped in an [anypb.Any] when handed off between +// source and destination shims. +message Payload { + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + uint32 schema_version = 1; + + // exec_id is the process's exec identifier; empty for the container's + // init process. + string exec_id = 2; + + // state is the process lifecycle stage. + Stage state = 3; + + // pid is the process ID inside the guest. + int32 pid = 4; + + // bundle is the OCI bundle path on the host. + string bundle = 5; + + // oci_process_spec_json is the JSON-encoded OCI process specification. + bytes oci_process_spec_json = 6; + + // exited_at is the time the process exited; zero while still running. + google.protobuf.Timestamp exited_at = 7; + + // exit_code is the process exit code; only meaningful once the process + // has terminated. + uint32 exit_code = 8; + + // io_retry_timeout is how long IO relay reconnects are retried for this + // process. + google.protobuf.Duration io_retry_timeout = 9; + + // stdin_port, stdout_port and stderr_port are the vsock ports used by + // the GCS<->shim IO relay for this process. The destination reattaches + // IO on the same ports so streams do not need to be renegotiated. + uint32 stdin_port = 10; + uint32 stdout_port = 11; + uint32 stderr_port = 12; +} diff --git a/internal/controller/vm/save/constants.go b/internal/controller/vm/save/constants.go new file mode 100644 index 0000000000..1f1174dc2b --- /dev/null +++ b/internal/controller/vm/save/constants.go @@ -0,0 +1,17 @@ +//go:build windows && (lcow || wcow) + +// Package save defines the wire format owned by the VM controller for +// live migration. The [Payload] envelope carries the VM's bookkeeping plus +// the sub-device controller states (SCSI, VPCI, Plan9) as opaque +// [anypb.Any] payloads; this package owns the envelope itself, not the +// inner sub-controller schemas. +package save + +// SchemaVersion is the on-the-wire compatibility version stamped into +// [Payload.SchemaVersion]. Bump on any breaking change to payload.proto. +const SchemaVersion uint32 = 1 + +// TypeURL identifies a VM [Payload] when wrapped in an [anypb.Any]. It is +// opaque to clients and only meaningful between two shims that agree on +// [SchemaVersion]. +const TypeURL = "type.microsoft.com/hcsshim.controller.vm.save.v1.Payload" diff --git a/internal/controller/vm/save/payload.pb.go b/internal/controller/vm/save/payload.pb.go new file mode 100644 index 0000000000..d80291cb39 --- /dev/null +++ b/internal/controller/vm/save/payload.pb.go @@ -0,0 +1,468 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.11 +// protoc v5.26.0 +// source: github.com/Microsoft/hcsshim/internal/controller/vm/save/payload.proto + +package save + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + anypb "google.golang.org/protobuf/types/known/anypb" + 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 Stage int32 + +const ( + Stage_STAGE_NOT_CREATED Stage = 0 + Stage_STAGE_CREATED Stage = 1 + Stage_STAGE_RUNNING Stage = 2 + Stage_STAGE_TERMINATED Stage = 3 + Stage_STAGE_INVALID Stage = 4 + Stage_STAGE_MIGRATING Stage = 5 +) + +// Enum value maps for Stage. +var ( + Stage_name = map[int32]string{ + 0: "STAGE_NOT_CREATED", + 1: "STAGE_CREATED", + 2: "STAGE_RUNNING", + 3: "STAGE_TERMINATED", + 4: "STAGE_INVALID", + 5: "STAGE_MIGRATING", + } + Stage_value = map[string]int32{ + "STAGE_NOT_CREATED": 0, + "STAGE_CREATED": 1, + "STAGE_RUNNING": 2, + "STAGE_TERMINATED": 3, + "STAGE_INVALID": 4, + "STAGE_MIGRATING": 5, + } +) + +func (x Stage) Enum() *Stage { + p := new(Stage) + *p = x + return p +} + +func (x Stage) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Stage) Descriptor() protoreflect.EnumDescriptor { + return file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_enumTypes[0].Descriptor() +} + +func (Stage) Type() protoreflect.EnumType { + return &file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_enumTypes[0] +} + +func (x Stage) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Stage.Descriptor instead. +func (Stage) EnumDescriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_rawDescGZIP(), []int{0} +} + +// Payload is the migration payload owned by the VM controller. It is the +// top-level message wrapped in an [anypb.Any] when handed off between source +// and destination shims. Sub-device controllers (SCSI, VPCI, Plan9) are +// carried as opaque [anypb.Any] payloads so each sub-controller fully owns +// its own schema and versioning. +type Payload struct { + state protoimpl.MessageState `protogen:"open.v1"` + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + SchemaVersion uint32 `protobuf:"varint,1,opt,name=schema_version,json=schemaVersion,proto3" json:"schema_version,omitempty"` + // vm_id is the HCS identifier of the running VM. + VmID string `protobuf:"bytes,2,opt,name=vm_id,json=vmId,proto3" json:"vm_id,omitempty"` + // state is the VM lifecycle stage. + State Stage `protobuf:"varint,3,opt,name=state,proto3,enum=hcsshim.controller.vm.save.v1.Stage" json:"state,omitempty"` + // sandbox_options is the original set of options the VM was built from, + // so the destination can recreate an equivalent VM. + SandboxOptions *SandboxOptions `protobuf:"bytes,4,opt,name=sandbox_options,json=sandboxOptions,proto3" json:"sandbox_options,omitempty"` + // scsi is the SCSI sub-controller's [anypb.Any] envelope (attached + // disks, mounts and outstanding reservations). + Scsi *anypb.Any `protobuf:"bytes,5,opt,name=scsi,proto3" json:"scsi,omitempty"` + // vpci is the VPCI sub-controller's [anypb.Any] envelope (assigned + // devices). + Vpci *anypb.Any `protobuf:"bytes,6,opt,name=vpci,proto3" json:"vpci,omitempty"` + // plan9 is the Plan9 sub-controller's [anypb.Any] envelope (LCOW file + // shares). + Plan9 *anypb.Any `protobuf:"bytes,7,opt,name=plan9,proto3" json:"plan9,omitempty"` + // gcs_next_port is the next free vsock port in the GCS port allocator. + // The destination seeds its allocator with this value so newly opened + // IO channels do not collide with per-process ports restored elsewhere. + GcsNextPort uint32 `protobuf:"varint,8,opt,name=gcs_next_port,json=gcsNextPort,proto3" json:"gcs_next_port,omitempty"` + // compat_info is the opaque, host-emitted compatibility blob describing + // the source VM's compatibility surface. The destination passes it back + // to HCS when starting the target VM so the platform can verify that the + // two VMs can interchange live-migration state. + CompatInfo []byte `protobuf:"bytes,9,opt,name=compat_info,json=compatInfo,proto3" json:"compat_info,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Payload) Reset() { + *x = Payload{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Payload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Payload) ProtoMessage() {} + +func (x *Payload) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_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 Payload.ProtoReflect.Descriptor instead. +func (*Payload) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_rawDescGZIP(), []int{0} +} + +func (x *Payload) GetSchemaVersion() uint32 { + if x != nil { + return x.SchemaVersion + } + return 0 +} + +func (x *Payload) GetVmID() string { + if x != nil { + return x.VmID + } + return "" +} + +func (x *Payload) GetState() Stage { + if x != nil { + return x.State + } + return Stage_STAGE_NOT_CREATED +} + +func (x *Payload) GetSandboxOptions() *SandboxOptions { + if x != nil { + return x.SandboxOptions + } + return nil +} + +func (x *Payload) GetScsi() *anypb.Any { + if x != nil { + return x.Scsi + } + return nil +} + +func (x *Payload) GetVpci() *anypb.Any { + if x != nil { + return x.Vpci + } + return nil +} + +func (x *Payload) GetPlan9() *anypb.Any { + if x != nil { + return x.Plan9 + } + return nil +} + +func (x *Payload) GetGcsNextPort() uint32 { + if x != nil { + return x.GcsNextPort + } + return 0 +} + +func (x *Payload) GetCompatInfo() []byte { + if x != nil { + return x.CompatInfo + } + return nil +} + +// SandboxOptions is the set of VM-creation options preserved verbatim so the +// destination can build an equivalent VM. +type SandboxOptions struct { + state protoimpl.MessageState `protogen:"open.v1"` + // no_writable_file_shares disallows read/write file shares into the guest. + NoWritableFileShares bool `protobuf:"varint,1,opt,name=no_writable_file_shares,json=noWritableFileShares,proto3" json:"no_writable_file_shares,omitempty"` + // enable_scratch_encryption enables dm-crypt on the scratch disk. + EnableScratchEncryption bool `protobuf:"varint,2,opt,name=enable_scratch_encryption,json=enableScratchEncryption,proto3" json:"enable_scratch_encryption,omitempty"` + // policy_based_routing enables policy-based routing in the guest network + // stack. + PolicyBasedRouting bool `protobuf:"varint,3,opt,name=policy_based_routing,json=policyBasedRouting,proto3" json:"policy_based_routing,omitempty"` + // architecture is the guest CPU architecture (e.g. "amd64", "arm64"). + Architecture string `protobuf:"bytes,4,opt,name=architecture,proto3" json:"architecture,omitempty"` + // fully_physically_backed forces all guest memory to be backed by physical + // memory (disables hot-add) and, as a side effect, disables VPMEM. + FullyPhysicallyBacked bool `protobuf:"varint,5,opt,name=fully_physically_backed,json=fullyPhysicallyBacked,proto3" json:"fully_physically_backed,omitempty"` + // confidential carries confidential-compute parameters. + Confidential *ConfidentialConfig `protobuf:"bytes,6,opt,name=confidential,proto3" json:"confidential,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SandboxOptions) Reset() { + *x = SandboxOptions{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SandboxOptions) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SandboxOptions) ProtoMessage() {} + +func (x *SandboxOptions) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_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 SandboxOptions.ProtoReflect.Descriptor instead. +func (*SandboxOptions) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_rawDescGZIP(), []int{1} +} + +func (x *SandboxOptions) GetNoWritableFileShares() bool { + if x != nil { + return x.NoWritableFileShares + } + return false +} + +func (x *SandboxOptions) GetEnableScratchEncryption() bool { + if x != nil { + return x.EnableScratchEncryption + } + return false +} + +func (x *SandboxOptions) GetPolicyBasedRouting() bool { + if x != nil { + return x.PolicyBasedRouting + } + return false +} + +func (x *SandboxOptions) GetArchitecture() string { + if x != nil { + return x.Architecture + } + return "" +} + +func (x *SandboxOptions) GetFullyPhysicallyBacked() bool { + if x != nil { + return x.FullyPhysicallyBacked + } + return false +} + +func (x *SandboxOptions) GetConfidential() *ConfidentialConfig { + if x != nil { + return x.Confidential + } + return nil +} + +// ConfidentialConfig carries confidential-compute parameters for the VM. +type ConfidentialConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + // security_policy is the base64-encoded security-policy document. + SecurityPolicy string `protobuf:"bytes,1,opt,name=security_policy,json=securityPolicy,proto3" json:"security_policy,omitempty"` + // security_policy_enforcer selects the enforcer implementation + // (e.g. "rego", "standard"). + SecurityPolicyEnforcer string `protobuf:"bytes,2,opt,name=security_policy_enforcer,json=securityPolicyEnforcer,proto3" json:"security_policy_enforcer,omitempty"` + // uvm_reference_info_file is the path to the UVM reference-info file used + // to validate measurements. + UvmReferenceInfoFile string `protobuf:"bytes,3,opt,name=uvm_reference_info_file,json=uvmReferenceInfoFile,proto3" json:"uvm_reference_info_file,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ConfidentialConfig) Reset() { + *x = ConfidentialConfig{} + mi := &file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ConfidentialConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConfidentialConfig) ProtoMessage() {} + +func (x *ConfidentialConfig) ProtoReflect() protoreflect.Message { + mi := &file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_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 ConfidentialConfig.ProtoReflect.Descriptor instead. +func (*ConfidentialConfig) Descriptor() ([]byte, []int) { + return file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_rawDescGZIP(), []int{2} +} + +func (x *ConfidentialConfig) GetSecurityPolicy() string { + if x != nil { + return x.SecurityPolicy + } + return "" +} + +func (x *ConfidentialConfig) GetSecurityPolicyEnforcer() string { + if x != nil { + return x.SecurityPolicyEnforcer + } + return "" +} + +func (x *ConfidentialConfig) GetUvmReferenceInfoFile() string { + if x != nil { + return x.UvmReferenceInfoFile + } + return "" +} + +var File_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto protoreflect.FileDescriptor + +const file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_rawDesc = "" + + "\n" + + "Fgithub.com/Microsoft/hcsshim/internal/controller/vm/save/payload.proto\x12\x1dhcsshim.controller.vm.save.v1\x1a\x19google/protobuf/any.proto\"\x9e\x03\n" + + "\aPayload\x12%\n" + + "\x0eschema_version\x18\x01 \x01(\rR\rschemaVersion\x12\x13\n" + + "\x05vm_id\x18\x02 \x01(\tR\x04vmId\x12:\n" + + "\x05state\x18\x03 \x01(\x0e2$.hcsshim.controller.vm.save.v1.StageR\x05state\x12V\n" + + "\x0fsandbox_options\x18\x04 \x01(\v2-.hcsshim.controller.vm.save.v1.SandboxOptionsR\x0esandboxOptions\x12(\n" + + "\x04scsi\x18\x05 \x01(\v2\x14.google.protobuf.AnyR\x04scsi\x12(\n" + + "\x04vpci\x18\x06 \x01(\v2\x14.google.protobuf.AnyR\x04vpci\x12*\n" + + "\x05plan9\x18\a \x01(\v2\x14.google.protobuf.AnyR\x05plan9\x12\"\n" + + "\rgcs_next_port\x18\b \x01(\rR\vgcsNextPort\x12\x1f\n" + + "\vcompat_info\x18\t \x01(\fR\n" + + "compatInfo\"\xe8\x02\n" + + "\x0eSandboxOptions\x125\n" + + "\x17no_writable_file_shares\x18\x01 \x01(\bR\x14noWritableFileShares\x12:\n" + + "\x19enable_scratch_encryption\x18\x02 \x01(\bR\x17enableScratchEncryption\x120\n" + + "\x14policy_based_routing\x18\x03 \x01(\bR\x12policyBasedRouting\x12\"\n" + + "\farchitecture\x18\x04 \x01(\tR\farchitecture\x126\n" + + "\x17fully_physically_backed\x18\x05 \x01(\bR\x15fullyPhysicallyBacked\x12U\n" + + "\fconfidential\x18\x06 \x01(\v21.hcsshim.controller.vm.save.v1.ConfidentialConfigR\fconfidential\"\xae\x01\n" + + "\x12ConfidentialConfig\x12'\n" + + "\x0fsecurity_policy\x18\x01 \x01(\tR\x0esecurityPolicy\x128\n" + + "\x18security_policy_enforcer\x18\x02 \x01(\tR\x16securityPolicyEnforcer\x125\n" + + "\x17uvm_reference_info_file\x18\x03 \x01(\tR\x14uvmReferenceInfoFile*\x82\x01\n" + + "\x05Stage\x12\x15\n" + + "\x11STAGE_NOT_CREATED\x10\x00\x12\x11\n" + + "\rSTAGE_CREATED\x10\x01\x12\x11\n" + + "\rSTAGE_RUNNING\x10\x02\x12\x14\n" + + "\x10STAGE_TERMINATED\x10\x03\x12\x11\n" + + "\rSTAGE_INVALID\x10\x04\x12\x13\n" + + "\x0fSTAGE_MIGRATING\x10\x05B?Z=github.com/Microsoft/hcsshim/internal/controller/vm/save;saveb\x06proto3" + +var ( + file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_rawDescOnce sync.Once + file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_rawDescData []byte +) + +func file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_rawDescGZIP() []byte { + file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_rawDescOnce.Do(func() { + file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_rawDesc))) + }) + return file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_rawDescData +} + +var file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_goTypes = []any{ + (Stage)(0), // 0: hcsshim.controller.vm.save.v1.Stage + (*Payload)(nil), // 1: hcsshim.controller.vm.save.v1.Payload + (*SandboxOptions)(nil), // 2: hcsshim.controller.vm.save.v1.SandboxOptions + (*ConfidentialConfig)(nil), // 3: hcsshim.controller.vm.save.v1.ConfidentialConfig + (*anypb.Any)(nil), // 4: google.protobuf.Any +} +var file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_depIdxs = []int32{ + 0, // 0: hcsshim.controller.vm.save.v1.Payload.state:type_name -> hcsshim.controller.vm.save.v1.Stage + 2, // 1: hcsshim.controller.vm.save.v1.Payload.sandbox_options:type_name -> hcsshim.controller.vm.save.v1.SandboxOptions + 4, // 2: hcsshim.controller.vm.save.v1.Payload.scsi:type_name -> google.protobuf.Any + 4, // 3: hcsshim.controller.vm.save.v1.Payload.vpci:type_name -> google.protobuf.Any + 4, // 4: hcsshim.controller.vm.save.v1.Payload.plan9:type_name -> google.protobuf.Any + 3, // 5: hcsshim.controller.vm.save.v1.SandboxOptions.confidential:type_name -> hcsshim.controller.vm.save.v1.ConfidentialConfig + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_init() } +func file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_init() { + if File_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_rawDesc), len(file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_rawDesc)), + NumEnums: 1, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_goTypes, + DependencyIndexes: file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_depIdxs, + EnumInfos: file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_enumTypes, + MessageInfos: file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_msgTypes, + }.Build() + File_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto = out.File + file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_goTypes = nil + file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_depIdxs = nil +} diff --git a/internal/controller/vm/save/payload.proto b/internal/controller/vm/save/payload.proto new file mode 100644 index 0000000000..f92dd5c721 --- /dev/null +++ b/internal/controller/vm/save/payload.proto @@ -0,0 +1,105 @@ +syntax = "proto3"; + +package hcsshim.controller.vm.save.v1; + +option go_package = "github.com/Microsoft/hcsshim/internal/controller/vm/save;save"; + +import "google/protobuf/any.proto"; + +// ============================================================================= +// Lifecycle stage enum +// +// Stage mirrors the Go State type used by the VM controller. The zero value +// matches the Go iota-zero value (NotCreated). +// ============================================================================= + +enum Stage { + STAGE_NOT_CREATED = 0; + STAGE_CREATED = 1; + STAGE_RUNNING = 2; + STAGE_TERMINATED = 3; + STAGE_INVALID = 4; + STAGE_MIGRATING = 5; +} + +// Payload is the migration payload owned by the VM controller. It is the +// top-level message wrapped in an [anypb.Any] when handed off between source +// and destination shims. Sub-device controllers (SCSI, VPCI, Plan9) are +// carried as opaque [anypb.Any] payloads so each sub-controller fully owns +// its own schema and versioning. +message Payload { + // schema_version is bumped whenever a field's semantics change. The + // destination MUST reject states whose version it does not recognise. + uint32 schema_version = 1; + + // vm_id is the HCS identifier of the running VM. + string vm_id = 2; + + // state is the VM lifecycle stage. + Stage state = 3; + + // sandbox_options is the original set of options the VM was built from, + // so the destination can recreate an equivalent VM. + SandboxOptions sandbox_options = 4; + + // scsi is the SCSI sub-controller's [anypb.Any] envelope (attached + // disks, mounts and outstanding reservations). + google.protobuf.Any scsi = 5; + + // vpci is the VPCI sub-controller's [anypb.Any] envelope (assigned + // devices). + google.protobuf.Any vpci = 6; + + // plan9 is the Plan9 sub-controller's [anypb.Any] envelope (LCOW file + // shares). + google.protobuf.Any plan9 = 7; + + // gcs_next_port is the next free vsock port in the GCS port allocator. + // The destination seeds its allocator with this value so newly opened + // IO channels do not collide with per-process ports restored elsewhere. + uint32 gcs_next_port = 8; + + // compat_info is the opaque, host-emitted compatibility blob describing + // the source VM's compatibility surface. The destination passes it back + // to HCS when starting the target VM so the platform can verify that the + // two VMs can interchange live-migration state. + bytes compat_info = 9; +} + +// SandboxOptions is the set of VM-creation options preserved verbatim so the +// destination can build an equivalent VM. +message SandboxOptions { + // no_writable_file_shares disallows read/write file shares into the guest. + bool no_writable_file_shares = 1; + + // enable_scratch_encryption enables dm-crypt on the scratch disk. + bool enable_scratch_encryption = 2; + + // policy_based_routing enables policy-based routing in the guest network + // stack. + bool policy_based_routing = 3; + + // architecture is the guest CPU architecture (e.g. "amd64", "arm64"). + string architecture = 4; + + // fully_physically_backed forces all guest memory to be backed by physical + // memory (disables hot-add) and, as a side effect, disables VPMEM. + bool fully_physically_backed = 5; + + // confidential carries confidential-compute parameters. + ConfidentialConfig confidential = 6; +} + +// ConfidentialConfig carries confidential-compute parameters for the VM. +message ConfidentialConfig { + // security_policy is the base64-encoded security-policy document. + string security_policy = 1; + + // security_policy_enforcer selects the enforcer implementation + // (e.g. "rego", "standard"). + string security_policy_enforcer = 2; + + // uvm_reference_info_file is the path to the UVM reference-info file used + // to validate measurements. + string uvm_reference_info_file = 3; +}