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