From d20642863e57e4838a74fb2dbced7ac33bbf87d0 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: 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/scsi/save/constants.go | 16 + .../controller/device/scsi/save/payload.pb.go | 566 ++++++++++++++++++ .../controller/device/scsi/save/payload.proto | 105 ++++ .../linuxcontainer/save/constants.go | 17 + .../linuxcontainer/save/payload.pb.go | 355 +++++++++++ .../linuxcontainer/save/payload.proto | 71 +++ .../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 | 248 ++++++++ .../controller/network/save/payload.proto | 42 ++ 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 | 229 +++++++ .../controller/process/save/payload.proto | 45 ++ internal/controller/vm/save/constants.go | 17 + internal/controller/vm/save/payload.pb.go | 308 ++++++++++ internal/controller/vm/save/payload.proto | 72 +++ 22 files changed, 2539 insertions(+), 1 deletion(-) 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/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/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..08c490cb74 --- /dev/null +++ b/internal/controller/device/scsi/save/payload.pb.go @@ -0,0 +1,566 @@ +// 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) +) + +// 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. +// +// A payload is only emitted when every disk is in the Attached stage and +// every mount is in the Mounted stage, so per-entry lifecycle stages are not +// carried on the wire. +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]. Every disk in a payload is implicitly in the Attached +// stage. +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"` + // mounts is keyed by partition number on the disk. + Mounts map[uint64]*MountState `protobuf:"bytes,2,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) 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"` + // ref_count is the number of live holders of this mount. + RefCount uint32 `protobuf:"varint,2,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,3,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) 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\"\x88\x02\n" + + "\tDiskState\x12C\n" + + "\x06config\x18\x01 \x01(\v2+.hcsshim.controller.scsi.save.v1.DiskConfigR\x06config\x12N\n" + + "\x06mounts\x18\x02 \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\"\x8e\x01\n" + + "\n" + + "MountState\x12D\n" + + "\x06config\x18\x01 \x01(\v2,.hcsshim.controller.scsi.save.v1.MountConfigR\x06config\x12\x1b\n" + + "\tref_count\x18\x02 \x01(\rR\brefCount\x12\x1d\n" + + "\n" + + "guest_path\x18\x03 \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\tpartitionBHZFgithub.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_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_goTypes = []any{ + (*Payload)(nil), // 0: hcsshim.controller.scsi.save.v1.Payload + (*DiskState)(nil), // 1: hcsshim.controller.scsi.save.v1.DiskState + (*DiskConfig)(nil), // 2: hcsshim.controller.scsi.save.v1.DiskConfig + (*MountState)(nil), // 3: hcsshim.controller.scsi.save.v1.MountState + (*MountConfig)(nil), // 4: hcsshim.controller.scsi.save.v1.MountConfig + (*Reservation)(nil), // 5: hcsshim.controller.scsi.save.v1.Reservation + nil, // 6: hcsshim.controller.scsi.save.v1.Payload.DisksEntry + nil, // 7: hcsshim.controller.scsi.save.v1.Payload.ReservationsEntry + nil, // 8: hcsshim.controller.scsi.save.v1.DiskState.MountsEntry +} +var file_github_com_Microsoft_hcsshim_internal_controller_device_scsi_save_payload_proto_depIdxs = []int32{ + 6, // 0: hcsshim.controller.scsi.save.v1.Payload.disks:type_name -> hcsshim.controller.scsi.save.v1.Payload.DisksEntry + 7, // 1: hcsshim.controller.scsi.save.v1.Payload.reservations:type_name -> hcsshim.controller.scsi.save.v1.Payload.ReservationsEntry + 2, // 2: hcsshim.controller.scsi.save.v1.DiskState.config:type_name -> hcsshim.controller.scsi.save.v1.DiskConfig + 8, // 3: hcsshim.controller.scsi.save.v1.DiskState.mounts:type_name -> hcsshim.controller.scsi.save.v1.DiskState.MountsEntry + 4, // 4: hcsshim.controller.scsi.save.v1.MountState.config:type_name -> hcsshim.controller.scsi.save.v1.MountConfig + 1, // 5: hcsshim.controller.scsi.save.v1.Payload.DisksEntry.value:type_name -> hcsshim.controller.scsi.save.v1.DiskState + 5, // 6: hcsshim.controller.scsi.save.v1.Payload.ReservationsEntry.value:type_name -> hcsshim.controller.scsi.save.v1.Reservation + 3, // 7: hcsshim.controller.scsi.save.v1.DiskState.MountsEntry.value:type_name -> hcsshim.controller.scsi.save.v1.MountState + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] 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: 0, + 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, + 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..56748063f2 --- /dev/null +++ b/internal/controller/device/scsi/save/payload.proto @@ -0,0 +1,105 @@ +syntax = "proto3"; + +package hcsshim.controller.scsi.save.v1; + +option go_package = "github.com/Microsoft/hcsshim/internal/controller/device/scsi/save;save"; + +// 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. +// +// A payload is only emitted when every disk is in the Attached stage and +// every mount is in the Mounted stage, so per-entry lifecycle stages are not +// carried on the wire. +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]. Every disk in a payload is implicitly in the Attached +// stage. +message DiskState { + // config is the disk attachment configuration. + DiskConfig config = 1; + + // mounts is keyed by partition number on the disk. + map mounts = 2; +} + +// 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; + + // ref_count is the number of live holders of this mount. + uint32 ref_count = 2; + + // guest_path is where the partition is mounted inside the guest. + string guest_path = 3; +} + +// 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/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..03551826ac --- /dev/null +++ b/internal/controller/linuxcontainer/save/payload.pb.go @@ -0,0 +1,355 @@ +// 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) +) + +// 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"` + // io_retry_timeout is how long IO relay reconnects are retried for this + // container. + IoRetryTimeout *durationpb.Duration `protobuf:"bytes,4,opt,name=io_retry_timeout,json=ioRetryTimeout,proto3" json:"io_retry_timeout,omitempty"` + // layers is the container's rootfs composition. + Layers *Layers `protobuf:"bytes,5,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,6,rep,name=scsi_reservation_ids,json=scsiReservationIds,proto3" json:"scsi_reservation_ids,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,7,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) 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) 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\"\xf4\x03\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\x12C\n" + + "\x10io_retry_timeout\x18\x04 \x01(\v2\x19.google.protobuf.DurationR\x0eioRetryTimeout\x12I\n" + + "\x06layers\x18\x05 \x01(\v21.hcsshim.controller.linuxcontainer.save.v1.LayersR\x06layers\x120\n" + + "\x14scsi_reservation_ids\x18\x06 \x03(\tR\x12scsiReservationIds\x12_\n" + + "\tprocesses\x18\a \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\tguestPathBKZIgithub.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_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_goTypes = []any{ + (*Payload)(nil), // 0: hcsshim.controller.linuxcontainer.save.v1.Payload + (*Layers)(nil), // 1: hcsshim.controller.linuxcontainer.save.v1.Layers + (*LayerReservation)(nil), // 2: hcsshim.controller.linuxcontainer.save.v1.LayerReservation + nil, // 3: hcsshim.controller.linuxcontainer.save.v1.Payload.ProcessesEntry + (*durationpb.Duration)(nil), // 4: google.protobuf.Duration + (*anypb.Any)(nil), // 5: google.protobuf.Any +} +var file_github_com_Microsoft_hcsshim_internal_controller_linuxcontainer_save_payload_proto_depIdxs = []int32{ + 4, // 0: hcsshim.controller.linuxcontainer.save.v1.Payload.io_retry_timeout:type_name -> google.protobuf.Duration + 1, // 1: hcsshim.controller.linuxcontainer.save.v1.Payload.layers:type_name -> hcsshim.controller.linuxcontainer.save.v1.Layers + 3, // 2: hcsshim.controller.linuxcontainer.save.v1.Payload.processes:type_name -> hcsshim.controller.linuxcontainer.save.v1.Payload.ProcessesEntry + 2, // 3: hcsshim.controller.linuxcontainer.save.v1.Layers.ro_layers:type_name -> hcsshim.controller.linuxcontainer.save.v1.LayerReservation + 2, // 4: hcsshim.controller.linuxcontainer.save.v1.Layers.scratch:type_name -> hcsshim.controller.linuxcontainer.save.v1.LayerReservation + 5, // 5: hcsshim.controller.linuxcontainer.save.v1.Payload.ProcessesEntry.value:type_name -> google.protobuf.Any + 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_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: 0, + 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, + 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..f4d66197a6 --- /dev/null +++ b/internal/controller/linuxcontainer/save/payload.proto @@ -0,0 +1,71 @@ +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"; + +// 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; + + // io_retry_timeout is how long IO relay reconnects are retried for this + // container. + google.protobuf.Duration io_retry_timeout = 4; + + // layers is the container's rootfs composition. + Layers layers = 5; + + // 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 = 6; + + // 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 = 7; +} + +// 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..3b4694ebdc --- /dev/null +++ b/internal/controller/network/save/payload.pb.go @@ -0,0 +1,248 @@ +// 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) +) + +// 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"` + // 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,5,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) 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\"\xa1\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" + + "\fvm_endpoints\x18\x05 \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\fendpointNameBDZBgithub.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_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_goTypes = []any{ + (*Payload)(nil), // 0: hcsshim.controller.network.save.v1.Payload + (*EndpointBinding)(nil), // 1: hcsshim.controller.network.save.v1.EndpointBinding + nil, // 2: hcsshim.controller.network.save.v1.Payload.VmEndpointsEntry +} +var file_github_com_Microsoft_hcsshim_internal_controller_network_save_payload_proto_depIdxs = []int32{ + 2, // 0: hcsshim.controller.network.save.v1.Payload.vm_endpoints:type_name -> hcsshim.controller.network.save.v1.Payload.VmEndpointsEntry + 1, // 1: hcsshim.controller.network.save.v1.Payload.VmEndpointsEntry.value:type_name -> hcsshim.controller.network.save.v1.EndpointBinding + 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_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: 0, + 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, + 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..9f9f8e537f --- /dev/null +++ b/internal/controller/network/save/payload.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; + +package hcsshim.controller.network.save.v1; + +option go_package = "github.com/Microsoft/hcsshim/internal/controller/network/save;save"; + +// 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; + + // 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 = 5; +} + +// 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..764ae048b1 --- /dev/null +++ b/internal/controller/process/save/payload.pb.go @@ -0,0 +1,229 @@ +// 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" + 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 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"` + // pid is the process ID inside the guest. + Pid int32 `protobuf:"varint,3,opt,name=pid,proto3" json:"pid,omitempty"` + // bundle is the OCI bundle path on the host. + Bundle string `protobuf:"bytes,4,opt,name=bundle,proto3" json:"bundle,omitempty"` + // oci_process_spec_json is the JSON-encoded OCI process specification. + OciProcessSpecJson []byte `protobuf:"bytes,5,opt,name=oci_process_spec_json,json=ociProcessSpecJson,proto3" json:"oci_process_spec_json,omitempty"` + // io_retry_timeout is how long IO relay reconnects are retried for this + // process. + IoRetryTimeout *durationpb.Duration `protobuf:"bytes,6,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,7,opt,name=stdin_port,json=stdinPort,proto3" json:"stdin_port,omitempty"` + StdoutPort uint32 `protobuf:"varint,8,opt,name=stdout_port,json=stdoutPort,proto3" json:"stdout_port,omitempty"` + StderrPort uint32 `protobuf:"varint,9,opt,name=stderr_port,json=stderrPort,proto3" json:"stderr_port,omitempty"` + // wait_call_id is the source bridge's outstanding WaitForProcess request + // id. The destination pre-registers a stub rpc against this id instead of + // issuing a duplicate wait. Zero if absent. + WaitCallID int64 `protobuf:"varint,10,opt,name=wait_call_id,json=waitCallId,proto3" json:"wait_call_id,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) 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) 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 +} + +func (x *Payload) GetWaitCallID() int64 { + if x != nil { + return x.WaitCallID + } + 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\"\xee\x02\n" + + "\aPayload\x12%\n" + + "\x0eschema_version\x18\x01 \x01(\rR\rschemaVersion\x12\x17\n" + + "\aexec_id\x18\x02 \x01(\tR\x06execId\x12\x10\n" + + "\x03pid\x18\x03 \x01(\x05R\x03pid\x12\x16\n" + + "\x06bundle\x18\x04 \x01(\tR\x06bundle\x121\n" + + "\x15oci_process_spec_json\x18\x05 \x01(\fR\x12ociProcessSpecJson\x12C\n" + + "\x10io_retry_timeout\x18\x06 \x01(\v2\x19.google.protobuf.DurationR\x0eioRetryTimeout\x12\x1d\n" + + "\n" + + "stdin_port\x18\a \x01(\rR\tstdinPort\x12\x1f\n" + + "\vstdout_port\x18\b \x01(\rR\n" + + "stdoutPort\x12\x1f\n" + + "\vstderr_port\x18\t \x01(\rR\n" + + "stderrPort\x12 \n" + + "\fwait_call_id\x18\n" + + " \x01(\x03R\n" + + "waitCallIdBDZBgithub.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_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_goTypes = []any{ + (*Payload)(nil), // 0: hcsshim.controller.process.save.v1.Payload + (*durationpb.Duration)(nil), // 1: google.protobuf.Duration +} +var file_github_com_Microsoft_hcsshim_internal_controller_process_save_payload_proto_depIdxs = []int32{ + 1, // 0: hcsshim.controller.process.save.v1.Payload.io_retry_timeout:type_name -> google.protobuf.Duration + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] 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: 0, + 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, + 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..cb815e3418 --- /dev/null +++ b/internal/controller/process/save/payload.proto @@ -0,0 +1,45 @@ +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"; + +// 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; + + // pid is the process ID inside the guest. + int32 pid = 3; + + // bundle is the OCI bundle path on the host. + string bundle = 4; + + // oci_process_spec_json is the JSON-encoded OCI process specification. + bytes oci_process_spec_json = 5; + + // io_retry_timeout is how long IO relay reconnects are retried for this + // process. + google.protobuf.Duration io_retry_timeout = 6; + + // 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 = 7; + uint32 stdout_port = 8; + uint32 stderr_port = 9; + + // wait_call_id is the source bridge's outstanding WaitForProcess request + // id. The destination pre-registers a stub rpc against this id instead of + // issuing a duplicate wait. Zero if absent. + int64 wait_call_id = 10; +} 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..f095cfec73 --- /dev/null +++ b/internal/controller/vm/save/payload.pb.go @@ -0,0 +1,308 @@ +// 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) +) + +// 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. The SCSI sub-controller is carried as an opaque +// [anypb.Any] so it 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"` + // 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,3,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,4,opt,name=scsi,proto3" json:"scsi,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,5,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,6,opt,name=compat_info,json=compatInfo,proto3" json:"compat_info,omitempty"` + // hcs_document is the JSON-encoded HCS [hcsschema.ComputeSystem] document + // that was used to create the source VM. The destination reuses this + // document verbatim (with [hcsschema.VirtualMachine.MigrationOptions] + // set) when creating the destination VM, ensuring the two VMs have + // identical hardware topology required for live-migration compatibility. + HcsDocument []byte `protobuf:"bytes,7,opt,name=hcs_document,json=hcsDocument,proto3" json:"hcs_document,omitempty"` + // bridge_next_id is the source bridge's next request-id. The destination + // seeds its bridge above this so newly issued ids cannot collide with + // requests the guest still has outstanding from the source. + BridgeNextID int64 `protobuf:"varint,8,opt,name=bridge_next_id,json=bridgeNextId,proto3" json:"bridge_next_id,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) 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) GetGcsNextPort() uint32 { + if x != nil { + return x.GcsNextPort + } + return 0 +} + +func (x *Payload) GetCompatInfo() []byte { + if x != nil { + return x.CompatInfo + } + return nil +} + +func (x *Payload) GetHcsDocument() []byte { + if x != nil { + return x.HcsDocument + } + return nil +} + +func (x *Payload) GetBridgeNextID() int64 { + if x != nil { + return x.BridgeNextID + } + return 0 +} + +// 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"` + 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 +} + +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\"\xd5\x02\n" + + "\aPayload\x12%\n" + + "\x0eschema_version\x18\x01 \x01(\rR\rschemaVersion\x12\x13\n" + + "\x05vm_id\x18\x02 \x01(\tR\x04vmId\x12V\n" + + "\x0fsandbox_options\x18\x03 \x01(\v2-.hcsshim.controller.vm.save.v1.SandboxOptionsR\x0esandboxOptions\x12(\n" + + "\x04scsi\x18\x04 \x01(\v2\x14.google.protobuf.AnyR\x04scsi\x12\"\n" + + "\rgcs_next_port\x18\x05 \x01(\rR\vgcsNextPort\x12\x1f\n" + + "\vcompat_info\x18\x06 \x01(\fR\n" + + "compatInfo\x12!\n" + + "\fhcs_document\x18\a \x01(\fR\vhcsDocument\x12$\n" + + "\x0ebridge_next_id\x18\b \x01(\x03R\fbridgeNextId\"\x91\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\x15fullyPhysicallyBackedB?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_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_goTypes = []any{ + (*Payload)(nil), // 0: hcsshim.controller.vm.save.v1.Payload + (*SandboxOptions)(nil), // 1: hcsshim.controller.vm.save.v1.SandboxOptions + (*anypb.Any)(nil), // 2: google.protobuf.Any +} +var file_github_com_Microsoft_hcsshim_internal_controller_vm_save_payload_proto_depIdxs = []int32{ + 1, // 0: hcsshim.controller.vm.save.v1.Payload.sandbox_options:type_name -> hcsshim.controller.vm.save.v1.SandboxOptions + 2, // 1: hcsshim.controller.vm.save.v1.Payload.scsi: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_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: 0, + NumMessages: 2, + 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, + 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..7e539f9818 --- /dev/null +++ b/internal/controller/vm/save/payload.proto @@ -0,0 +1,72 @@ +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"; + +// 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. The SCSI sub-controller is carried as an opaque +// [anypb.Any] so it 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; + + // sandbox_options is the original set of options the VM was built from, + // so the destination can recreate an equivalent VM. + SandboxOptions sandbox_options = 3; + + // scsi is the SCSI sub-controller's [anypb.Any] envelope (attached + // disks, mounts and outstanding reservations). + google.protobuf.Any scsi = 4; + + // 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 = 5; + + // 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 = 6; + + // hcs_document is the JSON-encoded HCS [hcsschema.ComputeSystem] document + // that was used to create the source VM. The destination reuses this + // document verbatim (with [hcsschema.VirtualMachine.MigrationOptions] + // set) when creating the destination VM, ensuring the two VMs have + // identical hardware topology required for live-migration compatibility. + bytes hcs_document = 7; + + // bridge_next_id is the source bridge's next request-id. The destination + // seeds its bridge above this so newly issued ids cannot collide with + // requests the guest still has outstanding from the source. + int64 bridge_next_id = 8; +} + +// 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; +}