diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go new file mode 100644 index 0000000000..fc39c2b4c2 --- /dev/null +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.pb.go @@ -0,0 +1,765 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.1 +// protoc (unknown) +// source: xyz/block/ftl/hotreload/v1/hotreload.proto + +package hotreloadpb + +import ( + v11 "github.com/block/ftl/backend/protos/xyz/block/ftl/language/v1" + v12 "github.com/block/ftl/backend/protos/xyz/block/ftl/v1" + v1 "github.com/block/ftl/common/protos/xyz/block/ftl/schema/v1" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +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 ReloadRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Force bool `protobuf:"varint,1,opt,name=force,proto3" json:"force,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReloadRequest) Reset() { + *x = ReloadRequest{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReloadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReloadRequest) ProtoMessage() {} + +func (x *ReloadRequest) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_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 ReloadRequest.ProtoReflect.Descriptor instead. +func (*ReloadRequest) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{0} +} + +func (x *ReloadRequest) GetForce() bool { + if x != nil { + return x.Force + } + return false +} + +type ReloadResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Event: + // + // *ReloadResponse_ReloadNotRequired + // *ReloadResponse_ReloadSuccess + // *ReloadResponse_ReloadFailed + Event isReloadResponse_Event `protobuf_oneof:"event"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReloadResponse) Reset() { + *x = ReloadResponse{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReloadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReloadResponse) ProtoMessage() {} + +func (x *ReloadResponse) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_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 ReloadResponse.ProtoReflect.Descriptor instead. +func (*ReloadResponse) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{1} +} + +func (x *ReloadResponse) GetEvent() isReloadResponse_Event { + if x != nil { + return x.Event + } + return nil +} + +func (x *ReloadResponse) GetReloadNotRequired() *ReloadNotRequired { + if x != nil { + if x, ok := x.Event.(*ReloadResponse_ReloadNotRequired); ok { + return x.ReloadNotRequired + } + } + return nil +} + +func (x *ReloadResponse) GetReloadSuccess() *ReloadSuccess { + if x != nil { + if x, ok := x.Event.(*ReloadResponse_ReloadSuccess); ok { + return x.ReloadSuccess + } + } + return nil +} + +func (x *ReloadResponse) GetReloadFailed() *ReloadFailed { + if x != nil { + if x, ok := x.Event.(*ReloadResponse_ReloadFailed); ok { + return x.ReloadFailed + } + } + return nil +} + +type isReloadResponse_Event interface { + isReloadResponse_Event() +} + +type ReloadResponse_ReloadNotRequired struct { + ReloadNotRequired *ReloadNotRequired `protobuf:"bytes,1,opt,name=reload_not_required,json=reloadNotRequired,proto3,oneof"` +} + +type ReloadResponse_ReloadSuccess struct { + ReloadSuccess *ReloadSuccess `protobuf:"bytes,2,opt,name=reload_success,json=reloadSuccess,proto3,oneof"` +} + +type ReloadResponse_ReloadFailed struct { + ReloadFailed *ReloadFailed `protobuf:"bytes,3,opt,name=reload_failed,json=reloadFailed,proto3,oneof"` +} + +func (*ReloadResponse_ReloadNotRequired) isReloadResponse_Event() {} + +func (*ReloadResponse_ReloadSuccess) isReloadResponse_Event() {} + +func (*ReloadResponse_ReloadFailed) isReloadResponse_Event() {} + +type WatchRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchRequest) Reset() { + *x = WatchRequest{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchRequest) ProtoMessage() {} + +func (x *WatchRequest) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_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 WatchRequest.ProtoReflect.Descriptor instead. +func (*WatchRequest) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{2} +} + +type WatchResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Event: + // + // *WatchResponse_ReloadSuccess + // *WatchResponse_ReloadFailed + Event isWatchResponse_Event `protobuf_oneof:"event"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchResponse) Reset() { + *x = WatchResponse{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchResponse) ProtoMessage() {} + +func (x *WatchResponse) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_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 WatchResponse.ProtoReflect.Descriptor instead. +func (*WatchResponse) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{3} +} + +func (x *WatchResponse) GetEvent() isWatchResponse_Event { + if x != nil { + return x.Event + } + return nil +} + +func (x *WatchResponse) GetReloadSuccess() *ReloadSuccess { + if x != nil { + if x, ok := x.Event.(*WatchResponse_ReloadSuccess); ok { + return x.ReloadSuccess + } + } + return nil +} + +func (x *WatchResponse) GetReloadFailed() *ReloadFailed { + if x != nil { + if x, ok := x.Event.(*WatchResponse_ReloadFailed); ok { + return x.ReloadFailed + } + } + return nil +} + +type isWatchResponse_Event interface { + isWatchResponse_Event() +} + +type WatchResponse_ReloadSuccess struct { + ReloadSuccess *ReloadSuccess `protobuf:"bytes,1,opt,name=reload_success,json=reloadSuccess,proto3,oneof"` +} + +type WatchResponse_ReloadFailed struct { + ReloadFailed *ReloadFailed `protobuf:"bytes,2,opt,name=reload_failed,json=reloadFailed,proto3,oneof"` +} + +func (*WatchResponse_ReloadSuccess) isWatchResponse_Event() {} + +func (*WatchResponse_ReloadFailed) isWatchResponse_Event() {} + +type RunnerInfoRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"` + Deployment string `protobuf:"bytes,2,opt,name=deployment,proto3" json:"deployment,omitempty"` + Databases []*Database `protobuf:"bytes,3,rep,name=databases,proto3" json:"databases,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RunnerInfoRequest) Reset() { + *x = RunnerInfoRequest{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RunnerInfoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunnerInfoRequest) ProtoMessage() {} + +func (x *RunnerInfoRequest) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_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 RunnerInfoRequest.ProtoReflect.Descriptor instead. +func (*RunnerInfoRequest) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{4} +} + +func (x *RunnerInfoRequest) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +func (x *RunnerInfoRequest) GetDeployment() string { + if x != nil { + return x.Deployment + } + return "" +} + +func (x *RunnerInfoRequest) GetDatabases() []*Database { + if x != nil { + return x.Databases + } + return nil +} + +type Database struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Database) Reset() { + *x = Database{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Database) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Database) ProtoMessage() {} + +func (x *Database) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_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 Database.ProtoReflect.Descriptor instead. +func (*Database) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{5} +} + +func (x *Database) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Database) GetAddress() string { + if x != nil { + return x.Address + } + return "" +} + +type RunnerInfoResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RunnerInfoResponse) Reset() { + *x = RunnerInfoResponse{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RunnerInfoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RunnerInfoResponse) ProtoMessage() {} + +func (x *RunnerInfoResponse) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RunnerInfoResponse.ProtoReflect.Descriptor instead. +func (*RunnerInfoResponse) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{6} +} + +type ReloadNotRequired struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReloadNotRequired) Reset() { + *x = ReloadNotRequired{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReloadNotRequired) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReloadNotRequired) ProtoMessage() {} + +func (x *ReloadNotRequired) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReloadNotRequired.ProtoReflect.Descriptor instead. +func (*ReloadNotRequired) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{7} +} + +type ReloadSuccess struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Module schema for the built module + Module *v1.Module `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` + // Module schema for the built module + Errors *v11.ErrorList `protobuf:"bytes,2,opt,name=errors,proto3" json:"errors,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReloadSuccess) Reset() { + *x = ReloadSuccess{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReloadSuccess) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReloadSuccess) ProtoMessage() {} + +func (x *ReloadSuccess) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReloadSuccess.ProtoReflect.Descriptor instead. +func (*ReloadSuccess) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{8} +} + +func (x *ReloadSuccess) GetModule() *v1.Module { + if x != nil { + return x.Module + } + return nil +} + +func (x *ReloadSuccess) GetErrors() *v11.ErrorList { + if x != nil { + return x.Errors + } + return nil +} + +type ReloadFailed struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Module schema for the built module + Errors *v11.ErrorList `protobuf:"bytes,1,opt,name=errors,proto3" json:"errors,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ReloadFailed) Reset() { + *x = ReloadFailed{} + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ReloadFailed) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReloadFailed) ProtoMessage() {} + +func (x *ReloadFailed) ProtoReflect() protoreflect.Message { + mi := &file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReloadFailed.ProtoReflect.Descriptor instead. +func (*ReloadFailed) Descriptor() ([]byte, []int) { + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP(), []int{9} +} + +func (x *ReloadFailed) GetErrors() *v11.ErrorList { + if x != nil { + return x.Errors + } + return nil +} + +var File_xyz_block_ftl_hotreload_v1_hotreload_proto protoreflect.FileDescriptor + +var file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc = []byte{ + 0x0a, 0x2a, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, + 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2f, 0x76, 0x31, 0x2f, 0x68, 0x6f, 0x74, + 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1a, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, + 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x1a, 0x28, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, + 0x2f, 0x76, 0x31, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x24, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, + 0x6c, 0x2f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x74, 0x6c, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x25, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x22, 0x9f, 0x02, 0x0a, 0x0e, + 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5f, + 0x0a, 0x13, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6e, 0x6f, 0x74, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x69, 0x72, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, + 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x4e, + 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x48, 0x00, 0x52, 0x11, 0x72, 0x65, + 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, + 0x52, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, + 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x12, 0x4f, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, + 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x0e, 0x0a, + 0x0c, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0xbd, 0x01, + 0x0a, 0x0d, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x52, 0x0a, 0x0e, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, + 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x48, 0x00, 0x52, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x12, 0x4f, 0x0a, 0x0d, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x66, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, + 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x48, 0x00, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, + 0x69, 0x6c, 0x65, 0x64, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x91, 0x01, + 0x0a, 0x11, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1e, 0x0a, + 0x0a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x64, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x42, 0x0a, + 0x09, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, + 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, + 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x09, 0x64, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, + 0x73, 0x22, 0x38, 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x14, 0x0a, 0x12, 0x52, + 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x13, 0x0a, 0x11, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x4e, 0x6f, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x22, 0x86, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x6c, 0x6f, 0x61, + 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x37, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, + 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, + 0x65, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, + 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, + 0x4c, 0x0a, 0x0c, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x12, + 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x32, 0x8c, 0x03, + 0x0a, 0x10, 0x48, 0x6f, 0x74, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, + 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, + 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x5f, + 0x0a, 0x06, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, + 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, + 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x5e, 0x0a, 0x05, 0x57, 0x61, 0x74, 0x63, 0x68, 0x12, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, + 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, + 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, + 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, + 0x6b, 0x0a, 0x0a, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x2e, + 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, + 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x6e, 0x65, + 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x78, + 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x68, 0x6f, 0x74, + 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4e, 0x50, 0x01, + 0x5a, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, + 0x66, 0x74, 0x6c, 0x2f, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x2f, 0x76, 0x31, + 0x3b, 0x68, 0x6f, 0x74, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescOnce sync.Once + file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescData = file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc +) + +func file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescGZIP() []byte { + file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescOnce.Do(func() { + file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescData = protoimpl.X.CompressGZIP(file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescData) + }) + return file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDescData +} + +var file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_xyz_block_ftl_hotreload_v1_hotreload_proto_goTypes = []any{ + (*ReloadRequest)(nil), // 0: xyz.block.ftl.hotreload.v1.ReloadRequest + (*ReloadResponse)(nil), // 1: xyz.block.ftl.hotreload.v1.ReloadResponse + (*WatchRequest)(nil), // 2: xyz.block.ftl.hotreload.v1.WatchRequest + (*WatchResponse)(nil), // 3: xyz.block.ftl.hotreload.v1.WatchResponse + (*RunnerInfoRequest)(nil), // 4: xyz.block.ftl.hotreload.v1.RunnerInfoRequest + (*Database)(nil), // 5: xyz.block.ftl.hotreload.v1.Database + (*RunnerInfoResponse)(nil), // 6: xyz.block.ftl.hotreload.v1.RunnerInfoResponse + (*ReloadNotRequired)(nil), // 7: xyz.block.ftl.hotreload.v1.ReloadNotRequired + (*ReloadSuccess)(nil), // 8: xyz.block.ftl.hotreload.v1.ReloadSuccess + (*ReloadFailed)(nil), // 9: xyz.block.ftl.hotreload.v1.ReloadFailed + (*v1.Module)(nil), // 10: xyz.block.ftl.schema.v1.Module + (*v11.ErrorList)(nil), // 11: xyz.block.ftl.language.v1.ErrorList + (*v12.PingRequest)(nil), // 12: xyz.block.ftl.v1.PingRequest + (*v12.PingResponse)(nil), // 13: xyz.block.ftl.v1.PingResponse +} +var file_xyz_block_ftl_hotreload_v1_hotreload_proto_depIdxs = []int32{ + 7, // 0: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_not_required:type_name -> xyz.block.ftl.hotreload.v1.ReloadNotRequired + 8, // 1: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess + 9, // 2: xyz.block.ftl.hotreload.v1.ReloadResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed + 8, // 3: xyz.block.ftl.hotreload.v1.WatchResponse.reload_success:type_name -> xyz.block.ftl.hotreload.v1.ReloadSuccess + 9, // 4: xyz.block.ftl.hotreload.v1.WatchResponse.reload_failed:type_name -> xyz.block.ftl.hotreload.v1.ReloadFailed + 5, // 5: xyz.block.ftl.hotreload.v1.RunnerInfoRequest.databases:type_name -> xyz.block.ftl.hotreload.v1.Database + 10, // 6: xyz.block.ftl.hotreload.v1.ReloadSuccess.module:type_name -> xyz.block.ftl.schema.v1.Module + 11, // 7: xyz.block.ftl.hotreload.v1.ReloadSuccess.errors:type_name -> xyz.block.ftl.language.v1.ErrorList + 11, // 8: xyz.block.ftl.hotreload.v1.ReloadFailed.errors:type_name -> xyz.block.ftl.language.v1.ErrorList + 12, // 9: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:input_type -> xyz.block.ftl.v1.PingRequest + 0, // 10: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:input_type -> xyz.block.ftl.hotreload.v1.ReloadRequest + 2, // 11: xyz.block.ftl.hotreload.v1.HotReloadService.Watch:input_type -> xyz.block.ftl.hotreload.v1.WatchRequest + 4, // 12: xyz.block.ftl.hotreload.v1.HotReloadService.RunnerInfo:input_type -> xyz.block.ftl.hotreload.v1.RunnerInfoRequest + 13, // 13: xyz.block.ftl.hotreload.v1.HotReloadService.Ping:output_type -> xyz.block.ftl.v1.PingResponse + 1, // 14: xyz.block.ftl.hotreload.v1.HotReloadService.Reload:output_type -> xyz.block.ftl.hotreload.v1.ReloadResponse + 3, // 15: xyz.block.ftl.hotreload.v1.HotReloadService.Watch:output_type -> xyz.block.ftl.hotreload.v1.WatchResponse + 6, // 16: xyz.block.ftl.hotreload.v1.HotReloadService.RunnerInfo:output_type -> xyz.block.ftl.hotreload.v1.RunnerInfoResponse + 13, // [13:17] is the sub-list for method output_type + 9, // [9:13] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name +} + +func init() { file_xyz_block_ftl_hotreload_v1_hotreload_proto_init() } +func file_xyz_block_ftl_hotreload_v1_hotreload_proto_init() { + if File_xyz_block_ftl_hotreload_v1_hotreload_proto != nil { + return + } + file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[1].OneofWrappers = []any{ + (*ReloadResponse_ReloadNotRequired)(nil), + (*ReloadResponse_ReloadSuccess)(nil), + (*ReloadResponse_ReloadFailed)(nil), + } + file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes[3].OneofWrappers = []any{ + (*WatchResponse_ReloadSuccess)(nil), + (*WatchResponse_ReloadFailed)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc, + NumEnums: 0, + NumMessages: 10, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_xyz_block_ftl_hotreload_v1_hotreload_proto_goTypes, + DependencyIndexes: file_xyz_block_ftl_hotreload_v1_hotreload_proto_depIdxs, + MessageInfos: file_xyz_block_ftl_hotreload_v1_hotreload_proto_msgTypes, + }.Build() + File_xyz_block_ftl_hotreload_v1_hotreload_proto = out.File + file_xyz_block_ftl_hotreload_v1_hotreload_proto_rawDesc = nil + file_xyz_block_ftl_hotreload_v1_hotreload_proto_goTypes = nil + file_xyz_block_ftl_hotreload_v1_hotreload_proto_depIdxs = nil +} diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto new file mode 100644 index 0000000000..4e346ea556 --- /dev/null +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreload.proto @@ -0,0 +1,77 @@ +syntax = "proto3"; + +package xyz.block.ftl.hotreload.v1; + +import "xyz/block/ftl/language/v1/language.proto"; +import "xyz/block/ftl/schema/v1/schema.proto"; +import "xyz/block/ftl/v1/ftl.proto"; + +option go_package = "github.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1;hotreloadpb"; +option java_multiple_files = true; + +message ReloadRequest { + bool force = 1; +} + +message ReloadResponse { + oneof event { + ReloadNotRequired reload_not_required = 1; + ReloadSuccess reload_success = 2; + ReloadFailed reload_failed = 3; + } +} + +message WatchRequest {} + +message WatchResponse { + oneof event { + ReloadSuccess reload_success = 1; + ReloadFailed reload_failed = 2; + } +} + +message RunnerInfoRequest { + string address = 1; + string deployment = 2; + repeated Database databases = 3; +} + +message Database { + string name = 1; + string address = 2; +} + +message RunnerInfoResponse {} + +message ReloadNotRequired {} + +message ReloadSuccess { + // Module schema for the built module + ftl.schema.v1.Module module = 1; + // Module schema for the built module + ftl.language.v1.ErrorList errors = 2; +} + +message ReloadFailed { + // Module schema for the built module + ftl.language.v1.ErrorList errors = 1; +} + +// HotReloadService is for communication between a language plugin a language runtime that can perform a hot reload +service HotReloadService { + // Ping service for readiness. + rpc Ping(xyz.block.ftl.v1.PingRequest) returns (xyz.block.ftl.v1.PingResponse) { + option idempotency_level = NO_SIDE_EFFECTS; + } + + // Forces an explicit Reload from the plugin. This is useful for when the plugin needs to trigger a Reload, + // such as when the Reload context changes. + // + rpc Reload(ReloadRequest) returns (ReloadResponse); + + // Watch for hot reloads not initiated by an explicit Reload call. + rpc Watch(WatchRequest) returns (stream WatchResponse); + + // Invoked by the runner to provide runner information to the plugin. + rpc RunnerInfo(RunnerInfoRequest) returns (RunnerInfoResponse); +} diff --git a/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go b/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go new file mode 100644 index 0000000000..667b1795ad --- /dev/null +++ b/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect/hotreload.connect.go @@ -0,0 +1,197 @@ +// Code generated by protoc-gen-connect-go. DO NOT EDIT. +// +// Source: xyz/block/ftl/hotreload/v1/hotreload.proto + +package hotreloadpbconnect + +import ( + connect "connectrpc.com/connect" + context "context" + errors "errors" + v11 "github.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1" + v1 "github.com/block/ftl/backend/protos/xyz/block/ftl/v1" + http "net/http" + strings "strings" +) + +// This is a compile-time assertion to ensure that this generated file and the connect package are +// compatible. If you get a compiler error that this constant is not defined, this code was +// generated with a version of connect newer than the one compiled into your binary. You can fix the +// problem by either regenerating this code with an older version of connect or updating the connect +// version compiled into your binary. +const _ = connect.IsAtLeastVersion1_7_0 + +const ( + // HotReloadServiceName is the fully-qualified name of the HotReloadService service. + HotReloadServiceName = "xyz.block.ftl.hotreload.v1.HotReloadService" +) + +// These constants are the fully-qualified names of the RPCs defined in this package. They're +// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. +// +// Note that these are different from the fully-qualified method names used by +// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to +// reflection-formatted method names, remove the leading slash and convert the remaining slash to a +// period. +const ( + // HotReloadServicePingProcedure is the fully-qualified name of the HotReloadService's Ping RPC. + HotReloadServicePingProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/Ping" + // HotReloadServiceReloadProcedure is the fully-qualified name of the HotReloadService's Reload RPC. + HotReloadServiceReloadProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/Reload" + // HotReloadServiceWatchProcedure is the fully-qualified name of the HotReloadService's Watch RPC. + HotReloadServiceWatchProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/Watch" + // HotReloadServiceRunnerInfoProcedure is the fully-qualified name of the HotReloadService's + // RunnerInfo RPC. + HotReloadServiceRunnerInfoProcedure = "/xyz.block.ftl.hotreload.v1.HotReloadService/RunnerInfo" +) + +// HotReloadServiceClient is a client for the xyz.block.ftl.hotreload.v1.HotReloadService service. +type HotReloadServiceClient interface { + // Ping service for readiness. + Ping(context.Context, *connect.Request[v1.PingRequest]) (*connect.Response[v1.PingResponse], error) + // Forces an explicit Reload from the plugin. This is useful for when the plugin needs to trigger a Reload, + // such as when the Reload context changes. + Reload(context.Context, *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) + // Watch for hot reloads not initiated by an explicit Reload call. + Watch(context.Context, *connect.Request[v11.WatchRequest]) (*connect.ServerStreamForClient[v11.WatchResponse], error) + // Invoked by the runner to provide runner information to the plugin. + RunnerInfo(context.Context, *connect.Request[v11.RunnerInfoRequest]) (*connect.Response[v11.RunnerInfoResponse], error) +} + +// NewHotReloadServiceClient constructs a client for the xyz.block.ftl.hotreload.v1.HotReloadService +// service. By default, it uses the Connect protocol with the binary Protobuf Codec, asks for +// gzipped responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply +// the connect.WithGRPC() or connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewHotReloadServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) HotReloadServiceClient { + baseURL = strings.TrimRight(baseURL, "/") + return &hotReloadServiceClient{ + ping: connect.NewClient[v1.PingRequest, v1.PingResponse]( + httpClient, + baseURL+HotReloadServicePingProcedure, + connect.WithIdempotency(connect.IdempotencyNoSideEffects), + connect.WithClientOptions(opts...), + ), + reload: connect.NewClient[v11.ReloadRequest, v11.ReloadResponse]( + httpClient, + baseURL+HotReloadServiceReloadProcedure, + opts..., + ), + watch: connect.NewClient[v11.WatchRequest, v11.WatchResponse]( + httpClient, + baseURL+HotReloadServiceWatchProcedure, + opts..., + ), + runnerInfo: connect.NewClient[v11.RunnerInfoRequest, v11.RunnerInfoResponse]( + httpClient, + baseURL+HotReloadServiceRunnerInfoProcedure, + opts..., + ), + } +} + +// hotReloadServiceClient implements HotReloadServiceClient. +type hotReloadServiceClient struct { + ping *connect.Client[v1.PingRequest, v1.PingResponse] + reload *connect.Client[v11.ReloadRequest, v11.ReloadResponse] + watch *connect.Client[v11.WatchRequest, v11.WatchResponse] + runnerInfo *connect.Client[v11.RunnerInfoRequest, v11.RunnerInfoResponse] +} + +// Ping calls xyz.block.ftl.hotreload.v1.HotReloadService.Ping. +func (c *hotReloadServiceClient) Ping(ctx context.Context, req *connect.Request[v1.PingRequest]) (*connect.Response[v1.PingResponse], error) { + return c.ping.CallUnary(ctx, req) +} + +// Reload calls xyz.block.ftl.hotreload.v1.HotReloadService.Reload. +func (c *hotReloadServiceClient) Reload(ctx context.Context, req *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) { + return c.reload.CallUnary(ctx, req) +} + +// Watch calls xyz.block.ftl.hotreload.v1.HotReloadService.Watch. +func (c *hotReloadServiceClient) Watch(ctx context.Context, req *connect.Request[v11.WatchRequest]) (*connect.ServerStreamForClient[v11.WatchResponse], error) { + return c.watch.CallServerStream(ctx, req) +} + +// RunnerInfo calls xyz.block.ftl.hotreload.v1.HotReloadService.RunnerInfo. +func (c *hotReloadServiceClient) RunnerInfo(ctx context.Context, req *connect.Request[v11.RunnerInfoRequest]) (*connect.Response[v11.RunnerInfoResponse], error) { + return c.runnerInfo.CallUnary(ctx, req) +} + +// HotReloadServiceHandler is an implementation of the xyz.block.ftl.hotreload.v1.HotReloadService +// service. +type HotReloadServiceHandler interface { + // Ping service for readiness. + Ping(context.Context, *connect.Request[v1.PingRequest]) (*connect.Response[v1.PingResponse], error) + // Forces an explicit Reload from the plugin. This is useful for when the plugin needs to trigger a Reload, + // such as when the Reload context changes. + Reload(context.Context, *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) + // Watch for hot reloads not initiated by an explicit Reload call. + Watch(context.Context, *connect.Request[v11.WatchRequest], *connect.ServerStream[v11.WatchResponse]) error + // Invoked by the runner to provide runner information to the plugin. + RunnerInfo(context.Context, *connect.Request[v11.RunnerInfoRequest]) (*connect.Response[v11.RunnerInfoResponse], error) +} + +// NewHotReloadServiceHandler builds an HTTP handler from the service implementation. It returns the +// path on which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewHotReloadServiceHandler(svc HotReloadServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { + hotReloadServicePingHandler := connect.NewUnaryHandler( + HotReloadServicePingProcedure, + svc.Ping, + connect.WithIdempotency(connect.IdempotencyNoSideEffects), + connect.WithHandlerOptions(opts...), + ) + hotReloadServiceReloadHandler := connect.NewUnaryHandler( + HotReloadServiceReloadProcedure, + svc.Reload, + opts..., + ) + hotReloadServiceWatchHandler := connect.NewServerStreamHandler( + HotReloadServiceWatchProcedure, + svc.Watch, + opts..., + ) + hotReloadServiceRunnerInfoHandler := connect.NewUnaryHandler( + HotReloadServiceRunnerInfoProcedure, + svc.RunnerInfo, + opts..., + ) + return "/xyz.block.ftl.hotreload.v1.HotReloadService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case HotReloadServicePingProcedure: + hotReloadServicePingHandler.ServeHTTP(w, r) + case HotReloadServiceReloadProcedure: + hotReloadServiceReloadHandler.ServeHTTP(w, r) + case HotReloadServiceWatchProcedure: + hotReloadServiceWatchHandler.ServeHTTP(w, r) + case HotReloadServiceRunnerInfoProcedure: + hotReloadServiceRunnerInfoHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedHotReloadServiceHandler returns CodeUnimplemented from all methods. +type UnimplementedHotReloadServiceHandler struct{} + +func (UnimplementedHotReloadServiceHandler) Ping(context.Context, *connect.Request[v1.PingRequest]) (*connect.Response[v1.PingResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.hotreload.v1.HotReloadService.Ping is not implemented")) +} + +func (UnimplementedHotReloadServiceHandler) Reload(context.Context, *connect.Request[v11.ReloadRequest]) (*connect.Response[v11.ReloadResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.hotreload.v1.HotReloadService.Reload is not implemented")) +} + +func (UnimplementedHotReloadServiceHandler) Watch(context.Context, *connect.Request[v11.WatchRequest], *connect.ServerStream[v11.WatchResponse]) error { + return connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.hotreload.v1.HotReloadService.Watch is not implemented")) +} + +func (UnimplementedHotReloadServiceHandler) RunnerInfo(context.Context, *connect.Request[v11.RunnerInfoRequest]) (*connect.Response[v11.RunnerInfoResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("xyz.block.ftl.hotreload.v1.HotReloadService.RunnerInfo is not implemented")) +} diff --git a/backend/protos/xyz/block/ftl/language/v1/language.pb.go b/backend/protos/xyz/block/ftl/language/v1/language.pb.go index 745bbf2765..e6ea1d5e23 100644 --- a/backend/protos/xyz/block/ftl/language/v1/language.pb.go +++ b/backend/protos/xyz/block/ftl/language/v1/language.pb.go @@ -1271,10 +1271,10 @@ type BuildSuccess struct { DevEndpoint *string `protobuf:"bytes,7,opt,name=dev_endpoint,json=devEndpoint,proto3,oneof" json:"dev_endpoint,omitempty"` // Dev mode debug port DebugPort *int32 `protobuf:"varint,8,opt,name=debug_port,json=debugPort,proto3,oneof" json:"debug_port,omitempty"` - // Dev mode runner info file, this file is used to allow the runner to communicate provisioner info back to the plugin - DevRunnerInfoFile *string `protobuf:"bytes,9,opt,name=dev_runner_info_file,json=devRunnerInfoFile,proto3,oneof" json:"dev_runner_info_file,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // Dev mode hot reload endpoint, this is used to allow the runner to communicate info back to the running process + DevHotReloadEndpoint *string `protobuf:"bytes,9,opt,name=dev_hot_reload_endpoint,json=devHotReloadEndpoint,proto3,oneof" json:"dev_hot_reload_endpoint,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *BuildSuccess) Reset() { @@ -1363,9 +1363,9 @@ func (x *BuildSuccess) GetDebugPort() int32 { return 0 } -func (x *BuildSuccess) GetDevRunnerInfoFile() string { - if x != nil && x.DevRunnerInfoFile != nil { - return *x.DevRunnerInfoFile +func (x *BuildSuccess) GetDevHotReloadEndpoint() string { + if x != nil && x.DevHotReloadEndpoint != nil { + return *x.DevHotReloadEndpoint } return "" } @@ -2047,7 +2047,7 @@ var file_xyz_block_ftl_language_v1_language_proto_rawDesc = []byte{ 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, - 0x64, 0x22, 0xcc, 0x03, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, + 0x64, 0x22, 0xd5, 0x03, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, @@ -2069,154 +2069,155 @@ var file_xyz_block_ftl_language_v1_language_proto_rawDesc = []byte{ 0x65, 0x76, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0a, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x09, 0x64, 0x65, 0x62, 0x75, 0x67, 0x50, 0x6f, 0x72, 0x74, 0x88, 0x01, - 0x01, 0x12, 0x34, 0x0a, 0x14, 0x64, 0x65, 0x76, 0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x5f, - 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x48, - 0x02, 0x52, 0x11, 0x64, 0x65, 0x76, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x6e, 0x66, 0x6f, - 0x46, 0x69, 0x6c, 0x65, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x64, 0x65, 0x76, 0x5f, - 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x0d, 0x0a, 0x0b, 0x5f, 0x64, 0x65, 0x62, - 0x75, 0x67, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x64, 0x65, 0x76, 0x5f, - 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x66, 0x69, 0x6c, 0x65, - 0x22, 0xd6, 0x01, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, - 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, - 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, - 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, - 0x69, 0x73, 0x41, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x52, 0x65, 0x62, 0x75, 0x69, - 0x6c, 0x64, 0x12, 0x3c, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, - 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, - 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, - 0x12, 0x37, 0x0a, 0x17, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x64, - 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x16, 0x69, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, - 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x22, 0x9b, 0x02, 0x0a, 0x0d, 0x42, 0x75, - 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, 0x61, - 0x75, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, - 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, - 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x48, 0x00, 0x52, 0x12, 0x61, 0x75, 0x74, 0x6f, - 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4e, - 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, - 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x4e, - 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, - 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x48, 0x00, - 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x42, 0x07, - 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0xa8, 0x02, 0x0a, 0x14, 0x47, 0x65, 0x6e, 0x65, - 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x64, 0x69, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, - 0x69, 0x72, 0x12, 0x37, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, - 0x74, 0x6c, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, - 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, - 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, - 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5e, 0x0a, 0x14, 0x6e, 0x61, 0x74, - 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, - 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x48, 0x00, 0x52, 0x12, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x88, 0x01, 0x01, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x6e, 0x61, - 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x22, 0x17, 0x0a, 0x15, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, - 0x75, 0x62, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdb, 0x01, 0x0a, 0x19, - 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, - 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x01, 0x12, 0x3a, 0x0a, 0x17, 0x64, 0x65, 0x76, 0x5f, 0x68, 0x6f, 0x74, 0x5f, 0x72, 0x65, 0x6c, + 0x6f, 0x61, 0x64, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x09, 0x48, 0x02, 0x52, 0x14, 0x64, 0x65, 0x76, 0x48, 0x6f, 0x74, 0x52, 0x65, 0x6c, 0x6f, + 0x61, 0x64, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, + 0x0d, 0x5f, 0x64, 0x65, 0x76, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x42, 0x0d, + 0x0a, 0x0b, 0x5f, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x42, 0x1a, 0x0a, + 0x18, 0x5f, 0x64, 0x65, 0x76, 0x5f, 0x68, 0x6f, 0x74, 0x5f, 0x72, 0x65, 0x6c, 0x6f, 0x61, 0x64, + 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x22, 0xd6, 0x01, 0x0a, 0x0c, 0x42, 0x75, + 0x69, 0x6c, 0x64, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x73, 0x5f, + 0x61, 0x75, 0x74, 0x6f, 0x6d, 0x61, 0x74, 0x69, 0x63, 0x5f, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, + 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x12, 0x69, 0x73, 0x41, 0x75, 0x74, 0x6f, 0x6d, + 0x61, 0x74, 0x69, 0x63, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x3c, 0x0a, 0x06, 0x65, + 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, + 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x4c, 0x69, 0x73, + 0x74, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x12, 0x37, 0x0a, 0x17, 0x69, 0x6e, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, + 0x63, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x16, 0x69, 0x6e, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, + 0x65, 0x73, 0x22, 0x9b, 0x02, 0x0a, 0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x61, 0x0a, 0x14, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x62, + 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, + 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x41, + 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, + 0x64, 0x48, 0x00, 0x52, 0x12, 0x61, 0x75, 0x74, 0x6f, 0x52, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4e, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x5f, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, + 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x4e, 0x0a, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x5f, 0x66, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, + 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, + 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, + 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x22, 0xa8, 0x02, 0x0a, 0x14, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, + 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x64, 0x69, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x64, 0x69, 0x72, 0x12, 0x37, 0x0a, 0x06, 0x6d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, + 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x12, 0x5e, 0x0a, 0x14, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, - 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x75, 0x62, 0x73, - 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x75, - 0x62, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, - 0x12, 0x37, 0x0a, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, - 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x79, 0x6e, - 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xb9, 0x08, 0x0a, 0x0f, 0x4c, 0x61, 0x6e, 0x67, - 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, - 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, - 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, - 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, 0x12, 0x87, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, - 0x12, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, - 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x12, 0x6e, 0x61, 0x74, + 0x69, 0x76, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x88, + 0x01, 0x01, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x17, 0x0a, 0x15, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xdb, 0x01, 0x0a, 0x19, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, + 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x4c, 0x0a, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, + 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x75, 0x62, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x75, 0x62, 0x73, 0x52, 0x6f, 0x6f, 0x74, 0x12, + 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x37, 0x0a, 0x06, 0x73, 0x63, 0x68, + 0x65, 0x6d, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x06, 0x73, 0x63, 0x68, 0x65, + 0x6d, 0x61, 0x22, 0x1c, 0x0a, 0x1a, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, + 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x32, 0xb9, 0x08, 0x0a, 0x0f, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1d, 0x2e, 0x78, + 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x78, 0x79, + 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x50, + 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x03, 0x90, 0x02, 0x01, + 0x12, 0x87, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x12, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, + 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x37, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, + 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, + 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, + 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x0c, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x2e, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, + 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x78, 0x79, 0x7a, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, + 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x87, 0x01, 0x0a, 0x14, + 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, + 0x75, 0x6c, 0x74, 0x73, 0x12, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, + 0x61, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, + 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, + 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x78, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, + 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x46, 0x6c, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x6f, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, - 0x65, 0x12, 0x2e, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, - 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, - 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x87, 0x01, 0x0a, 0x14, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x36, 0x2e, 0x78, 0x79, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, + 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, - 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, - 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x65, 0x66, 0x61, - 0x75, 0x6c, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x78, 0x0a, 0x0f, - 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, - 0x31, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, - 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, - 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5c, 0x0a, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, - 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, - 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, - 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, + 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x65, 0x70, 0x65, 0x6e, + 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x5c, 0x0a, 0x05, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x27, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, - 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x30, 0x01, 0x12, 0x84, 0x01, 0x0a, 0x13, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x35, 0x2e, 0x78, + 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x28, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, + 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, + 0x69, 0x6c, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x84, 0x01, + 0x0a, 0x13, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x35, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, - 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x72, 0x0a, 0x0d, 0x47, - 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x12, 0x2f, 0x2e, 0x78, - 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, - 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, - 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, - 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, - 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, - 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x81, 0x01, 0x0a, 0x12, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, - 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x34, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x72, 0x0a, 0x0d, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x53, 0x74, 0x75, 0x62, 0x73, 0x12, 0x2f, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, - 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x78, - 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, - 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, - 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x42, 0x4c, 0x50, 0x01, 0x5a, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, - 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, - 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x75, - 0x61, 0x67, 0x65, 0x2f, 0x76, 0x31, 0x3b, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x70, - 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x74, 0x75, 0x62, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x81, 0x01, 0x0a, 0x12, 0x53, 0x79, 0x6e, + 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x12, + 0x34, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, + 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x78, 0x79, 0x7a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x2e, 0x66, 0x74, 0x6c, 0x2e, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2e, 0x76, + 0x31, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x53, 0x74, 0x75, 0x62, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x63, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x4c, 0x50, 0x01, + 0x5a, 0x48, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x2f, 0x66, 0x74, 0x6c, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x78, 0x79, 0x7a, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x2f, + 0x66, 0x74, 0x6c, 0x2f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x2f, 0x76, 0x31, 0x3b, + 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/backend/protos/xyz/block/ftl/language/v1/language.proto b/backend/protos/xyz/block/ftl/language/v1/language.proto index 62a49bc068..6d4dc932f4 100644 --- a/backend/protos/xyz/block/ftl/language/v1/language.proto +++ b/backend/protos/xyz/block/ftl/language/v1/language.proto @@ -237,8 +237,8 @@ message BuildSuccess { // Dev mode debug port optional int32 debug_port = 8; - // Dev mode runner info file, this file is used to allow the runner to communicate provisioner info back to the plugin - optional string dev_runner_info_file = 9; + // Dev mode hot reload endpoint, this is used to allow the runner to communicate info back to the running process + optional string dev_hot_reload_endpoint = 9; } // BuildFailure should be sent when a build fails. diff --git a/backend/provisioner/scaling/localscaling/local_scaling.go b/backend/provisioner/scaling/localscaling/local_scaling.go index e393d417ad..80de96b63e 100644 --- a/backend/provisioner/scaling/localscaling/local_scaling.go +++ b/backend/provisioner/scaling/localscaling/local_scaling.go @@ -100,11 +100,11 @@ func (l *localScaling) TerminatePreviousDeployments(ctx context.Context, module } type devModeRunner struct { - uri url.URL + uri string + hotReloadURI string // The deployment key of the deployment that is currently running - deploymentKey optional.Option[key.Deployment] - debugPort int - runnerInfoFile optional.Option[string] + deploymentKey optional.Option[key.Deployment] + debugPort int } func (l *localScaling) Start(ctx context.Context) error { @@ -122,9 +122,9 @@ func (l *localScaling) Start(ctx context.Context) error { // Must be called under lock func (l *localScaling) updateDevModeEndpoint(ctx context.Context, devEndpoints dev.LocalEndpoint) { l.devModeEndpoints[devEndpoints.Module] = &devModeRunner{ - uri: devEndpoints.Endpoint, - debugPort: devEndpoints.DebugPort, - runnerInfoFile: devEndpoints.RunnerInfoFile, + uri: devEndpoints.Endpoint, + debugPort: devEndpoints.DebugPort, + hotReloadURI: devEndpoints.HotReloadEndpoint, } if ide, ok := l.ideSupport.Get(); ok { if devEndpoints.DebugPort != 0 { @@ -255,12 +255,12 @@ func (l *localScaling) startRunner(ctx context.Context, deploymentKey key.Deploy } devEndpoint := l.devModeEndpoints[info.module] - devURI := optional.None[url.URL]() - devRunnerInfoFile := optional.None[string]() + devURI := optional.None[string]() + devHotReloadURI := optional.None[string]() debugPort := 0 if devEndpoint != nil { devURI = optional.Some(devEndpoint.uri) - devRunnerInfoFile = devEndpoint.runnerInfoFile + devHotReloadURI = optional.Some(devEndpoint.hotReloadURI) if devKey, ok := devEndpoint.deploymentKey.Get(); ok && devKey.Equal(deploymentKey) { // Already running, don't start another return nil @@ -297,14 +297,14 @@ func (l *localScaling) startRunner(ctx context.Context, deploymentKey key.Deploy return fmt.Errorf("failed to start runner: %w", err) } config := runner.Config{ - Bind: bindURL, - ControllerEndpoint: controllerEndpoint, - LeaseEndpoint: l.leaseAddress, - Key: key.NewLocalRunnerKey(keySuffix), - Deployment: deploymentKey, - DebugPort: debugPort, - DevEndpoint: devURI, - DevRunnerInfoFile: devRunnerInfoFile, + Bind: bindURL, + ControllerEndpoint: controllerEndpoint, + LeaseEndpoint: l.leaseAddress, + Key: key.NewLocalRunnerKey(keySuffix), + Deployment: deploymentKey, + DebugPort: debugPort, + DevEndpoint: devURI, + DevHotReloadEndpoint: devHotReloadURI, } simpleName := fmt.Sprintf("runner%d", keySuffix) diff --git a/backend/runner/runner.go b/backend/runner/runner.go index aabfb775f9..30396ffcf8 100644 --- a/backend/runner/runner.go +++ b/backend/runner/runner.go @@ -31,6 +31,8 @@ import ( mysql "github.com/block/ftl-mysql-auth-proxy" "github.com/block/ftl/backend/controller/artefacts" ftldeploymentconnect "github.com/block/ftl/backend/protos/xyz/block/ftl/deployment/v1/deploymentpbconnect" + hotreloadpb "github.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1" + "github.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect" ftlleaseconnect "github.com/block/ftl/backend/protos/xyz/block/ftl/lease/v1/leasepbconnect" "github.com/block/ftl/backend/protos/xyz/block/ftl/pubsub/v1/pubsubpbconnect" "github.com/block/ftl/backend/protos/xyz/block/ftl/query/v1/querypbconnect" @@ -57,22 +59,22 @@ import ( ) type Config struct { - Config []string `name:"config" short:"C" help:"Paths to FTL project configuration files." env:"FTL_CONFIG" placeholder:"FILE[,FILE,...]" type:"existingfile"` - Bind *url.URL `help:"Endpoint the Runner should bind to and advertise." default:"http://127.0.0.1:8892" env:"FTL_BIND"` - Key key.Runner `help:"Runner key (auto)."` - ControllerEndpoint *url.URL `name:"ftl-endpoint" help:"Controller endpoint." env:"FTL_ENDPOINT" default:"http://127.0.0.1:8892"` - LeaseEndpoint *url.URL `name:"ftl-lease-endpoint" help:"Lease endpoint endpoint." env:"FTL_LEASE_ENDPOINT" default:"http://127.0.0.1:8895"` - QueryEndpoint *url.URL `name:"ftl-query-endpoint" help:"Query endpoint." env:"FTL_QUERY_ENDPOINT" default:"http://127.0.0.1:8897"` - TimelineEndpoint *url.URL `help:"Timeline endpoint." env:"FTL_TIMELINE_ENDPOINT" default:"http://127.0.0.1:8894"` - TemplateDir string `help:"Template directory to copy into each deployment, if any." type:"existingdir"` - DeploymentDir string `help:"Directory to store deployments in." default:"${deploymentdir}"` - DeploymentKeepHistory int `help:"Number of deployments to keep history for." default:"3"` - HeartbeatPeriod time.Duration `help:"Minimum period between heartbeats." default:"3s"` - HeartbeatJitter time.Duration `help:"Jitter to add to heartbeat period." default:"2s"` - Deployment key.Deployment `help:"The deployment this runner is for." env:"FTL_DEPLOYMENT"` - DebugPort int `help:"The port to use for debugging." env:"FTL_DEBUG_PORT"` - DevEndpoint optional.Option[url.URL] `help:"An existing endpoint to connect to in development mode" hidden:""` - DevRunnerInfoFile optional.Option[string] `help:"The path to a file that we write dev endpoint information to." hidden:""` + Config []string `name:"config" short:"C" help:"Paths to FTL project configuration files." env:"FTL_CONFIG" placeholder:"FILE[,FILE,...]" type:"existingfile"` + Bind *url.URL `help:"Endpoint the Runner should bind to and advertise." default:"http://127.0.0.1:8892" env:"FTL_BIND"` + Key key.Runner `help:"Runner key (auto)."` + ControllerEndpoint *url.URL `name:"ftl-endpoint" help:"Controller endpoint." env:"FTL_ENDPOINT" default:"http://127.0.0.1:8892"` + LeaseEndpoint *url.URL `name:"ftl-lease-endpoint" help:"Lease endpoint endpoint." env:"FTL_LEASE_ENDPOINT" default:"http://127.0.0.1:8895"` + QueryEndpoint *url.URL `name:"ftl-query-endpoint" help:"Query endpoint." env:"FTL_QUERY_ENDPOINT" default:"http://127.0.0.1:8897"` + TimelineEndpoint *url.URL `help:"Timeline endpoint." env:"FTL_TIMELINE_ENDPOINT" default:"http://127.0.0.1:8894"` + TemplateDir string `help:"Template directory to copy into each deployment, if any." type:"existingdir"` + DeploymentDir string `help:"Directory to store deployments in." default:"${deploymentdir}"` + DeploymentKeepHistory int `help:"Number of deployments to keep history for." default:"3"` + HeartbeatPeriod time.Duration `help:"Minimum period between heartbeats." default:"3s"` + HeartbeatJitter time.Duration `help:"Jitter to add to heartbeat period." default:"2s"` + Deployment key.Deployment `help:"The deployment this runner is for." env:"FTL_DEPLOYMENT"` + DebugPort int `help:"The port to use for debugging." env:"FTL_DEBUG_PORT"` + DevEndpoint optional.Option[string] `help:"An existing endpoint to connect to in development mode" hidden:""` + DevHotReloadEndpoint optional.Option[string] `help:"The gRPC enpoint to send runner into to for hot reload." hidden:""` } func Start(ctx context.Context, config Config, storage *artefacts.OCIArtefactService) error { @@ -119,17 +121,17 @@ func Start(ctx context.Context, config Config, storage *artefacts.OCIArtefactSer timelineClient := timeline.NewClient(ctx, config.TimelineEndpoint) svc := &Service{ - key: runnerKey, - config: config, - storage: storage, - controllerClient: controllerClient, - timelineClient: timelineClient, - labels: labels, - deploymentLogQueue: make(chan log.Entry, 10000), - cancelFunc: doneFunc, - devEndpoint: config.DevEndpoint, - devRunnerInfoFile: config.DevRunnerInfoFile, - queryServices: query.NewMultiService(), + key: runnerKey, + config: config, + storage: storage, + controllerClient: controllerClient, + timelineClient: timelineClient, + labels: labels, + deploymentLogQueue: make(chan log.Entry, 10000), + cancelFunc: doneFunc, + devEndpoint: config.DevEndpoint, + devHotReloadEndpoint: config.DevHotReloadEndpoint, + queryServices: query.NewMultiService(), } module, err := svc.getModule(ctx, config.Deployment) @@ -245,10 +247,11 @@ var _ ftlv1connect.VerbServiceHandler = (*Service)(nil) type deployment struct { key key.Deployment // Cancelled when plugin terminates - ctx context.Context - cmd optional.Option[exec.Cmd] - endpoint *url.URL // The endpoint the plugin is listening on. - client ftlv1connect.VerbServiceClient + ctx context.Context + cmd optional.Option[exec.Cmd] + endpoint string // The endpoint the plugin is listening on. + client ftlv1connect.VerbServiceClient + reverseProxy *httputil.ReverseProxy } type Service struct { @@ -262,16 +265,16 @@ type Service struct { controllerClient ftlv1connect.ControllerServiceClient timelineClient *timeline.Client // Failed to register with the Controller - registrationFailure atomic.Value[optional.Option[error]] - labels *structpb.Struct - deploymentLogQueue chan log.Entry - cancelFunc func() - devEndpoint optional.Option[url.URL] - devRunnerInfoFile optional.Option[string] - proxy *proxy.Service - pubSub *pubsub.Service - queryServices *query.MultiService - proxyBindAddress *url.URL + registrationFailure atomic.Value[optional.Option[error]] + labels *structpb.Struct + deploymentLogQueue chan log.Entry + cancelFunc func() + devEndpoint optional.Option[string] + devHotReloadEndpoint optional.Option[string] + proxy *proxy.Service + pubSub *pubsub.Service + queryServices *query.MultiService + proxyBindAddress *url.URL } func (s *Service) Call(ctx context.Context, req *connect.Request[ftlv1.CallRequest]) (*connect.Response[ftlv1.CallResponse], error) { @@ -387,27 +390,38 @@ func (s *Service) deploy(ctx context.Context, key key.Deployment, module *schema var dep *deployment if ep, ok := s.devEndpoint.Get(); ok { - client := rpc.Dial(ftlv1connect.NewVerbServiceClient, ep.String(), log.Error) - dep = &deployment{ - ctx: ctx, - key: key, - cmd: optional.None[exec.Cmd](), - endpoint: &ep, - client: client, - } - if file, ok := s.devRunnerInfoFile.Get(); ok { - fileContents := "proxy.bind.address=" + s.proxyBindAddress.String() - fileContents += fmt.Sprintf("\ndeployment=%s", s.config.Deployment.String()) + if hotRelaodEp, ok := s.devHotReloadEndpoint.Get(); ok { + hotReloadClient := rpc.Dial(hotreloadpbconnect.NewHotReloadServiceClient, hotRelaodEp, log.Error) + err = rpc.Wait(ctx, backoff.Backoff{}, time.Minute, hotReloadClient) + if err != nil { + return fmt.Errorf("failed to ping hot reload endpoint: %w", err) + } + var databases []*hotreloadpb.Database dbAddresses.Range(func(key string, value string) bool { - fileContents += fmt.Sprintf("\ndatabase.%s.url=%s", key, value) + databases = append(databases, &hotreloadpb.Database{ + Name: key, + Address: value, + }) return true }) - err = os.WriteFile(file, []byte(fileContents), 0660) // #nosec + _, err := hotReloadClient.RunnerInfo(ctx, connect.NewRequest(&hotreloadpb.RunnerInfoRequest{ + Deployment: s.config.Deployment.String(), + Address: s.proxyBindAddress.String(), + Databases: databases, + })) if err != nil { - logger.Errorf(err, "could not create FTL dev Config") + return fmt.Errorf("failed to send runner info: %w", err) } } + client := rpc.Dial(ftlv1connect.NewVerbServiceClient, ep, log.Error) err = rpc.Wait(ctx, backoff.Backoff{}, time.Minute, client) + dep = &deployment{ + ctx: ctx, + key: key, + cmd: optional.None[exec.Cmd](), + endpoint: ep, + client: client, + } if err != nil { observability.Deployment.Failure(ctx, optional.Some(key.String())) return fmt.Errorf("failed to ping dev endpoint: %w", err) @@ -510,18 +524,19 @@ func (s *Service) ServeHTTP(w http.ResponseWriter, r *http.Request) { http.Error(w, "no deployment", http.StatusNotFound) return } - proxy := httputil.NewSingleHostReverseProxy(deployment.endpoint) - proxy.ServeHTTP(w, r) + deployment.reverseProxy.ServeHTTP(w, r) } func (s *Service) makeDeployment(ctx context.Context, key key.Deployment, plugin *plugin.Plugin[ftlv1connect.VerbServiceClient, ftlv1.PingRequest, ftlv1.PingResponse, *ftlv1.PingResponse]) *deployment { + proxy := httputil.NewSingleHostReverseProxy(plugin.Endpoint) return &deployment{ - ctx: ctx, - key: key, - cmd: optional.Ptr(plugin.Cmd), - endpoint: plugin.Endpoint, - client: plugin.Client, + ctx: ctx, + key: key, + cmd: optional.Ptr(plugin.Cmd), + endpoint: plugin.Endpoint.String(), + client: plugin.Client, + reverseProxy: proxy, } } diff --git a/common/schema/schema.go b/common/schema/schema.go index 14f2fe3e76..706e4ad360 100644 --- a/common/schema/schema.go +++ b/common/schema/schema.go @@ -210,6 +210,9 @@ func TypeName(v any) string { // FromProto converts a protobuf Schema to a Schema and validates it. func FromProto(s *schemapb.Schema) (*Schema, error) { + if s == nil { + return &Schema{}, nil + } modules, err := moduleListToSchema(s.Modules) if err != nil { return nil, err diff --git a/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_connect.ts b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_connect.ts new file mode 100644 index 0000000000..06f01b7c0e --- /dev/null +++ b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_connect.ts @@ -0,0 +1,67 @@ +// @generated by protoc-gen-connect-es v1.6.1 with parameter "target=ts" +// @generated from file xyz/block/ftl/hotreload/v1/hotreload.proto (package xyz.block.ftl.hotreload.v1, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import { PingRequest, PingResponse } from "../../v1/ftl_pb.js"; +import { MethodIdempotency, MethodKind } from "@bufbuild/protobuf"; +import { ReloadRequest, ReloadResponse, RunnerInfoRequest, RunnerInfoResponse, WatchRequest, WatchResponse } from "./hotreload_pb.js"; + +/** + * HotReloadService is for communication between a language plugin a language runtime that can perform a hot reload + * + * @generated from service xyz.block.ftl.hotreload.v1.HotReloadService + */ +export const HotReloadService = { + typeName: "xyz.block.ftl.hotreload.v1.HotReloadService", + methods: { + /** + * Ping service for readiness. + * + * @generated from rpc xyz.block.ftl.hotreload.v1.HotReloadService.Ping + */ + ping: { + name: "Ping", + I: PingRequest, + O: PingResponse, + kind: MethodKind.Unary, + idempotency: MethodIdempotency.NoSideEffects, + }, + /** + * Forces an explicit Reload from the plugin. This is useful for when the plugin needs to trigger a Reload, + * such as when the Reload context changes. + * + * + * @generated from rpc xyz.block.ftl.hotreload.v1.HotReloadService.Reload + */ + reload: { + name: "Reload", + I: ReloadRequest, + O: ReloadResponse, + kind: MethodKind.Unary, + }, + /** + * Watch for hot reloads not initiated by an explicit Reload call. + * + * @generated from rpc xyz.block.ftl.hotreload.v1.HotReloadService.Watch + */ + watch: { + name: "Watch", + I: WatchRequest, + O: WatchResponse, + kind: MethodKind.ServerStreaming, + }, + /** + * Invoked by the runner to provide runner information to the plugin. + * + * @generated from rpc xyz.block.ftl.hotreload.v1.HotReloadService.RunnerInfo + */ + runnerInfo: { + name: "RunnerInfo", + I: RunnerInfoRequest, + O: RunnerInfoResponse, + kind: MethodKind.Unary, + }, + } +} as const; + diff --git a/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts new file mode 100644 index 0000000000..d3444022d4 --- /dev/null +++ b/frontend/console/src/protos/xyz/block/ftl/hotreload/v1/hotreload_pb.ts @@ -0,0 +1,425 @@ +// @generated by protoc-gen-es v1.10.0 with parameter "target=ts" +// @generated from file xyz/block/ftl/hotreload/v1/hotreload.proto (package xyz.block.ftl.hotreload.v1, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; +import { Message, proto3 } from "@bufbuild/protobuf"; +import { Module } from "../../schema/v1/schema_pb.js"; +import { ErrorList } from "../../language/v1/language_pb.js"; + +/** + * @generated from message xyz.block.ftl.hotreload.v1.ReloadRequest + */ +export class ReloadRequest extends Message { + /** + * @generated from field: bool force = 1; + */ + force = false; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.ReloadRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "force", kind: "scalar", T: 8 /* ScalarType.BOOL */ }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): ReloadRequest { + return new ReloadRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): ReloadRequest { + return new ReloadRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): ReloadRequest { + return new ReloadRequest().fromJsonString(jsonString, options); + } + + static equals(a: ReloadRequest | PlainMessage | undefined, b: ReloadRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(ReloadRequest, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.hotreload.v1.ReloadResponse + */ +export class ReloadResponse extends Message { + /** + * @generated from oneof xyz.block.ftl.hotreload.v1.ReloadResponse.event + */ + event: { + /** + * @generated from field: xyz.block.ftl.hotreload.v1.ReloadNotRequired reload_not_required = 1; + */ + value: ReloadNotRequired; + case: "reloadNotRequired"; + } | { + /** + * @generated from field: xyz.block.ftl.hotreload.v1.ReloadSuccess reload_success = 2; + */ + value: ReloadSuccess; + case: "reloadSuccess"; + } | { + /** + * @generated from field: xyz.block.ftl.hotreload.v1.ReloadFailed reload_failed = 3; + */ + value: ReloadFailed; + case: "reloadFailed"; + } | { case: undefined; value?: undefined } = { case: undefined }; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.ReloadResponse"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "reload_not_required", kind: "message", T: ReloadNotRequired, oneof: "event" }, + { no: 2, name: "reload_success", kind: "message", T: ReloadSuccess, oneof: "event" }, + { no: 3, name: "reload_failed", kind: "message", T: ReloadFailed, oneof: "event" }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): ReloadResponse { + return new ReloadResponse().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): ReloadResponse { + return new ReloadResponse().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): ReloadResponse { + return new ReloadResponse().fromJsonString(jsonString, options); + } + + static equals(a: ReloadResponse | PlainMessage | undefined, b: ReloadResponse | PlainMessage | undefined): boolean { + return proto3.util.equals(ReloadResponse, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.hotreload.v1.WatchRequest + */ +export class WatchRequest extends Message { + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.WatchRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): WatchRequest { + return new WatchRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): WatchRequest { + return new WatchRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): WatchRequest { + return new WatchRequest().fromJsonString(jsonString, options); + } + + static equals(a: WatchRequest | PlainMessage | undefined, b: WatchRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(WatchRequest, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.hotreload.v1.WatchResponse + */ +export class WatchResponse extends Message { + /** + * @generated from oneof xyz.block.ftl.hotreload.v1.WatchResponse.event + */ + event: { + /** + * @generated from field: xyz.block.ftl.hotreload.v1.ReloadSuccess reload_success = 1; + */ + value: ReloadSuccess; + case: "reloadSuccess"; + } | { + /** + * @generated from field: xyz.block.ftl.hotreload.v1.ReloadFailed reload_failed = 2; + */ + value: ReloadFailed; + case: "reloadFailed"; + } | { case: undefined; value?: undefined } = { case: undefined }; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.WatchResponse"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "reload_success", kind: "message", T: ReloadSuccess, oneof: "event" }, + { no: 2, name: "reload_failed", kind: "message", T: ReloadFailed, oneof: "event" }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): WatchResponse { + return new WatchResponse().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): WatchResponse { + return new WatchResponse().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): WatchResponse { + return new WatchResponse().fromJsonString(jsonString, options); + } + + static equals(a: WatchResponse | PlainMessage | undefined, b: WatchResponse | PlainMessage | undefined): boolean { + return proto3.util.equals(WatchResponse, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.hotreload.v1.RunnerInfoRequest + */ +export class RunnerInfoRequest extends Message { + /** + * @generated from field: string address = 1; + */ + address = ""; + + /** + * @generated from field: string deployment = 2; + */ + deployment = ""; + + /** + * @generated from field: repeated xyz.block.ftl.hotreload.v1.Database databases = 3; + */ + databases: Database[] = []; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.RunnerInfoRequest"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "address", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "deployment", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 3, name: "databases", kind: "message", T: Database, repeated: true }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): RunnerInfoRequest { + return new RunnerInfoRequest().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): RunnerInfoRequest { + return new RunnerInfoRequest().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): RunnerInfoRequest { + return new RunnerInfoRequest().fromJsonString(jsonString, options); + } + + static equals(a: RunnerInfoRequest | PlainMessage | undefined, b: RunnerInfoRequest | PlainMessage | undefined): boolean { + return proto3.util.equals(RunnerInfoRequest, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.hotreload.v1.Database + */ +export class Database extends Message { + /** + * @generated from field: string name = 1; + */ + name = ""; + + /** + * @generated from field: string address = 2; + */ + address = ""; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.Database"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "address", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): Database { + return new Database().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): Database { + return new Database().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): Database { + return new Database().fromJsonString(jsonString, options); + } + + static equals(a: Database | PlainMessage | undefined, b: Database | PlainMessage | undefined): boolean { + return proto3.util.equals(Database, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.hotreload.v1.RunnerInfoResponse + */ +export class RunnerInfoResponse extends Message { + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.RunnerInfoResponse"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): RunnerInfoResponse { + return new RunnerInfoResponse().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): RunnerInfoResponse { + return new RunnerInfoResponse().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): RunnerInfoResponse { + return new RunnerInfoResponse().fromJsonString(jsonString, options); + } + + static equals(a: RunnerInfoResponse | PlainMessage | undefined, b: RunnerInfoResponse | PlainMessage | undefined): boolean { + return proto3.util.equals(RunnerInfoResponse, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.hotreload.v1.ReloadNotRequired + */ +export class ReloadNotRequired extends Message { + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.ReloadNotRequired"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): ReloadNotRequired { + return new ReloadNotRequired().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): ReloadNotRequired { + return new ReloadNotRequired().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): ReloadNotRequired { + return new ReloadNotRequired().fromJsonString(jsonString, options); + } + + static equals(a: ReloadNotRequired | PlainMessage | undefined, b: ReloadNotRequired | PlainMessage | undefined): boolean { + return proto3.util.equals(ReloadNotRequired, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.hotreload.v1.ReloadSuccess + */ +export class ReloadSuccess extends Message { + /** + * Module schema for the built module + * + * @generated from field: xyz.block.ftl.schema.v1.Module module = 1; + */ + module?: Module; + + /** + * Module schema for the built module + * + * @generated from field: xyz.block.ftl.language.v1.ErrorList errors = 2; + */ + errors?: ErrorList; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.ReloadSuccess"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "module", kind: "message", T: Module }, + { no: 2, name: "errors", kind: "message", T: ErrorList }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): ReloadSuccess { + return new ReloadSuccess().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): ReloadSuccess { + return new ReloadSuccess().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): ReloadSuccess { + return new ReloadSuccess().fromJsonString(jsonString, options); + } + + static equals(a: ReloadSuccess | PlainMessage | undefined, b: ReloadSuccess | PlainMessage | undefined): boolean { + return proto3.util.equals(ReloadSuccess, a, b); + } +} + +/** + * @generated from message xyz.block.ftl.hotreload.v1.ReloadFailed + */ +export class ReloadFailed extends Message { + /** + * Module schema for the built module + * + * @generated from field: xyz.block.ftl.language.v1.ErrorList errors = 1; + */ + errors?: ErrorList; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "xyz.block.ftl.hotreload.v1.ReloadFailed"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "errors", kind: "message", T: ErrorList }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): ReloadFailed { + return new ReloadFailed().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): ReloadFailed { + return new ReloadFailed().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): ReloadFailed { + return new ReloadFailed().fromJsonString(jsonString, options); + } + + static equals(a: ReloadFailed | PlainMessage | undefined, b: ReloadFailed | PlainMessage | undefined): boolean { + return proto3.util.equals(ReloadFailed, a, b); + } +} + diff --git a/frontend/console/src/protos/xyz/block/ftl/language/v1/language_pb.ts b/frontend/console/src/protos/xyz/block/ftl/language/v1/language_pb.ts index 66d2df2701..d9ee407631 100644 --- a/frontend/console/src/protos/xyz/block/ftl/language/v1/language_pb.ts +++ b/frontend/console/src/protos/xyz/block/ftl/language/v1/language_pb.ts @@ -1170,11 +1170,11 @@ export class BuildSuccess extends Message { debugPort?: number; /** - * Dev mode runner info file, this file is used to allow the runner to communicate provisioner info back to the plugin + * Dev mode hot reload endpoint, this is used to allow the runner to communicate info back to the running process * - * @generated from field: optional string dev_runner_info_file = 9; + * @generated from field: optional string dev_hot_reload_endpoint = 9; */ - devRunnerInfoFile?: string; + devHotReloadEndpoint?: string; constructor(data?: PartialMessage) { super(); @@ -1192,7 +1192,7 @@ export class BuildSuccess extends Message { { no: 6, name: "errors", kind: "message", T: ErrorList }, { no: 7, name: "dev_endpoint", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, { no: 8, name: "debug_port", kind: "scalar", T: 5 /* ScalarType.INT32 */, opt: true }, - { no: 9, name: "dev_runner_info_file", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, + { no: 9, name: "dev_hot_reload_endpoint", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true }, ]); static fromBinary(bytes: Uint8Array, options?: Partial): BuildSuccess { diff --git a/internal/buildengine/build.go b/internal/buildengine/build.go index e050a82e26..47ebf44711 100644 --- a/internal/buildengine/build.go +++ b/internal/buildengine/build.go @@ -3,7 +3,6 @@ package buildengine import ( "context" "fmt" - "net/url" "os" "path/filepath" "time" @@ -93,10 +92,7 @@ func handleBuildResult(ctx context.Context, projectConfig projectconfig.Config, } if endpoint, ok := result.DevEndpoint.Get(); ok { if devModeEndpoints != nil { - parsed, err := url.Parse(endpoint) - if err == nil { - devModeEndpoints <- dev.LocalEndpoint{Module: config.Module, Endpoint: *parsed, DebugPort: result.DebugPort, Language: config.Language, RunnerInfoFile: result.DevRunnerInfoFile} - } + devModeEndpoints <- dev.LocalEndpoint{Module: config.Module, Endpoint: endpoint, DebugPort: result.DebugPort, Language: config.Language, HotReloadEndpoint: result.HotReloadEndpoint.Default("")} } } return result.Schema, result.Deploy, nil diff --git a/internal/buildengine/languageplugin/plugin.go b/internal/buildengine/languageplugin/plugin.go index 25801244fe..748ed0a172 100644 --- a/internal/buildengine/languageplugin/plugin.go +++ b/internal/buildengine/languageplugin/plugin.go @@ -41,7 +41,7 @@ type BuildResult struct { DevEndpoint optional.Option[string] // File that the runner can use to pass info into the hot reload endpoint - DevRunnerInfoFile optional.Option[string] + HotReloadEndpoint optional.Option[string] DebugPort int } @@ -581,7 +581,7 @@ func buildResultFromProto(result either.Either[*langpb.BuildResponse_BuildSucces Deploy: buildSuccess.Deploy, StartTime: startTime, DevEndpoint: optional.Ptr(buildSuccess.DevEndpoint), - DevRunnerInfoFile: optional.Ptr(buildSuccess.DevRunnerInfoFile), + HotReloadEndpoint: optional.Ptr(buildSuccess.DevHotReloadEndpoint), DebugPort: port, }, nil case either.Right[*langpb.BuildResponse_BuildSuccess, *langpb.BuildResponse_BuildFailure]: diff --git a/internal/buildengine/languageplugin/plugin_integration_test.go b/internal/buildengine/languageplugin/plugin_integration_test.go index 413241bd5e..0a13b95154 100644 --- a/internal/buildengine/languageplugin/plugin_integration_test.go +++ b/internal/buildengine/languageplugin/plugin_integration_test.go @@ -92,7 +92,7 @@ func TestBuilds(t *testing.T) { // Update verb name and expect auto rebuild started and ended modifyVerbName(MODULE_NAME, VERB_NAME_SNIPPET, "aaabbbccc"), - waitForAutoRebuildToStart("build-and-watch"), + in.IfLanguages(waitForAutoRebuildToStart("build-and-watch"), "go"), waitForBuildToEnd(SUCCESS, "build-and-watch", true, func(t testing.TB, ic in.TestContext, event *langpb.BuildResponse) { successEvent, ok := event.Event.(*langpb.BuildResponse_BuildSuccess) assert.True(t, ok) @@ -109,7 +109,7 @@ func TestBuilds(t *testing.T) { // Trigger an auto rebuild, but when we are told of the build being started, send a build context update // to force a new build modifyVerbName(MODULE_NAME, "aaabbbccc", "aaaabbbbcccc"), - waitForAutoRebuildToStart("build-and-watch"), + in.IfLanguages(waitForAutoRebuildToStart("build-and-watch"), "go"), sendUpdatedBuildContext("explicit-build", []string{}, sch), waitForBuildToEnd(SUCCESSORFAILURE, "build-and-watch", true, nil), waitForBuildToEnd(SUCCESS, "explicit-build", false, nil), diff --git a/internal/dev/devendpoint.go b/internal/dev/devendpoint.go index ec7f7c2877..6d3906af3b 100644 --- a/internal/dev/devendpoint.go +++ b/internal/dev/devendpoint.go @@ -1,15 +1,9 @@ package dev -import ( - "net/url" - - "github.com/alecthomas/types/optional" -) - type LocalEndpoint struct { - Module string - Endpoint url.URL - DebugPort int - Language string - RunnerInfoFile optional.Option[string] + Module string + Endpoint string + DebugPort int + Language string + HotReloadEndpoint string } diff --git a/internal/integration/harness.go b/internal/integration/harness.go index 327f9c89d0..c2497e10af 100644 --- a/internal/integration/harness.go +++ b/internal/integration/harness.go @@ -48,7 +48,7 @@ const dumpPath = "/tmp/ftl-kube-report" var RedPandaBrokers = []string{"127.0.0.1:19092"} func (i TestContext) integrationTestTimeout() time.Duration { - timeout := optional.Zero(os.Getenv("FTL_INTEGRATION_TEST_TIMEOUT")).Default("5s") + timeout := optional.Zero(os.Getenv("FTL_INTEGRATION_TEST_TIMEOUT")).Default("25s") d, err := time.ParseDuration(timeout) if err != nil { panic(err) diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java new file mode 100644 index 0000000000..3be83f4f72 --- /dev/null +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/HotReloadHandler.java @@ -0,0 +1,275 @@ +package xyz.block.ftl.deployment; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.jboss.logging.Logger; + +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.stub.StreamObserver; +import io.quarkus.bootstrap.classloading.QuarkusClassLoader; +import io.quarkus.deployment.dev.RuntimeUpdatesProcessor; +import xyz.block.ftl.hotreload.RunnerInfo; +import xyz.block.ftl.hotreload.RunnerNotification; +import xyz.block.ftl.hotreload.v1.HotReloadServiceGrpc; +import xyz.block.ftl.hotreload.v1.ReloadFailed; +import xyz.block.ftl.hotreload.v1.ReloadNotRequired; +import xyz.block.ftl.hotreload.v1.ReloadRequest; +import xyz.block.ftl.hotreload.v1.ReloadResponse; +import xyz.block.ftl.hotreload.v1.ReloadSuccess; +import xyz.block.ftl.hotreload.v1.RunnerInfoRequest; +import xyz.block.ftl.hotreload.v1.RunnerInfoResponse; +import xyz.block.ftl.hotreload.v1.WatchRequest; +import xyz.block.ftl.hotreload.v1.WatchResponse; +import xyz.block.ftl.language.v1.Error; +import xyz.block.ftl.language.v1.ErrorList; +import xyz.block.ftl.schema.v1.Module; +import xyz.block.ftl.v1.PingRequest; +import xyz.block.ftl.v1.PingResponse; + +public class HotReloadHandler extends HotReloadServiceGrpc.HotReloadServiceImplBase { + + private static final Logger LOG = Logger.getLogger(HotReloadHandler.class); + + static final Set existingMigrations = Collections.newSetFromMap(new ConcurrentHashMap<>()); + + private static volatile HotReloadHandler INSTANCE; + + private volatile Module module; + private volatile ErrorList errors; + private volatile Server server; + private boolean explicitlyReloading = false; + private boolean sentResults = false; + private final List> watches = Collections.synchronizedList(new ArrayList<>()); + + public static HotReloadHandler getInstance() { + start(); + return INSTANCE; + } + + synchronized void setResults(Module module, ErrorList errors) { + this.module = module; + this.errors = errors; + if (!explicitlyReloading) { + List> watches; + synchronized (this.watches) { + watches = new ArrayList<>(this.watches); + } + for (var watch : watches) { + try { + if (errors == null || errors.getErrorsCount() == 0) { + watch.onNext(WatchResponse.newBuilder() + .setReloadSuccess(ReloadSuccess.newBuilder().setModule(module).build()).build()); + } else { + watch.onNext(WatchResponse.newBuilder() + .setReloadFailed(ReloadFailed.newBuilder().setErrors(errors).build()).build()); + } + } catch (Exception e) { + LOG.debugf("Failed to send watch response %s", e.toString()); + this.watches.remove(watch); + } + } + } + sentResults = false; + explicitlyReloading = false; + notifyAll(); + } + + @Override + public void ping(PingRequest request, StreamObserver responseObserver) { + responseObserver.onNext(PingResponse.newBuilder().build()); + responseObserver.onCompleted(); + } + + @Override + public void reload(ReloadRequest request, StreamObserver responseObserver) { + + // This is complex, as the restart can't happen until the runner is up + // We want to report on the results of the schema generations, so we can bring up a runner + // Run the restart in a new thread, so we can report on the schema once it is ready + var currentModule = module; + synchronized (HotReloadHandler.this) { + if (explicitlyReloading) { + responseObserver.onNext(ReloadResponse.newBuilder() + .setReloadNotRequired(ReloadNotRequired.newBuilder().build()) + .build()); + return; + } + // If we have new results we don't want to re-scan, we want to send them back + if (!sentResults) { + explicitlyReloading = true; + Thread t = new Thread(() -> { + try { + doScan(request.getForce()); + } finally { + synchronized (HotReloadHandler.this) { + explicitlyReloading = false; + HotReloadHandler.this.notifyAll(); + } + } + }, "FTL Restart Thread"); + t.start(); + while (explicitlyReloading) { + try { + HotReloadHandler.this.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + sentResults = true; + Throwable compileProblem = RuntimeUpdatesProcessor.INSTANCE.getCompileProblem(); + Throwable deploymentProblems = RuntimeUpdatesProcessor.INSTANCE.getDeploymentProblem(); + if (compileProblem != null || deploymentProblems != null) { + ErrorList.Builder builder = ErrorList.newBuilder(); + if (compileProblem != null) { + builder.addErrors(xyz.block.ftl.language.v1.Error.newBuilder() + .setLevel(xyz.block.ftl.language.v1.Error.ErrorLevel.ERROR_LEVEL_ERROR) + .setType(xyz.block.ftl.language.v1.Error.ErrorType.ERROR_TYPE_COMPILER) + .setMsg(compileProblem.getMessage()) + .build()); + } + if (deploymentProblems != null) { + builder.addErrors(xyz.block.ftl.language.v1.Error.newBuilder() + .setLevel(xyz.block.ftl.language.v1.Error.ErrorLevel.ERROR_LEVEL_ERROR) + .setType(Error.ErrorType.ERROR_TYPE_FTL) + .setMsg(deploymentProblems.getMessage()) + .build()); + } + responseObserver.onNext(ReloadResponse.newBuilder() + .setReloadFailed(ReloadFailed.newBuilder() + .setErrors(builder).build()) + .build()); + responseObserver.onCompleted(); + } else if (errors != null && errors.getErrorsCount() > 0) { + responseObserver.onNext(ReloadResponse.newBuilder() + .setReloadFailed(ReloadFailed.newBuilder() + .setErrors(errors).build()) + .build()); + responseObserver.onCompleted(); + } else if (module != null) { + if (module == currentModule) { + responseObserver.onNext(ReloadResponse.newBuilder() + .setReloadNotRequired(ReloadNotRequired.newBuilder().build()) + .build()); + } else { + responseObserver.onNext(ReloadResponse.newBuilder() + .setReloadSuccess(ReloadSuccess.newBuilder() + .setModule(module).build()) + .build()); + } + responseObserver.onCompleted(); + } else { + responseObserver.onError(new RuntimeException("schema not generated")); + } + } + } + + @Override + public void watch(WatchRequest request, StreamObserver responseObserver) { + if (module != null || errors != null) { + if (errors == null || errors.getErrorsCount() == 0) { + responseObserver.onNext(WatchResponse.newBuilder() + .setReloadSuccess(ReloadSuccess.newBuilder().setModule(module).build()).build()); + } else { + responseObserver.onNext(WatchResponse.newBuilder() + .setReloadFailed(ReloadFailed.newBuilder().setErrors(errors).build()).build()); + } + } + watches.add(responseObserver); + } + + @Override + public void runnerInfo(RunnerInfoRequest request, StreamObserver responseObserver) { + Map databases = new HashMap<>(); + for (var db : request.getDatabasesList()) { + databases.put(db.getName(), db.getAddress()); + } + RunnerNotification + .setRunnerInfo(new RunnerInfo(request.getAddress(), request.getDeployment(), databases)); + responseObserver.onNext(RunnerInfoResponse.newBuilder().build()); + responseObserver.onCompleted(); + } + + public static void start() { + if (INSTANCE != null) { + return; + } + synchronized (HotReloadHandler.class) { + if (INSTANCE == null) { + var hr = new HotReloadHandler(); + hr.init(); + INSTANCE = hr; + } + } + } + + private void init() { + gatherMigrations(); + int port = Integer.getInteger("ftl.language.port"); + server = ServerBuilder.forPort(port) + .addService(this) + .build(); + try { + LOG.info("Starting Hot Reload gRPC server on port " + port); + server.start(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + ((QuarkusClassLoader) HotReloadHandler.class.getClassLoader()).addCloseTask(new Runnable() { + @Override + public void run() { + server.shutdownNow(); + } + }); + } + + private static void gatherMigrations() { + for (var dir : RuntimeUpdatesProcessor.INSTANCE.getSourcesDir()) { + Path migrations = dir.resolve("db"); + if (Files.isDirectory(migrations)) { + try (var stream = Files.walk(migrations)) { + stream.forEach(existingMigrations::add); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + } + + void doScan(boolean force) { + if (RuntimeUpdatesProcessor.INSTANCE != null) { + try { + AtomicBoolean newForce = new AtomicBoolean(); + for (var dir : RuntimeUpdatesProcessor.INSTANCE.getSourcesDir()) { + Path migrations = dir.resolve("db"); + if (Files.isDirectory(migrations)) { + try (var stream = Files.walk(migrations)) { + stream.forEach(p -> { + if (p.getFileName().toString().endsWith(".sql")) { + if (existingMigrations.add(p)) { + newForce.set(true); + } + } + }); + } + } + } + RuntimeUpdatesProcessor.INSTANCE.doScan(force || newForce.get()); + } catch (Exception e) { + Logger.getLogger(HotReloadHandler.class).error("Failed to scan for changes", e); + } + } + } +} diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleBuilder.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleBuilder.java index 28d60b9028..77941bee9e 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleBuilder.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleBuilder.java @@ -17,6 +17,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.regex.Pattern; @@ -537,7 +538,7 @@ public int getDeclsCount() { return decls.size(); } - public void writeTo(OutputStream out, OutputStream errorOut) throws IOException { + public void writeTo(OutputStream out, OutputStream errorOut, BiConsumer consumer) throws IOException { decls.values().stream().forEachOrdered(protoModuleBuilder::addDecls); ErrorList.Builder builder = ErrorList.newBuilder(); if (!validationFailures.isEmpty()) { @@ -552,8 +553,13 @@ public void writeTo(OutputStream out, OutputStream errorOut) throws IOException .build())); errors.forEach(log::error); } - builder.build().writeTo(errorOut); - protoModuleBuilder.build().writeTo(out); + ErrorList errorList = builder.build(); + errorList.writeTo(errorOut); + Module module = protoModuleBuilder.build(); + module.writeTo(out); + if (consumer != null) { + consumer.accept(module, errorList); + } } public void registerTypeAlias(String name, org.jboss.jandex.Type finalT, org.jboss.jandex.Type finalS, boolean exported, diff --git a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java index b64a4f3354..3fc8c51620 100644 --- a/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java +++ b/jvm-runtime/ftl-runtime/common/deployment/src/main/java/xyz/block/ftl/deployment/ModuleProcessor.java @@ -2,7 +2,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -15,8 +14,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Timer; -import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BiConsumer; import java.util.stream.Collectors; import org.jboss.jandex.DotName; @@ -25,8 +24,6 @@ import org.tomlj.TomlParseResult; import io.quarkus.arc.deployment.AdditionalBeanBuildItem; -import io.quarkus.bootstrap.classloading.QuarkusClassLoader; -import io.quarkus.deployment.IsDevelopment; import io.quarkus.deployment.annotations.BuildProducer; import io.quarkus.deployment.annotations.BuildStep; import io.quarkus.deployment.annotations.ExecutionTime; @@ -41,14 +38,13 @@ import io.quarkus.deployment.builditem.ServiceStartBuildItem; import io.quarkus.deployment.builditem.ShutdownContextBuildItem; import io.quarkus.deployment.builditem.SystemPropertyBuildItem; -import io.quarkus.deployment.dev.RuntimeUpdatesProcessor; import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem; import io.quarkus.grpc.deployment.BindableServiceBuildItem; import io.quarkus.runtime.LaunchMode; import io.quarkus.runtime.util.HashUtil; import io.quarkus.vertx.http.deployment.RequireSocketHttpBuildItem; import io.quarkus.vertx.http.deployment.RequireVirtualHttpBuildItem; -import xyz.block.ftl.language.v1.Error; +import xyz.block.ftl.hotreload.RunnerNotification; import xyz.block.ftl.language.v1.ErrorList; import xyz.block.ftl.runtime.FTLDatasourceCredentials; import xyz.block.ftl.runtime.FTLRecorder; @@ -68,8 +64,6 @@ public class ModuleProcessor { private static final String SCHEMA_OUT = "schema.pb"; private static final String ERRORS_OUT = "errors.pb"; - public static final String DEV_MODE_RUNNER_INFO_FILE = "FTL_RUNNER_INFO"; - /** * Persistent schema hash, used to detect runner restarts in dev mode. */ @@ -91,76 +85,6 @@ public SystemPropertyBuildItem moduleNameConfig(ApplicationInfoBuildItem applica return new SystemPropertyBuildItem("ftl.module.name", applicationInfoBuildItem.getName()); } - private static volatile Timer devModeProblemTimer; - - @BuildStep(onlyIf = IsDevelopment.class) - @Record(ExecutionTime.STATIC_INIT) - public void reportDevModeProblems(FTLRecorder recorder, OutputTargetBuildItem outputTargetBuildItem) { - if (devModeProblemTimer != null) { - return; - } - Path errorOutput = outputTargetBuildItem.getOutputDirectory().resolve(ERRORS_OUT); - devModeProblemTimer = new Timer("FTL Dev Mode Error Report", true); - devModeProblemTimer.schedule(new TimerTask() { - - String errorHash; - - @Override - public void run() { - Throwable compileProblem = RuntimeUpdatesProcessor.INSTANCE.getCompileProblem(); - Throwable deploymentProblems = RuntimeUpdatesProcessor.INSTANCE.getDeploymentProblem(); - if (compileProblem != null || deploymentProblems != null) { - ErrorList.Builder builder = ErrorList.newBuilder(); - if (compileProblem != null) { - builder.addErrors(Error.newBuilder() - .setLevel(Error.ErrorLevel.ERROR_LEVEL_ERROR) - .setType(Error.ErrorType.ERROR_TYPE_COMPILER) - .setMsg(compileProblem.getMessage()) - .build()); - } - if (deploymentProblems != null) { - builder.addErrors(Error.newBuilder() - .setLevel(Error.ErrorLevel.ERROR_LEVEL_ERROR) - .setType(Error.ErrorType.ERROR_TYPE_FTL) - .setMsg(deploymentProblems.getMessage()) - .build()); - } - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try (var out = Files.newOutputStream(errorOutput)) { - builder.build().writeTo(baos); - errorHash = HashUtil.sha256(baos.toByteArray()); - builder.build().writeTo(out); - } catch (IOException e) { - log.error("Failed to write error list", e); - } - } else if (errorHash != null) { - if (!Files.exists(errorOutput)) { - // File already cleared - errorHash = null; - } else { - try { - var currentHash = HashUtil.sha256(Files.readAllBytes(errorOutput)); - if (currentHash.equals(errorHash)) { - try (OutputStream output = Files.newOutputStream(errorOutput)) { - ErrorList.newBuilder().build().writeTo(output); - } - } - } catch (IOException e) { - log.errorf("Failed to read error list", e); - } - } - } - } - }, 1000, 1000); - ((QuarkusClassLoader) ModuleProcessor.class.getClassLoader()).addCloseTask(new Runnable() { - @Override - public void run() { - devModeProblemTimer.cancel(); - devModeProblemTimer = null; - } - }); - } - @BuildStep ModuleNameBuildItem moduleName(ApplicationInfoBuildItem applicationInfoBuildItem, ApplicationArchivesBuildItem archivesBuildItem) throws IOException { @@ -246,51 +170,50 @@ public void generateSchema(CombinedIndexBuildItem index, Path errorOutput = outputTargetBuildItem.getOutputDirectory().resolve(ERRORS_OUT); ByteArrayOutputStream sch = new ByteArrayOutputStream(); ByteArrayOutputStream err = new ByteArrayOutputStream(); - moduleBuilder.writeTo(sch, err); + AtomicReference errRef = new AtomicReference<>(); + AtomicReference schRef = new AtomicReference<>(); + moduleBuilder.writeTo(sch, err, new BiConsumer() { + @Override + public void accept(xyz.block.ftl.schema.v1.Module module, ErrorList errorList) { + errRef.set(errorList); + schRef.set(module); + } + }); var schBytes = sch.toByteArray(); var errBytes = err.toByteArray(); + recorder.loadModuleContextOnStartup(); + + Files.write(output, schBytes); + Files.write(errorOutput, errBytes); if (launchModeBuildItem.getLaunchMode() == LaunchMode.DEVELOPMENT) { + HotReloadHandler.getInstance().setResults(schRef.get(), errRef.get()); // Handle runner restarts in development mode. If this is the first launch, or the schema has changed, we need to // get updated runner information, although we don't actually get this until the runner has started. var hash = HashUtil.sha256(schBytes); - if (Objects.equals(hash, schemaHash)) { - return; + if (!Objects.equals(hash, schemaHash)) { + schemaHash = hash; + RunnerNotification.setRequiresNewRunnerDetails(); } - schemaHash = hash; - String runnerInfo = System.getenv(DEV_MODE_RUNNER_INFO_FILE); - if (runnerInfo != null) { - Path path = Path.of(runnerInfo); - // Delete the runner info file if it already exists - Files.deleteIfExists(path); - // This method tells the runtime not to actually start until we have updated runner details - systemPropertyBuildItemBuildProducer - .produce(new SystemPropertyBuildItem(FTLRecorder.DEV_MODE_RUNNER_INFO_PATH, path.toString())); - recorder.handleDevModeRunnerStart(shutdownContextBuildItem); + } else { + output = outputTargetBuildItem.getOutputDirectory().resolve("launch"); + try (var out = Files.newOutputStream(output)) { + out.write( + """ + #!/bin/bash + if [ -n "$FTL_DEBUG_PORT" ]; then + FTL_JVM_OPTS="$FTL_JVM_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:$FTL_DEBUG_PORT" + fi + exec java $FTL_JVM_OPTS -jar quarkus-app/quarkus-run.jar""" + .getBytes(StandardCharsets.UTF_8)); } + var perms = Files.getPosixFilePermissions(output); + EnumSet newPerms = EnumSet.copyOf(perms); + newPerms.add(PosixFilePermission.GROUP_EXECUTE); + newPerms.add(PosixFilePermission.OWNER_EXECUTE); + Files.setPosixFilePermissions(output, newPerms); } - recorder.loadModuleContextOnStartup(); - - Files.write(output, schBytes); - Files.write(errorOutput, errBytes); - - output = outputTargetBuildItem.getOutputDirectory().resolve("launch"); - try (var out = Files.newOutputStream(output)) { - out.write( - """ - #!/bin/bash - if [ -n "$FTL_DEBUG_PORT" ]; then - FTL_JVM_OPTS="$FTL_JVM_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:$FTL_DEBUG_PORT" - fi - exec java $FTL_JVM_OPTS -jar quarkus-app/quarkus-run.jar""" - .getBytes(StandardCharsets.UTF_8)); - } - var perms = Files.getPosixFilePermissions(output); - EnumSet newPerms = EnumSet.copyOf(perms); - newPerms.add(PosixFilePermission.GROUP_EXECUTE); - newPerms.add(PosixFilePermission.OWNER_EXECUTE); - Files.setPosixFilePermissions(output, newPerms); } @BuildStep @@ -319,10 +242,4 @@ void openSocket(BuildProducer virtual, socket.produce(RequireSocketHttpBuildItem.MARKER); virtual.produce(RequireVirtualHttpBuildItem.MARKER); } - - @Record(ExecutionTime.RUNTIME_INIT) - @BuildStep(onlyIf = IsDevelopment.class) - void hotReload(ShutdownContextBuildItem shutdownContextBuildItem, FTLRecorder recorder) { - recorder.startReloadTimer(shutdownContextBuildItem); - } } diff --git a/jvm-runtime/ftl-runtime/common/hotreload/pom.xml b/jvm-runtime/ftl-runtime/common/hotreload/pom.xml new file mode 100644 index 0000000000..bf28b9b7c2 --- /dev/null +++ b/jvm-runtime/ftl-runtime/common/hotreload/pom.xml @@ -0,0 +1,14 @@ + + + 4.0.0 + + + xyz.block.ftl + ftl-jvm-runtime-parent + 1.0-SNAPSHOT + + ftl-jvm-hot-reload + FTL Java Runtime - Hot Reload SPI + + diff --git a/jvm-runtime/ftl-runtime/common/hotreload/src/main/java/xyz/block/ftl/hotreload/RunnerInfo.java b/jvm-runtime/ftl-runtime/common/hotreload/src/main/java/xyz/block/ftl/hotreload/RunnerInfo.java new file mode 100644 index 0000000000..dfaa55b63f --- /dev/null +++ b/jvm-runtime/ftl-runtime/common/hotreload/src/main/java/xyz/block/ftl/hotreload/RunnerInfo.java @@ -0,0 +1,7 @@ +package xyz.block.ftl.hotreload; + +import java.util.Map; + +public record RunnerInfo(String address, String deployment, Map databases) { + +} diff --git a/jvm-runtime/ftl-runtime/common/hotreload/src/main/java/xyz/block/ftl/hotreload/RunnerNotification.java b/jvm-runtime/ftl-runtime/common/hotreload/src/main/java/xyz/block/ftl/hotreload/RunnerNotification.java new file mode 100644 index 0000000000..a74492093f --- /dev/null +++ b/jvm-runtime/ftl-runtime/common/hotreload/src/main/java/xyz/block/ftl/hotreload/RunnerNotification.java @@ -0,0 +1,47 @@ +package xyz.block.ftl.hotreload; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; + +public class RunnerNotification { + + private static volatile Consumer callback; + private static volatile RunnerInfo info; + private static final AtomicBoolean requiresNewRunnerDetails = new AtomicBoolean(false); + private static final List runnerDetailsCallbacks = Collections.synchronizedList(new ArrayList<>()); + + public static synchronized void setCallback(Consumer callback) { + if (RunnerNotification.callback != null) { + throw new IllegalStateException("Callback already set"); + } + if (info != null) { + callback.accept(info); + info = null; + } else { + RunnerNotification.callback = callback; + } + } + + public static synchronized void setRunnerInfo(RunnerInfo info) { + if (callback != null) { + callback.accept(info); + callback = null; + } else { + RunnerNotification.info = info; + } + } + + public static synchronized void setRequiresNewRunnerDetails() { + for (Runnable callback : runnerDetailsCallbacks) { + callback.run(); + } + } + + public static synchronized void onRunnerDetails(Runnable callback) { + runnerDetailsCallbacks.add(callback); + } + +} diff --git a/jvm-runtime/ftl-runtime/common/pom.xml b/jvm-runtime/ftl-runtime/common/pom.xml index 3aa2e02a32..74a2a85925 100644 --- a/jvm-runtime/ftl-runtime/common/pom.xml +++ b/jvm-runtime/ftl-runtime/common/pom.xml @@ -18,5 +18,6 @@ runtime bom build-parent + hotreload diff --git a/jvm-runtime/ftl-runtime/common/runtime/pom.xml b/jvm-runtime/ftl-runtime/common/runtime/pom.xml index 89362d3694..133ed0e98e 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/pom.xml +++ b/jvm-runtime/ftl-runtime/common/runtime/pom.xml @@ -12,6 +12,10 @@ FTL Java Runtime - Runtime + + xyz.block.ftl + ftl-jvm-hot-reload + io.quarkus quarkus-arc @@ -100,8 +104,8 @@ extension-descriptor - ${project.groupId}:${project.artifactId}-deployment:${project.version} - + ${project.groupId}:${project.artifactId}-deployment:${project.version} + xyz.block.ftl:ftl-jvm-hot-reload @@ -164,6 +168,7 @@ **/lease.proto **/ftl.proto **/language.proto + **/hotreload.proto **/publish.proto **/pubsub.proto diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DevModeRunnerDetails.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DevModeRunnerDetails.java index 36fc9235b4..6a31e86510 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DevModeRunnerDetails.java +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/DevModeRunnerDetails.java @@ -1,69 +1,32 @@ package xyz.block.ftl.runtime; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.Properties; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import xyz.block.ftl.deployment.v1.GetDeploymentContextResponse; +import xyz.block.ftl.hotreload.RunnerInfo; +import xyz.block.ftl.hotreload.RunnerNotification; public class DevModeRunnerDetails implements RunnerDetails { - private final Path path; private volatile Map databases; private volatile String proxyAddress; private volatile String deployment; private volatile boolean closed; - private static final Pattern dbNames = Pattern.compile("database\\.([a-zA-Z0-9]+).url"); private volatile boolean loaded = false; - public DevModeRunnerDetails(Path path) { - this.path = path; - startWatchThread(); + public DevModeRunnerDetails() { + RunnerNotification.setCallback(this::setRunnerInfo); } - void startWatchThread() { - Thread watchThread = new Thread(() -> { - while (!closed) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - if (Files.exists(path)) { - Properties p = new Properties(); - try (InputStream stream = Files.newInputStream(path)) { - p.load(stream); - synchronized (this) { - proxyAddress = p.getProperty("proxy.bind.address"); - deployment = p.getProperty("deployment"); - var dbs = new HashMap(); - for (var addr : p.stringPropertyNames()) { - Matcher m = dbNames.matcher(addr); - if (m.matches()) { - dbs.put(m.group(1), p.getProperty(addr)); - } - } - databases = dbs; - loaded = true; - notifyAll(); - return; - } - - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - }); - watchThread.setDaemon(true); - watchThread.start(); + private void setRunnerInfo(RunnerInfo runnerInfo) { + synchronized (this) { + proxyAddress = runnerInfo.address(); + deployment = runnerInfo.deployment(); + databases = runnerInfo.databases(); + loaded = true; + notifyAll(); + } } @Override diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLController.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLController.java index 485272af46..f092166aa8 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLController.java +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLController.java @@ -1,6 +1,5 @@ package xyz.block.ftl.runtime; -import java.nio.file.Path; import java.time.Duration; import java.util.List; import java.util.Map; @@ -13,6 +12,7 @@ import xyz.block.ftl.LeaseFailedException; import xyz.block.ftl.LeaseHandle; import xyz.block.ftl.deployment.v1.GetDeploymentContextResponse; +import xyz.block.ftl.hotreload.RunnerNotification; public class FTLController implements LeaseClient { private static final Logger log = Logger.getLogger(FTLController.class); @@ -44,6 +44,7 @@ public static FTLController instance() { this.moduleName = System.getProperty("ftl.module.name"); if (LaunchMode.current() != LaunchMode.DEVELOPMENT) { haveRunnerInfo = true; + RunnerNotification.onRunnerDetails(this::devModeShutdown); } } @@ -94,7 +95,7 @@ public void readDevModeRunnerInfo() { runnerConnection = null; } runnerDetails.close(); - runnerDetails = new DevModeRunnerDetails(Path.of(System.getProperty(FTLRecorder.DEV_MODE_RUNNER_INFO_PATH))); + runnerDetails = new DevModeRunnerDetails(); haveRunnerInfo = true; this.notifyAll(); } diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java index 8edd90fb34..4ed04f1ed8 100644 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java +++ b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/FTLRecorder.java @@ -3,8 +3,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.List; -import java.util.Timer; -import java.util.TimerTask; import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext; import org.jboss.resteasy.reactive.server.core.parameters.ParameterExtractor; @@ -12,7 +10,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.quarkus.arc.Arc; -import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; import xyz.block.ftl.deployment.v1.GetDeploymentContextResponse; import xyz.block.ftl.runtime.http.FTLHttpHandler; @@ -23,7 +20,6 @@ public class FTLRecorder { public static final String X_FTL_VERB = "X-ftl-verb"; - public static final String DEV_MODE_RUNNER_INFO_PATH = "ftl.dev.runner.info"; public void registerVerb(String module, String verbName, String methodName, List> parameterTypes, Class verbHandlerClass, List paramMappers, @@ -157,34 +153,13 @@ public Object extractParameter(ResteasyReactiveRequestContext context) { } } - public void startReloadTimer(ShutdownContext shutdownContext) { - Timer t = new Timer("FTL Hot Reload Timer", true); - t.schedule(new TimerTask() { - @Override - public void run() { - HotReloadSetup.doScan(false); - } - }, 1000, 1000); - shutdownContext.addShutdownTask(new Runnable() { - @Override - public void run() { - t.cancel(); - } - }); - } - public void registerDatabase(String dbKind, GetDeploymentContextResponse.DbType name) { FTLController.instance().registerDatabase(dbKind, name); } - public void handleDevModeRunnerStart(ShutdownContext shutdownContext) { + public void requireNewRunnerDetails() { + FTLController.instance().devModeShutdown(); FTLController.instance().readDevModeRunnerInfo(); - shutdownContext.addShutdownTask(new Runnable() { - @Override - public void run() { - FTLController.instance().devModeShutdown(); - } - }); } public void loadModuleContextOnStartup() { diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/HotReloadSetup.java b/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/HotReloadSetup.java deleted file mode 100644 index 52900ddaf2..0000000000 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/java/xyz/block/ftl/runtime/HotReloadSetup.java +++ /dev/null @@ -1,66 +0,0 @@ -package xyz.block.ftl.runtime; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collections; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.jboss.logging.Logger; - -import io.quarkus.dev.spi.HotReplacementContext; -import io.quarkus.dev.spi.HotReplacementSetup; - -public class HotReloadSetup implements HotReplacementSetup { - - static final Set existingMigrations = Collections.newSetFromMap(new ConcurrentHashMap<>()); - - static volatile HotReplacementContext context; - private static volatile String errorOutputPath; - private static final String ERRORS_OUT = "errors.pb"; - - @Override - public void setupHotDeployment(HotReplacementContext hrc) { - context = hrc; - for (var dir : context.getSourcesDir()) { - Path migrations = dir.resolve("db"); - if (Files.isDirectory(migrations)) { - try (var stream = Files.walk(migrations)) { - stream.forEach(p -> { - existingMigrations.add(p); - - }); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - } - - static void doScan(boolean force) { - if (context != null) { - try { - AtomicBoolean newForce = new AtomicBoolean(); - for (var dir : context.getSourcesDir()) { - Path migrations = dir.resolve("db"); - if (Files.isDirectory(migrations)) { - try (var stream = Files.walk(migrations)) { - stream.forEach(p -> { - if (p.getFileName().toString().endsWith(".sql")) { - if (existingMigrations.add(p)) { - newForce.set(true); - } - } - }); - } - } - } - context.doScan(force || newForce.get()); - } catch (Exception e) { - Logger.getLogger(HotReloadSetup.class).error("Failed to scan for changes", e); - } - } - } -} diff --git a/jvm-runtime/ftl-runtime/common/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup b/jvm-runtime/ftl-runtime/common/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup deleted file mode 100644 index f02b221da3..0000000000 --- a/jvm-runtime/ftl-runtime/common/runtime/src/main/resources/META-INF/services/io.quarkus.dev.spi.HotReplacementSetup +++ /dev/null @@ -1 +0,0 @@ -xyz.block.ftl.runtime.HotReloadSetup \ No newline at end of file diff --git a/jvm-runtime/ftl-runtime/pom.xml b/jvm-runtime/ftl-runtime/pom.xml index c9e2333377..61426032c4 100644 --- a/jvm-runtime/ftl-runtime/pom.xml +++ b/jvm-runtime/ftl-runtime/pom.xml @@ -73,6 +73,11 @@ pom import + + xyz.block.ftl + ftl-jvm-hot-reload + ${project.version} + xyz.block.ftl ftl-jvm-runtime diff --git a/jvm-runtime/plugin/common/jvmcommon.go b/jvm-runtime/plugin/common/jvmcommon.go index fa64f54ae0..27666205d5 100644 --- a/jvm-runtime/plugin/common/jvmcommon.go +++ b/jvm-runtime/plugin/common/jvmcommon.go @@ -7,7 +7,6 @@ import ( "context" "fmt" "io/fs" - "net/http" "os" "path/filepath" "reflect" @@ -24,11 +23,14 @@ import ( "github.com/beevik/etree" "github.com/block/scaffolder" "github.com/go-viper/mapstructure/v2" + "github.com/jpillora/backoff" "golang.org/x/exp/maps" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/structpb" "github.com/block/ftl" + hotreloadpb "github.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1" + "github.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1/hotreloadpbconnect" langpb "github.com/block/ftl/backend/protos/xyz/block/ftl/language/v1" langconnect "github.com/block/ftl/backend/protos/xyz/block/ftl/language/v1/languagepbconnect" ftlv1 "github.com/block/ftl/backend/protos/xyz/block/ftl/v1" @@ -45,6 +47,7 @@ import ( "github.com/block/ftl/internal/flock" "github.com/block/ftl/internal/log" "github.com/block/ftl/internal/moduleconfig" + "github.com/block/ftl/internal/rpc" "github.com/block/ftl/internal/watch" ) @@ -247,6 +250,7 @@ func (s *Service) runDevMode(ctx context.Context, req *connect.Request[langpb.Bu err := s.runQuarkusDev(ctx, req, stream, first) first = false if err != nil { + log.FromContext(ctx).Errorf(err, "Dev mode process exited") return err } if !waitForFileChanges(ctx, fileEvents) { @@ -342,10 +346,8 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb } errorFile := filepath.Join(buildCtx.Config.DeployDir, ErrorFile) schemaFile := filepath.Join(buildCtx.Config.DeployDir, SchemaFile) - runnerInfoFile := filepath.Join(buildCtx.Config.Dir, ".ftl-runner-info") os.Remove(errorFile) os.Remove(schemaFile) - os.Remove(runnerInfoFile) errorHash := sha256.SHA256{} schemaHash := sha256.SHA256{} migrationHash := watch.FileHashes{} @@ -357,7 +359,8 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb } ctx = log.ContextWithLogger(ctx, logger) - bind := fmt.Sprintf("http://localhost:%d", address.Port) + devModeEndpoint := fmt.Sprintf("http://localhost:%d", address.Port) + bind := devModeEndpoint devModeBuild := buildCtx.Config.DevModeBuild debugPort, err := plugin.AllocatePort() debugPort32 := int32(debugPort.Port) @@ -365,14 +368,19 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb if err == nil { devModeBuild = fmt.Sprintf("%s -Ddebug=%d", devModeBuild, debugPort.Port) } + hotReloadPort, err := plugin.AllocatePort() + if err != nil { + return fmt.Errorf("could not allocate port: %w", err) + } + devModeBuild = fmt.Sprintf("%s -Dftl.language.port=%d", devModeBuild, hotReloadPort.Port) + if os.Getenv("FTL_SUSPEND") == "true" { devModeBuild += " -Dsuspend " } go func() { logger.Infof("Using dev mode build command '%s'", devModeBuild) command := exec.Command(ctx, log.Debug, buildCtx.Config.Dir, "bash", "-c", devModeBuild) - command.Env = append(command.Env, fmt.Sprintf("FTL_BIND=%s", bind)) - command.Env = append(command.Env, fmt.Sprintf("FTL_RUNNER_INFO=%s", runnerInfoFile)) + command.Env = append(command.Env, fmt.Sprintf("FTL_BIND=%s", bind), "MAVEN_OPTS=-Xmx1024m") command.Stdout = os.Stdout command.Stderr = os.Stderr err = command.Run() @@ -385,11 +393,22 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb }() forceUpdate := false - schemaChangeTicker := time.NewTicker(100 * time.Millisecond) + // Wait for the plugin to start. + hotReloadEndpoint := fmt.Sprintf("http://localhost:%d", hotReloadPort.Port) + client := rpc.Dial(hotreloadpbconnect.NewHotReloadServiceClient, hotReloadEndpoint, log.Trace) + err = rpc.Wait(ctx, backoff.Backoff{}, time.Minute, client) + if err != nil { + logger.Infof("Dev mode process failed to start") + return fmt.Errorf("timed out waiting for start %w", err) + } + logger.Debugf("Dev mode process started") + + schemaChangeTicker := time.NewTicker(500 * time.Millisecond) defer schemaChangeTicker.Stop() for { select { case <-ctx.Done(): + logger.Debugf("Context done") // the context is done before we notified the build engine // we need to send a build failure event err = stream.Send(&langpb.BuildResponse{Event: &langpb.BuildResponse_BuildFailure{ @@ -403,25 +422,30 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb } return nil case bc := <-events: + logger.Debugf("Build context updated") buildCtx = bc.buildCtx forceUpdate = true - - // Force a hot reload by sending a HEAD request to the dev server - req, err := http.NewRequestWithContext(ctx, http.MethodHead, bind, nil) + result, err := client.Reload(ctx, connect.NewRequest(&hotreloadpb.ReloadRequest{Force: true})) if err != nil { - logger.Errorf(err, "could not create request to force build context update") - continue + return fmt.Errorf("failed to invoke hot reload for build context update %w", err) + } + resp := s.toBuildResponse(result.Msg, &bc.buildCtx, false, devModeEndpoint, debugPort32, hotReloadEndpoint) + if resp != nil { + err = stream.Send(resp) } - resp, err := http.DefaultClient.Do(req) if err != nil { - logger.Errorf(err, "could not send request to force build context update") - continue + return fmt.Errorf("failed to send response %w", err) } - resp.Body.Close() - case <-schemaChangeTicker.C: - changed := false + if !firstAttempt { + logger.Debugf("Calling reload") + _, err := client.Reload(ctx, connect.NewRequest(&hotreloadpb.ReloadRequest{Force: false})) + logger.Debugf("Called reload") + if err != nil { + return fmt.Errorf("failed to invoke hot reload for build context update %w", err) + } + } file, err := os.ReadFile(errorFile) if err == nil { sum := sha256.Sum(file) @@ -438,6 +462,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb schemaHash = sum } } + logger.Debugf("Checking for schema changes") if fileExists(buildCtx.Config.SQLMigrationDirectory) { newMigrationHash, err := watch.ComputeFileHashes(buildCtx.Config.SQLMigrationDirectory, true, []string{"**/*.sql"}) @@ -451,6 +476,7 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb if changed || forceUpdate { auto := !firstAttempt && !forceUpdate if auto { + logger.Infof("sending auto build event") err = stream.Send(&langpb.BuildResponse{Event: &langpb.BuildResponse_AutoRebuildStarted{AutoRebuildStarted: &langpb.AutoRebuildStarted{ContextId: buildCtx.ID}}}) if err != nil { return fmt.Errorf("could not send build event: %w", err) @@ -484,17 +510,19 @@ func (s *Service) runQuarkusDev(ctx context.Context, req *connect.Request[langpb continue } - logger.Infof("Live reload schema changed, sending build success event") + if !firstAttempt { + logger.Infof("Live reload schema changed, sending build success event") + } err = stream.Send(&langpb.BuildResponse{ Event: &langpb.BuildResponse_BuildSuccess{ BuildSuccess: &langpb.BuildSuccess{ - ContextId: buildCtx.ID, - IsAutomaticRebuild: auto, - Module: moduleProto, - DevEndpoint: ptr(fmt.Sprintf("http://localhost:%d", address.Port)), - DevRunnerInfoFile: &runnerInfoFile, - DebugPort: &debugPort32, - Deploy: []string{SchemaFile}, + ContextId: buildCtx.ID, + IsAutomaticRebuild: auto, + Module: moduleProto, + DevEndpoint: ptr(devModeEndpoint), + DevHotReloadEndpoint: ptr(hotReloadEndpoint), + DebugPort: &debugPort32, + Deploy: []string{SchemaFile}, }, }, }) @@ -945,3 +973,31 @@ func (s *Service) writeGenericSchemaFiles(ctx context.Context, v *schema.Schema, } return nil } + +func (s *Service) toBuildResponse(result *hotreloadpb.ReloadResponse, bc *buildContext, auto bool, devEndpoint string, debugPort int32, hotReloadAddress string) (ret *langpb.BuildResponse) { + ret = &langpb.BuildResponse{} + switch e := result.Event.(type) { + case *hotreloadpb.ReloadResponse_ReloadSuccess: + ret.Event = &langpb.BuildResponse_BuildSuccess{ + BuildSuccess: &langpb.BuildSuccess{ + ContextId: bc.ID, + IsAutomaticRebuild: auto, + Module: e.ReloadSuccess.Module, + Errors: e.ReloadSuccess.Errors, + DevEndpoint: &devEndpoint, + DebugPort: &debugPort, + DevHotReloadEndpoint: &hotReloadAddress, + }, + } + + case *hotreloadpb.ReloadResponse_ReloadFailed: + ret.Event = &langpb.BuildResponse_BuildFailure{ + BuildFailure: &langpb.BuildFailure{ + ContextId: bc.ID, + IsAutomaticRebuild: auto, + Errors: e.ReloadFailed.Errors, + }, + } + } + return ret +} diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py new file mode 100644 index 0000000000..5220f45a7d --- /dev/null +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: xyz/block/ftl/hotreload/v1/hotreload.proto +# Protobuf Python Version: 5.29.2 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 2, + '', + 'xyz/block/ftl/hotreload/v1/hotreload.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from xyz.block.ftl.language.v1 import language_pb2 as xyz_dot_block_dot_ftl_dot_language_dot_v1_dot_language__pb2 +from xyz.block.ftl.schema.v1 import schema_pb2 as xyz_dot_block_dot_ftl_dot_schema_dot_v1_dot_schema__pb2 +from xyz.block.ftl.v1 import ftl_pb2 as xyz_dot_block_dot_ftl_dot_v1_dot_ftl__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n*xyz/block/ftl/hotreload/v1/hotreload.proto\x12\x1axyz.block.ftl.hotreload.v1\x1a(xyz/block/ftl/language/v1/language.proto\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"%\n\rReloadRequest\x12\x14\n\x05\x66orce\x18\x01 \x01(\x08R\x05\x66orce\"\x9f\x02\n\x0eReloadResponse\x12_\n\x13reload_not_required\x18\x01 \x01(\x0b\x32-.xyz.block.ftl.hotreload.v1.ReloadNotRequiredH\x00R\x11reloadNotRequired\x12R\n\x0ereload_success\x18\x02 \x01(\x0b\x32).xyz.block.ftl.hotreload.v1.ReloadSuccessH\x00R\rreloadSuccess\x12O\n\rreload_failed\x18\x03 \x01(\x0b\x32(.xyz.block.ftl.hotreload.v1.ReloadFailedH\x00R\x0creloadFailedB\x07\n\x05\x65vent\"\x0e\n\x0cWatchRequest\"\xbd\x01\n\rWatchResponse\x12R\n\x0ereload_success\x18\x01 \x01(\x0b\x32).xyz.block.ftl.hotreload.v1.ReloadSuccessH\x00R\rreloadSuccess\x12O\n\rreload_failed\x18\x02 \x01(\x0b\x32(.xyz.block.ftl.hotreload.v1.ReloadFailedH\x00R\x0creloadFailedB\x07\n\x05\x65vent\"\x91\x01\n\x11RunnerInfoRequest\x12\x18\n\x07\x61\x64\x64ress\x18\x01 \x01(\tR\x07\x61\x64\x64ress\x12\x1e\n\ndeployment\x18\x02 \x01(\tR\ndeployment\x12\x42\n\tdatabases\x18\x03 \x03(\x0b\x32$.xyz.block.ftl.hotreload.v1.DatabaseR\tdatabases\"8\n\x08\x44\x61tabase\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07\x61\x64\x64ress\x18\x02 \x01(\tR\x07\x61\x64\x64ress\"\x14\n\x12RunnerInfoResponse\"\x13\n\x11ReloadNotRequired\"\x86\x01\n\rReloadSuccess\x12\x37\n\x06module\x18\x01 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12<\n\x06\x65rrors\x18\x02 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\"L\n\x0cReloadFailed\x12<\n\x06\x65rrors\x18\x01 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors2\x8c\x03\n\x10HotReloadService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12_\n\x06Reload\x12).xyz.block.ftl.hotreload.v1.ReloadRequest\x1a*.xyz.block.ftl.hotreload.v1.ReloadResponse\x12^\n\x05Watch\x12(.xyz.block.ftl.hotreload.v1.WatchRequest\x1a).xyz.block.ftl.hotreload.v1.WatchResponse0\x01\x12k\n\nRunnerInfo\x12-.xyz.block.ftl.hotreload.v1.RunnerInfoRequest\x1a..xyz.block.ftl.hotreload.v1.RunnerInfoResponseBNP\x01ZJgithub.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1;hotreloadpbb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'xyz.block.ftl.hotreload.v1.hotreload_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'P\001ZJgithub.com/block/ftl/backend/protos/xyz/block/ftl/hotreload/v1;hotreloadpb' + _globals['_HOTRELOADSERVICE'].methods_by_name['Ping']._loaded_options = None + _globals['_HOTRELOADSERVICE'].methods_by_name['Ping']._serialized_options = b'\220\002\001' + _globals['_RELOADREQUEST']._serialized_start=182 + _globals['_RELOADREQUEST']._serialized_end=219 + _globals['_RELOADRESPONSE']._serialized_start=222 + _globals['_RELOADRESPONSE']._serialized_end=509 + _globals['_WATCHREQUEST']._serialized_start=511 + _globals['_WATCHREQUEST']._serialized_end=525 + _globals['_WATCHRESPONSE']._serialized_start=528 + _globals['_WATCHRESPONSE']._serialized_end=717 + _globals['_RUNNERINFOREQUEST']._serialized_start=720 + _globals['_RUNNERINFOREQUEST']._serialized_end=865 + _globals['_DATABASE']._serialized_start=867 + _globals['_DATABASE']._serialized_end=923 + _globals['_RUNNERINFORESPONSE']._serialized_start=925 + _globals['_RUNNERINFORESPONSE']._serialized_end=945 + _globals['_RELOADNOTREQUIRED']._serialized_start=947 + _globals['_RELOADNOTREQUIRED']._serialized_end=966 + _globals['_RELOADSUCCESS']._serialized_start=969 + _globals['_RELOADSUCCESS']._serialized_end=1103 + _globals['_RELOADFAILED']._serialized_start=1105 + _globals['_RELOADFAILED']._serialized_end=1181 + _globals['_HOTRELOADSERVICE']._serialized_start=1184 + _globals['_HOTRELOADSERVICE']._serialized_end=1580 +# @@protoc_insertion_point(module_scope) diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi new file mode 100644 index 0000000000..85a63be648 --- /dev/null +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/hotreload/v1/hotreload_pb2.pyi @@ -0,0 +1,77 @@ +from xyz.block.ftl.language.v1 import language_pb2 as _language_pb2 +from xyz.block.ftl.schema.v1 import schema_pb2 as _schema_pb2 +from xyz.block.ftl.v1 import ftl_pb2 as _ftl_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class ReloadRequest(_message.Message): + __slots__ = ("force",) + FORCE_FIELD_NUMBER: _ClassVar[int] + force: bool + def __init__(self, force: bool = ...) -> None: ... + +class ReloadResponse(_message.Message): + __slots__ = ("reload_not_required", "reload_success", "reload_failed") + RELOAD_NOT_REQUIRED_FIELD_NUMBER: _ClassVar[int] + RELOAD_SUCCESS_FIELD_NUMBER: _ClassVar[int] + RELOAD_FAILED_FIELD_NUMBER: _ClassVar[int] + reload_not_required: ReloadNotRequired + reload_success: ReloadSuccess + reload_failed: ReloadFailed + def __init__(self, reload_not_required: _Optional[_Union[ReloadNotRequired, _Mapping]] = ..., reload_success: _Optional[_Union[ReloadSuccess, _Mapping]] = ..., reload_failed: _Optional[_Union[ReloadFailed, _Mapping]] = ...) -> None: ... + +class WatchRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class WatchResponse(_message.Message): + __slots__ = ("reload_success", "reload_failed") + RELOAD_SUCCESS_FIELD_NUMBER: _ClassVar[int] + RELOAD_FAILED_FIELD_NUMBER: _ClassVar[int] + reload_success: ReloadSuccess + reload_failed: ReloadFailed + def __init__(self, reload_success: _Optional[_Union[ReloadSuccess, _Mapping]] = ..., reload_failed: _Optional[_Union[ReloadFailed, _Mapping]] = ...) -> None: ... + +class RunnerInfoRequest(_message.Message): + __slots__ = ("address", "deployment", "databases") + ADDRESS_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + DATABASES_FIELD_NUMBER: _ClassVar[int] + address: str + deployment: str + databases: _containers.RepeatedCompositeFieldContainer[Database] + def __init__(self, address: _Optional[str] = ..., deployment: _Optional[str] = ..., databases: _Optional[_Iterable[_Union[Database, _Mapping]]] = ...) -> None: ... + +class Database(_message.Message): + __slots__ = ("name", "address") + NAME_FIELD_NUMBER: _ClassVar[int] + ADDRESS_FIELD_NUMBER: _ClassVar[int] + name: str + address: str + def __init__(self, name: _Optional[str] = ..., address: _Optional[str] = ...) -> None: ... + +class RunnerInfoResponse(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class ReloadNotRequired(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class ReloadSuccess(_message.Message): + __slots__ = ("module", "errors") + MODULE_FIELD_NUMBER: _ClassVar[int] + ERRORS_FIELD_NUMBER: _ClassVar[int] + module: _schema_pb2.Module + errors: _language_pb2.ErrorList + def __init__(self, module: _Optional[_Union[_schema_pb2.Module, _Mapping]] = ..., errors: _Optional[_Union[_language_pb2.ErrorList, _Mapping]] = ...) -> None: ... + +class ReloadFailed(_message.Message): + __slots__ = ("errors",) + ERRORS_FIELD_NUMBER: _ClassVar[int] + errors: _language_pb2.ErrorList + def __init__(self, errors: _Optional[_Union[_language_pb2.ErrorList, _Mapping]] = ...) -> None: ... diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.py b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.py index 78895592e7..57c2328990 100644 --- a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.py +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.py @@ -27,7 +27,7 @@ from xyz.block.ftl.v1 import ftl_pb2 as xyz_dot_block_dot_ftl_dot_v1_dot_ftl__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(xyz/block/ftl/language/v1/language.proto\x12\x19xyz.block.ftl.language.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"\x99\x03\n\x0cModuleConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03\x64ir\x18\x02 \x01(\tR\x03\x64ir\x12\x1a\n\x08language\x18\x03 \x01(\tR\x08language\x12\x1d\n\ndeploy_dir\x18\x04 \x01(\tR\tdeployDir\x12\x19\n\x05\x62uild\x18\x05 \x01(\tH\x00R\x05\x62uild\x88\x01\x01\x12)\n\x0e\x64\x65v_mode_build\x18\x06 \x01(\tH\x01R\x0c\x64\x65vModeBuild\x88\x01\x01\x12\x1d\n\nbuild_lock\x18\x07 \x01(\tR\tbuildLock\x12\x14\n\x05watch\x18\t \x03(\tR\x05watch\x12@\n\x0flanguage_config\x18\n \x01(\x0b\x32\x17.google.protobuf.StructR\x0elanguageConfig\x12*\n\x11sql_migration_dir\x18\x0b \x01(\tR\x0fsqlMigrationDir\x12\"\n\rsql_query_dir\x18\x0c \x01(\tR\x0bsqlQueryDirB\x08\n\x06_buildB\x11\n\x0f_dev_mode_build\"d\n\rProjectConfig\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x15\n\x06no_git\x18\x03 \x01(\x08R\x05noGit\x12\x16\n\x06hermit\x18\x04 \x01(\x08R\x06hermit\"9\n\x1bGetCreateModuleFlagsRequest\x12\x1a\n\x08language\x18\x01 \x01(\tR\x08language\"\xcf\x02\n\x1cGetCreateModuleFlagsResponse\x12R\n\x05\x66lags\x18\x01 \x03(\x0b\x32<.xyz.block.ftl.language.v1.GetCreateModuleFlagsResponse.FlagR\x05\x66lags\x1a\xda\x01\n\x04\x46lag\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04help\x18\x02 \x01(\tR\x04help\x12\x19\n\x05\x65nvar\x18\x03 \x01(\tH\x00R\x05\x65nvar\x88\x01\x01\x12\x19\n\x05short\x18\x04 \x01(\tH\x01R\x05short\x88\x01\x01\x12%\n\x0bplaceholder\x18\x05 \x01(\tH\x02R\x0bplaceholder\x88\x01\x01\x12\x1d\n\x07\x64\x65\x66\x61ult\x18\x06 \x01(\tH\x03R\x07\x64\x65\x66\x61ult\x88\x01\x01\x42\x08\n\x06_envarB\x08\n\x06_shortB\x0e\n\x0c_placeholderB\n\n\x08_default\"\xbb\x01\n\x13\x43reateModuleRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03\x64ir\x18\x02 \x01(\tR\x03\x64ir\x12O\n\x0eproject_config\x18\x03 \x01(\x0b\x32(.xyz.block.ftl.language.v1.ProjectConfigR\rprojectConfig\x12-\n\x05\x66lags\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructR\x05\x66lags\"\x16\n\x14\x43reateModuleResponse\"/\n\x1bModuleConfigDefaultsRequest\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\"\xfb\x02\n\x1cModuleConfigDefaultsResponse\x12\x1d\n\ndeploy_dir\x18\x01 \x01(\tR\tdeployDir\x12\x19\n\x05\x62uild\x18\x02 \x01(\tH\x00R\x05\x62uild\x88\x01\x01\x12)\n\x0e\x64\x65v_mode_build\x18\x03 \x01(\tH\x01R\x0c\x64\x65vModeBuild\x88\x01\x01\x12\"\n\nbuild_lock\x18\x04 \x01(\tH\x02R\tbuildLock\x88\x01\x01\x12\x14\n\x05watch\x18\x06 \x03(\tR\x05watch\x12@\n\x0flanguage_config\x18\x07 \x01(\x0b\x32\x17.google.protobuf.StructR\x0elanguageConfig\x12*\n\x11sql_migration_dir\x18\x08 \x01(\tR\x0fsqlMigrationDir\x12\"\n\rsql_query_dir\x18\t \x01(\tR\x0bsqlQueryDirB\x08\n\x06_buildB\x11\n\x0f_dev_mode_buildB\r\n\x0b_build_lock\"f\n\x16GetDependenciesRequest\x12L\n\rmodule_config\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\"3\n\x17GetDependenciesResponse\x12\x18\n\x07modules\x18\x01 \x03(\tR\x07modules\"\xe6\x01\n\x0c\x42uildContext\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12L\n\rmodule_config\x18\x02 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12\x37\n\x06schema\x18\x03 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.SchemaR\x06schema\x12\"\n\x0c\x64\x65pendencies\x18\x04 \x03(\tR\x0c\x64\x65pendencies\x12\x1b\n\tbuild_env\x18\x05 \x03(\tR\x08\x62uildEnv\"j\n\x1a\x42uildContextUpdatedRequest\x12L\n\rbuild_context\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildContextR\x0c\x62uildContext\"\x1d\n\x1b\x42uildContextUpdatedResponse\"\xa4\x03\n\x05\x45rror\x12\x10\n\x03msg\x18\x01 \x01(\tR\x03msg\x12\x41\n\x05level\x18\x04 \x01(\x0e\x32+.xyz.block.ftl.language.v1.Error.ErrorLevelR\x05level\x12:\n\x03pos\x18\x05 \x01(\x0b\x32#.xyz.block.ftl.language.v1.PositionH\x00R\x03pos\x88\x01\x01\x12>\n\x04type\x18\x06 \x01(\x0e\x32*.xyz.block.ftl.language.v1.Error.ErrorTypeR\x04type\"l\n\nErrorLevel\x12\x1b\n\x17\x45RROR_LEVEL_UNSPECIFIED\x10\x00\x12\x14\n\x10\x45RROR_LEVEL_INFO\x10\x01\x12\x14\n\x10\x45RROR_LEVEL_WARN\x10\x02\x12\x15\n\x11\x45RROR_LEVEL_ERROR\x10\x03\"T\n\tErrorType\x12\x1a\n\x16\x45RROR_TYPE_UNSPECIFIED\x10\x00\x12\x12\n\x0e\x45RROR_TYPE_FTL\x10\x01\x12\x17\n\x13\x45RROR_TYPE_COMPILER\x10\x02\x42\x06\n\x04_pos\"|\n\x08Position\x12\x1a\n\x08\x66ilename\x18\x01 \x01(\tR\x08\x66ilename\x12\x12\n\x04line\x18\x02 \x01(\x03R\x04line\x12!\n\x0cstart_column\x18\x03 \x01(\x03R\x0bstartColumn\x12\x1d\n\nend_column\x18\x04 \x01(\x03R\tendColumn\"E\n\tErrorList\x12\x38\n\x06\x65rrors\x18\x01 \x03(\x0b\x32 .xyz.block.ftl.language.v1.ErrorR\x06\x65rrors\"\xd3\x01\n\x0c\x42uildRequest\x12!\n\x0cproject_root\x18\x01 \x01(\tR\x0bprojectRoot\x12\x1d\n\nstubs_root\x18\x02 \x01(\tR\tstubsRoot\x12\x33\n\x15rebuild_automatically\x18\x03 \x01(\x08R\x14rebuildAutomatically\x12L\n\rbuild_context\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildContextR\x0c\x62uildContext\"3\n\x12\x41utoRebuildStarted\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\"\xcc\x03\n\x0c\x42uildSuccess\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\x12\x30\n\x14is_automatic_rebuild\x18\x02 \x01(\x08R\x12isAutomaticRebuild\x12\x37\n\x06module\x18\x03 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12\x16\n\x06\x64\x65ploy\x18\x04 \x03(\tR\x06\x64\x65ploy\x12!\n\x0c\x64ocker_image\x18\x05 \x01(\tR\x0b\x64ockerImage\x12<\n\x06\x65rrors\x18\x06 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\x12&\n\x0c\x64\x65v_endpoint\x18\x07 \x01(\tH\x00R\x0b\x64\x65vEndpoint\x88\x01\x01\x12\"\n\ndebug_port\x18\x08 \x01(\x05H\x01R\tdebugPort\x88\x01\x01\x12\x34\n\x14\x64\x65v_runner_info_file\x18\t \x01(\tH\x02R\x11\x64\x65vRunnerInfoFile\x88\x01\x01\x42\x0f\n\r_dev_endpointB\r\n\x0b_debug_portB\x17\n\x15_dev_runner_info_file\"\xd6\x01\n\x0c\x42uildFailure\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\x12\x30\n\x14is_automatic_rebuild\x18\x02 \x01(\x08R\x12isAutomaticRebuild\x12<\n\x06\x65rrors\x18\x03 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\x12\x37\n\x17invalidate_dependencies\x18\x04 \x01(\x08R\x16invalidateDependencies\"\x9b\x02\n\rBuildResponse\x12\x61\n\x14\x61uto_rebuild_started\x18\x02 \x01(\x0b\x32-.xyz.block.ftl.language.v1.AutoRebuildStartedH\x00R\x12\x61utoRebuildStarted\x12N\n\rbuild_success\x18\x03 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildSuccessH\x00R\x0c\x62uildSuccess\x12N\n\rbuild_failure\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildFailureH\x00R\x0c\x62uildFailureB\x07\n\x05\x65vent\"\xa8\x02\n\x14GenerateStubsRequest\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\x12\x37\n\x06module\x18\x02 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12L\n\rmodule_config\x18\x03 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12^\n\x14native_module_config\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigH\x00R\x12nativeModuleConfig\x88\x01\x01\x42\x17\n\x15_native_module_config\"\x17\n\x15GenerateStubsResponse\"\xdb\x01\n\x19SyncStubReferencesRequest\x12L\n\rmodule_config\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12\x1d\n\nstubs_root\x18\x02 \x01(\tR\tstubsRoot\x12\x18\n\x07modules\x18\x03 \x03(\tR\x07modules\x12\x37\n\x06schema\x18\x04 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.SchemaR\x06schema\"\x1c\n\x1aSyncStubReferencesResponse2\xb9\x08\n\x0fLanguageService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12\x87\x01\n\x14GetCreateModuleFlags\x12\x36.xyz.block.ftl.language.v1.GetCreateModuleFlagsRequest\x1a\x37.xyz.block.ftl.language.v1.GetCreateModuleFlagsResponse\x12o\n\x0c\x43reateModule\x12..xyz.block.ftl.language.v1.CreateModuleRequest\x1a/.xyz.block.ftl.language.v1.CreateModuleResponse\x12\x87\x01\n\x14ModuleConfigDefaults\x12\x36.xyz.block.ftl.language.v1.ModuleConfigDefaultsRequest\x1a\x37.xyz.block.ftl.language.v1.ModuleConfigDefaultsResponse\x12x\n\x0fGetDependencies\x12\x31.xyz.block.ftl.language.v1.GetDependenciesRequest\x1a\x32.xyz.block.ftl.language.v1.GetDependenciesResponse\x12\\\n\x05\x42uild\x12\'.xyz.block.ftl.language.v1.BuildRequest\x1a(.xyz.block.ftl.language.v1.BuildResponse0\x01\x12\x84\x01\n\x13\x42uildContextUpdated\x12\x35.xyz.block.ftl.language.v1.BuildContextUpdatedRequest\x1a\x36.xyz.block.ftl.language.v1.BuildContextUpdatedResponse\x12r\n\rGenerateStubs\x12/.xyz.block.ftl.language.v1.GenerateStubsRequest\x1a\x30.xyz.block.ftl.language.v1.GenerateStubsResponse\x12\x81\x01\n\x12SyncStubReferences\x12\x34.xyz.block.ftl.language.v1.SyncStubReferencesRequest\x1a\x35.xyz.block.ftl.language.v1.SyncStubReferencesResponseBLP\x01ZHgithub.com/block/ftl/backend/protos/xyz/block/ftl/language/v1;languagepbb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(xyz/block/ftl/language/v1/language.proto\x12\x19xyz.block.ftl.language.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a$xyz/block/ftl/schema/v1/schema.proto\x1a\x1axyz/block/ftl/v1/ftl.proto\"\x99\x03\n\x0cModuleConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03\x64ir\x18\x02 \x01(\tR\x03\x64ir\x12\x1a\n\x08language\x18\x03 \x01(\tR\x08language\x12\x1d\n\ndeploy_dir\x18\x04 \x01(\tR\tdeployDir\x12\x19\n\x05\x62uild\x18\x05 \x01(\tH\x00R\x05\x62uild\x88\x01\x01\x12)\n\x0e\x64\x65v_mode_build\x18\x06 \x01(\tH\x01R\x0c\x64\x65vModeBuild\x88\x01\x01\x12\x1d\n\nbuild_lock\x18\x07 \x01(\tR\tbuildLock\x12\x14\n\x05watch\x18\t \x03(\tR\x05watch\x12@\n\x0flanguage_config\x18\n \x01(\x0b\x32\x17.google.protobuf.StructR\x0elanguageConfig\x12*\n\x11sql_migration_dir\x18\x0b \x01(\tR\x0fsqlMigrationDir\x12\"\n\rsql_query_dir\x18\x0c \x01(\tR\x0bsqlQueryDirB\x08\n\x06_buildB\x11\n\x0f_dev_mode_build\"d\n\rProjectConfig\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x15\n\x06no_git\x18\x03 \x01(\x08R\x05noGit\x12\x16\n\x06hermit\x18\x04 \x01(\x08R\x06hermit\"9\n\x1bGetCreateModuleFlagsRequest\x12\x1a\n\x08language\x18\x01 \x01(\tR\x08language\"\xcf\x02\n\x1cGetCreateModuleFlagsResponse\x12R\n\x05\x66lags\x18\x01 \x03(\x0b\x32<.xyz.block.ftl.language.v1.GetCreateModuleFlagsResponse.FlagR\x05\x66lags\x1a\xda\x01\n\x04\x46lag\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04help\x18\x02 \x01(\tR\x04help\x12\x19\n\x05\x65nvar\x18\x03 \x01(\tH\x00R\x05\x65nvar\x88\x01\x01\x12\x19\n\x05short\x18\x04 \x01(\tH\x01R\x05short\x88\x01\x01\x12%\n\x0bplaceholder\x18\x05 \x01(\tH\x02R\x0bplaceholder\x88\x01\x01\x12\x1d\n\x07\x64\x65\x66\x61ult\x18\x06 \x01(\tH\x03R\x07\x64\x65\x66\x61ult\x88\x01\x01\x42\x08\n\x06_envarB\x08\n\x06_shortB\x0e\n\x0c_placeholderB\n\n\x08_default\"\xbb\x01\n\x13\x43reateModuleRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03\x64ir\x18\x02 \x01(\tR\x03\x64ir\x12O\n\x0eproject_config\x18\x03 \x01(\x0b\x32(.xyz.block.ftl.language.v1.ProjectConfigR\rprojectConfig\x12-\n\x05\x66lags\x18\x04 \x01(\x0b\x32\x17.google.protobuf.StructR\x05\x66lags\"\x16\n\x14\x43reateModuleResponse\"/\n\x1bModuleConfigDefaultsRequest\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\"\xfb\x02\n\x1cModuleConfigDefaultsResponse\x12\x1d\n\ndeploy_dir\x18\x01 \x01(\tR\tdeployDir\x12\x19\n\x05\x62uild\x18\x02 \x01(\tH\x00R\x05\x62uild\x88\x01\x01\x12)\n\x0e\x64\x65v_mode_build\x18\x03 \x01(\tH\x01R\x0c\x64\x65vModeBuild\x88\x01\x01\x12\"\n\nbuild_lock\x18\x04 \x01(\tH\x02R\tbuildLock\x88\x01\x01\x12\x14\n\x05watch\x18\x06 \x03(\tR\x05watch\x12@\n\x0flanguage_config\x18\x07 \x01(\x0b\x32\x17.google.protobuf.StructR\x0elanguageConfig\x12*\n\x11sql_migration_dir\x18\x08 \x01(\tR\x0fsqlMigrationDir\x12\"\n\rsql_query_dir\x18\t \x01(\tR\x0bsqlQueryDirB\x08\n\x06_buildB\x11\n\x0f_dev_mode_buildB\r\n\x0b_build_lock\"f\n\x16GetDependenciesRequest\x12L\n\rmodule_config\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\"3\n\x17GetDependenciesResponse\x12\x18\n\x07modules\x18\x01 \x03(\tR\x07modules\"\xe6\x01\n\x0c\x42uildContext\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12L\n\rmodule_config\x18\x02 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12\x37\n\x06schema\x18\x03 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.SchemaR\x06schema\x12\"\n\x0c\x64\x65pendencies\x18\x04 \x03(\tR\x0c\x64\x65pendencies\x12\x1b\n\tbuild_env\x18\x05 \x03(\tR\x08\x62uildEnv\"j\n\x1a\x42uildContextUpdatedRequest\x12L\n\rbuild_context\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildContextR\x0c\x62uildContext\"\x1d\n\x1b\x42uildContextUpdatedResponse\"\xa4\x03\n\x05\x45rror\x12\x10\n\x03msg\x18\x01 \x01(\tR\x03msg\x12\x41\n\x05level\x18\x04 \x01(\x0e\x32+.xyz.block.ftl.language.v1.Error.ErrorLevelR\x05level\x12:\n\x03pos\x18\x05 \x01(\x0b\x32#.xyz.block.ftl.language.v1.PositionH\x00R\x03pos\x88\x01\x01\x12>\n\x04type\x18\x06 \x01(\x0e\x32*.xyz.block.ftl.language.v1.Error.ErrorTypeR\x04type\"l\n\nErrorLevel\x12\x1b\n\x17\x45RROR_LEVEL_UNSPECIFIED\x10\x00\x12\x14\n\x10\x45RROR_LEVEL_INFO\x10\x01\x12\x14\n\x10\x45RROR_LEVEL_WARN\x10\x02\x12\x15\n\x11\x45RROR_LEVEL_ERROR\x10\x03\"T\n\tErrorType\x12\x1a\n\x16\x45RROR_TYPE_UNSPECIFIED\x10\x00\x12\x12\n\x0e\x45RROR_TYPE_FTL\x10\x01\x12\x17\n\x13\x45RROR_TYPE_COMPILER\x10\x02\x42\x06\n\x04_pos\"|\n\x08Position\x12\x1a\n\x08\x66ilename\x18\x01 \x01(\tR\x08\x66ilename\x12\x12\n\x04line\x18\x02 \x01(\x03R\x04line\x12!\n\x0cstart_column\x18\x03 \x01(\x03R\x0bstartColumn\x12\x1d\n\nend_column\x18\x04 \x01(\x03R\tendColumn\"E\n\tErrorList\x12\x38\n\x06\x65rrors\x18\x01 \x03(\x0b\x32 .xyz.block.ftl.language.v1.ErrorR\x06\x65rrors\"\xd3\x01\n\x0c\x42uildRequest\x12!\n\x0cproject_root\x18\x01 \x01(\tR\x0bprojectRoot\x12\x1d\n\nstubs_root\x18\x02 \x01(\tR\tstubsRoot\x12\x33\n\x15rebuild_automatically\x18\x03 \x01(\x08R\x14rebuildAutomatically\x12L\n\rbuild_context\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildContextR\x0c\x62uildContext\"3\n\x12\x41utoRebuildStarted\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\"\xd5\x03\n\x0c\x42uildSuccess\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\x12\x30\n\x14is_automatic_rebuild\x18\x02 \x01(\x08R\x12isAutomaticRebuild\x12\x37\n\x06module\x18\x03 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12\x16\n\x06\x64\x65ploy\x18\x04 \x03(\tR\x06\x64\x65ploy\x12!\n\x0c\x64ocker_image\x18\x05 \x01(\tR\x0b\x64ockerImage\x12<\n\x06\x65rrors\x18\x06 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\x12&\n\x0c\x64\x65v_endpoint\x18\x07 \x01(\tH\x00R\x0b\x64\x65vEndpoint\x88\x01\x01\x12\"\n\ndebug_port\x18\x08 \x01(\x05H\x01R\tdebugPort\x88\x01\x01\x12:\n\x17\x64\x65v_hot_reload_endpoint\x18\t \x01(\tH\x02R\x14\x64\x65vHotReloadEndpoint\x88\x01\x01\x42\x0f\n\r_dev_endpointB\r\n\x0b_debug_portB\x1a\n\x18_dev_hot_reload_endpoint\"\xd6\x01\n\x0c\x42uildFailure\x12\x1d\n\ncontext_id\x18\x01 \x01(\tR\tcontextId\x12\x30\n\x14is_automatic_rebuild\x18\x02 \x01(\x08R\x12isAutomaticRebuild\x12<\n\x06\x65rrors\x18\x03 \x01(\x0b\x32$.xyz.block.ftl.language.v1.ErrorListR\x06\x65rrors\x12\x37\n\x17invalidate_dependencies\x18\x04 \x01(\x08R\x16invalidateDependencies\"\x9b\x02\n\rBuildResponse\x12\x61\n\x14\x61uto_rebuild_started\x18\x02 \x01(\x0b\x32-.xyz.block.ftl.language.v1.AutoRebuildStartedH\x00R\x12\x61utoRebuildStarted\x12N\n\rbuild_success\x18\x03 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildSuccessH\x00R\x0c\x62uildSuccess\x12N\n\rbuild_failure\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.BuildFailureH\x00R\x0c\x62uildFailureB\x07\n\x05\x65vent\"\xa8\x02\n\x14GenerateStubsRequest\x12\x10\n\x03\x64ir\x18\x01 \x01(\tR\x03\x64ir\x12\x37\n\x06module\x18\x02 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.ModuleR\x06module\x12L\n\rmodule_config\x18\x03 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12^\n\x14native_module_config\x18\x04 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigH\x00R\x12nativeModuleConfig\x88\x01\x01\x42\x17\n\x15_native_module_config\"\x17\n\x15GenerateStubsResponse\"\xdb\x01\n\x19SyncStubReferencesRequest\x12L\n\rmodule_config\x18\x01 \x01(\x0b\x32\'.xyz.block.ftl.language.v1.ModuleConfigR\x0cmoduleConfig\x12\x1d\n\nstubs_root\x18\x02 \x01(\tR\tstubsRoot\x12\x18\n\x07modules\x18\x03 \x03(\tR\x07modules\x12\x37\n\x06schema\x18\x04 \x01(\x0b\x32\x1f.xyz.block.ftl.schema.v1.SchemaR\x06schema\"\x1c\n\x1aSyncStubReferencesResponse2\xb9\x08\n\x0fLanguageService\x12J\n\x04Ping\x12\x1d.xyz.block.ftl.v1.PingRequest\x1a\x1e.xyz.block.ftl.v1.PingResponse\"\x03\x90\x02\x01\x12\x87\x01\n\x14GetCreateModuleFlags\x12\x36.xyz.block.ftl.language.v1.GetCreateModuleFlagsRequest\x1a\x37.xyz.block.ftl.language.v1.GetCreateModuleFlagsResponse\x12o\n\x0c\x43reateModule\x12..xyz.block.ftl.language.v1.CreateModuleRequest\x1a/.xyz.block.ftl.language.v1.CreateModuleResponse\x12\x87\x01\n\x14ModuleConfigDefaults\x12\x36.xyz.block.ftl.language.v1.ModuleConfigDefaultsRequest\x1a\x37.xyz.block.ftl.language.v1.ModuleConfigDefaultsResponse\x12x\n\x0fGetDependencies\x12\x31.xyz.block.ftl.language.v1.GetDependenciesRequest\x1a\x32.xyz.block.ftl.language.v1.GetDependenciesResponse\x12\\\n\x05\x42uild\x12\'.xyz.block.ftl.language.v1.BuildRequest\x1a(.xyz.block.ftl.language.v1.BuildResponse0\x01\x12\x84\x01\n\x13\x42uildContextUpdated\x12\x35.xyz.block.ftl.language.v1.BuildContextUpdatedRequest\x1a\x36.xyz.block.ftl.language.v1.BuildContextUpdatedResponse\x12r\n\rGenerateStubs\x12/.xyz.block.ftl.language.v1.GenerateStubsRequest\x1a\x30.xyz.block.ftl.language.v1.GenerateStubsResponse\x12\x81\x01\n\x12SyncStubReferences\x12\x34.xyz.block.ftl.language.v1.SyncStubReferencesRequest\x1a\x35.xyz.block.ftl.language.v1.SyncStubReferencesResponseBLP\x01ZHgithub.com/block/ftl/backend/protos/xyz/block/ftl/language/v1;languagepbb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -80,19 +80,19 @@ _globals['_AUTOREBUILDSTARTED']._serialized_start=3086 _globals['_AUTOREBUILDSTARTED']._serialized_end=3137 _globals['_BUILDSUCCESS']._serialized_start=3140 - _globals['_BUILDSUCCESS']._serialized_end=3600 - _globals['_BUILDFAILURE']._serialized_start=3603 - _globals['_BUILDFAILURE']._serialized_end=3817 - _globals['_BUILDRESPONSE']._serialized_start=3820 - _globals['_BUILDRESPONSE']._serialized_end=4103 - _globals['_GENERATESTUBSREQUEST']._serialized_start=4106 - _globals['_GENERATESTUBSREQUEST']._serialized_end=4402 - _globals['_GENERATESTUBSRESPONSE']._serialized_start=4404 - _globals['_GENERATESTUBSRESPONSE']._serialized_end=4427 - _globals['_SYNCSTUBREFERENCESREQUEST']._serialized_start=4430 - _globals['_SYNCSTUBREFERENCESREQUEST']._serialized_end=4649 - _globals['_SYNCSTUBREFERENCESRESPONSE']._serialized_start=4651 - _globals['_SYNCSTUBREFERENCESRESPONSE']._serialized_end=4679 - _globals['_LANGUAGESERVICE']._serialized_start=4682 - _globals['_LANGUAGESERVICE']._serialized_end=5763 + _globals['_BUILDSUCCESS']._serialized_end=3609 + _globals['_BUILDFAILURE']._serialized_start=3612 + _globals['_BUILDFAILURE']._serialized_end=3826 + _globals['_BUILDRESPONSE']._serialized_start=3829 + _globals['_BUILDRESPONSE']._serialized_end=4112 + _globals['_GENERATESTUBSREQUEST']._serialized_start=4115 + _globals['_GENERATESTUBSREQUEST']._serialized_end=4411 + _globals['_GENERATESTUBSRESPONSE']._serialized_start=4413 + _globals['_GENERATESTUBSRESPONSE']._serialized_end=4436 + _globals['_SYNCSTUBREFERENCESREQUEST']._serialized_start=4439 + _globals['_SYNCSTUBREFERENCESREQUEST']._serialized_end=4658 + _globals['_SYNCSTUBREFERENCESRESPONSE']._serialized_start=4660 + _globals['_SYNCSTUBREFERENCESRESPONSE']._serialized_end=4688 + _globals['_LANGUAGESERVICE']._serialized_start=4691 + _globals['_LANGUAGESERVICE']._serialized_end=5772 # @@protoc_insertion_point(module_scope) diff --git a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.pyi b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.pyi index 107171c3fb..513afae05e 100644 --- a/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.pyi +++ b/python-runtime/ftl/src/ftl/protos/xyz/block/ftl/language/v1/language_pb2.pyi @@ -219,7 +219,7 @@ class AutoRebuildStarted(_message.Message): def __init__(self, context_id: _Optional[str] = ...) -> None: ... class BuildSuccess(_message.Message): - __slots__ = ("context_id", "is_automatic_rebuild", "module", "deploy", "docker_image", "errors", "dev_endpoint", "debug_port", "dev_runner_info_file") + __slots__ = ("context_id", "is_automatic_rebuild", "module", "deploy", "docker_image", "errors", "dev_endpoint", "debug_port", "dev_hot_reload_endpoint") CONTEXT_ID_FIELD_NUMBER: _ClassVar[int] IS_AUTOMATIC_REBUILD_FIELD_NUMBER: _ClassVar[int] MODULE_FIELD_NUMBER: _ClassVar[int] @@ -228,7 +228,7 @@ class BuildSuccess(_message.Message): ERRORS_FIELD_NUMBER: _ClassVar[int] DEV_ENDPOINT_FIELD_NUMBER: _ClassVar[int] DEBUG_PORT_FIELD_NUMBER: _ClassVar[int] - DEV_RUNNER_INFO_FILE_FIELD_NUMBER: _ClassVar[int] + DEV_HOT_RELOAD_ENDPOINT_FIELD_NUMBER: _ClassVar[int] context_id: str is_automatic_rebuild: bool module: _schema_pb2.Module @@ -237,8 +237,8 @@ class BuildSuccess(_message.Message): errors: ErrorList dev_endpoint: str debug_port: int - dev_runner_info_file: str - def __init__(self, context_id: _Optional[str] = ..., is_automatic_rebuild: bool = ..., module: _Optional[_Union[_schema_pb2.Module, _Mapping]] = ..., deploy: _Optional[_Iterable[str]] = ..., docker_image: _Optional[str] = ..., errors: _Optional[_Union[ErrorList, _Mapping]] = ..., dev_endpoint: _Optional[str] = ..., debug_port: _Optional[int] = ..., dev_runner_info_file: _Optional[str] = ...) -> None: ... + dev_hot_reload_endpoint: str + def __init__(self, context_id: _Optional[str] = ..., is_automatic_rebuild: bool = ..., module: _Optional[_Union[_schema_pb2.Module, _Mapping]] = ..., deploy: _Optional[_Iterable[str]] = ..., docker_image: _Optional[str] = ..., errors: _Optional[_Union[ErrorList, _Mapping]] = ..., dev_endpoint: _Optional[str] = ..., debug_port: _Optional[int] = ..., dev_hot_reload_endpoint: _Optional[str] = ...) -> None: ... class BuildFailure(_message.Message): __slots__ = ("context_id", "is_automatic_rebuild", "errors", "invalidate_dependencies")