feat: track long running generic commands in the oplog (#516)

This commit is contained in:
Gareth
2024-10-12 13:38:28 -07:00
committed by GitHub
parent 31ff0b7002
commit ada778320e
23 changed files with 642 additions and 445 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.32.0
// protoc v5.27.2
// protoc (unknown)
// source: v1/config.proto
package v1
+233 -138
View File
@@ -218,6 +218,7 @@ type Operation struct {
// *Operation_OperationStats
// *Operation_OperationRunHook
// *Operation_OperationCheck
// *Operation_OperationRunCommand
Op isOperation_Op `protobuf_oneof:"op"`
}
@@ -393,6 +394,13 @@ func (x *Operation) GetOperationCheck() *OperationCheck {
return nil
}
func (x *Operation) GetOperationRunCommand() *OperationRunCommand {
if x, ok := x.GetOp().(*Operation_OperationRunCommand); ok {
return x.OperationRunCommand
}
return nil
}
type isOperation_Op interface {
isOperation_Op()
}
@@ -429,6 +437,10 @@ type Operation_OperationCheck struct {
OperationCheck *OperationCheck `protobuf:"bytes,107,opt,name=operation_check,json=operationCheck,proto3,oneof"`
}
type Operation_OperationRunCommand struct {
OperationRunCommand *OperationRunCommand `protobuf:"bytes,108,opt,name=operation_run_command,json=operationRunCommand,proto3,oneof"`
}
func (*Operation_OperationBackup) isOperation_Op() {}
func (*Operation_OperationIndexSnapshot) isOperation_Op() {}
@@ -445,6 +457,8 @@ func (*Operation_OperationRunHook) isOperation_Op() {}
func (*Operation_OperationCheck) isOperation_Op() {}
func (*Operation_OperationRunCommand) isOperation_Op() {}
// OperationEvent is used in the wireformat to stream operation changes to clients
type OperationEvent struct {
state protoimpl.MessageState
@@ -838,6 +852,62 @@ func (x *OperationCheck) GetOutputLogref() string {
return ""
}
// OperationRunCommand tracks a long running command. Commands are grouped into a flow ID for each session.
type OperationRunCommand struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Command string `protobuf:"bytes,1,opt,name=command,proto3" json:"command,omitempty"`
OutputLogref string `protobuf:"bytes,2,opt,name=output_logref,json=outputLogref,proto3" json:"output_logref,omitempty"`
}
func (x *OperationRunCommand) Reset() {
*x = OperationRunCommand{}
if protoimpl.UnsafeEnabled {
mi := &file_v1_operations_proto_msgTypes[8]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *OperationRunCommand) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*OperationRunCommand) ProtoMessage() {}
func (x *OperationRunCommand) ProtoReflect() protoreflect.Message {
mi := &file_v1_operations_proto_msgTypes[8]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use OperationRunCommand.ProtoReflect.Descriptor instead.
func (*OperationRunCommand) Descriptor() ([]byte, []int) {
return file_v1_operations_proto_rawDescGZIP(), []int{8}
}
func (x *OperationRunCommand) GetCommand() string {
if x != nil {
return x.Command
}
return ""
}
func (x *OperationRunCommand) GetOutputLogref() string {
if x != nil {
return x.OutputLogref
}
return ""
}
// OperationRestore tracks a restore operation.
type OperationRestore struct {
state protoimpl.MessageState
@@ -852,7 +922,7 @@ type OperationRestore struct {
func (x *OperationRestore) Reset() {
*x = OperationRestore{}
if protoimpl.UnsafeEnabled {
mi := &file_v1_operations_proto_msgTypes[8]
mi := &file_v1_operations_proto_msgTypes[9]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -865,7 +935,7 @@ func (x *OperationRestore) String() string {
func (*OperationRestore) ProtoMessage() {}
func (x *OperationRestore) ProtoReflect() protoreflect.Message {
mi := &file_v1_operations_proto_msgTypes[8]
mi := &file_v1_operations_proto_msgTypes[9]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -878,7 +948,7 @@ func (x *OperationRestore) ProtoReflect() protoreflect.Message {
// Deprecated: Use OperationRestore.ProtoReflect.Descriptor instead.
func (*OperationRestore) Descriptor() ([]byte, []int) {
return file_v1_operations_proto_rawDescGZIP(), []int{8}
return file_v1_operations_proto_rawDescGZIP(), []int{9}
}
func (x *OperationRestore) GetPath() string {
@@ -914,7 +984,7 @@ type OperationStats struct {
func (x *OperationStats) Reset() {
*x = OperationStats{}
if protoimpl.UnsafeEnabled {
mi := &file_v1_operations_proto_msgTypes[9]
mi := &file_v1_operations_proto_msgTypes[10]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -927,7 +997,7 @@ func (x *OperationStats) String() string {
func (*OperationStats) ProtoMessage() {}
func (x *OperationStats) ProtoReflect() protoreflect.Message {
mi := &file_v1_operations_proto_msgTypes[9]
mi := &file_v1_operations_proto_msgTypes[10]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -940,7 +1010,7 @@ func (x *OperationStats) ProtoReflect() protoreflect.Message {
// Deprecated: Use OperationStats.ProtoReflect.Descriptor instead.
func (*OperationStats) Descriptor() ([]byte, []int) {
return file_v1_operations_proto_rawDescGZIP(), []int{9}
return file_v1_operations_proto_rawDescGZIP(), []int{10}
}
func (x *OperationStats) GetStats() *RepoStats {
@@ -965,7 +1035,7 @@ type OperationRunHook struct {
func (x *OperationRunHook) Reset() {
*x = OperationRunHook{}
if protoimpl.UnsafeEnabled {
mi := &file_v1_operations_proto_msgTypes[10]
mi := &file_v1_operations_proto_msgTypes[11]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -978,7 +1048,7 @@ func (x *OperationRunHook) String() string {
func (*OperationRunHook) ProtoMessage() {}
func (x *OperationRunHook) ProtoReflect() protoreflect.Message {
mi := &file_v1_operations_proto_msgTypes[10]
mi := &file_v1_operations_proto_msgTypes[11]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -991,7 +1061,7 @@ func (x *OperationRunHook) ProtoReflect() protoreflect.Message {
// Deprecated: Use OperationRunHook.ProtoReflect.Descriptor instead.
func (*OperationRunHook) Descriptor() ([]byte, []int) {
return file_v1_operations_proto_rawDescGZIP(), []int{10}
return file_v1_operations_proto_rawDescGZIP(), []int{11}
}
func (x *OperationRunHook) GetParentOp() int64 {
@@ -1033,7 +1103,7 @@ var file_v1_operations_proto_rawDesc = []byte{
0x0a, 0x0d, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x12,
0x2d, 0x0a, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x52, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x96,
0x6f, 0x6e, 0x52, 0x0a, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0xe5,
0x07, 0x0a, 0x09, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07,
0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x66,
@@ -1091,98 +1161,108 @@ var file_v1_operations_proto_rawDesc = []byte{
0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x6b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x76, 0x31,
0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x48,
0x00, 0x52, 0x0e, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x65, 0x63,
0x6b, 0x42, 0x04, 0x0a, 0x02, 0x6f, 0x70, 0x22, 0x93, 0x02, 0x0a, 0x0e, 0x4f, 0x70, 0x65, 0x72,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2d, 0x0a, 0x0a, 0x6b, 0x65,
0x65, 0x70, 0x5f, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c,
0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x09,
0x6b, 0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x42, 0x0a, 0x12, 0x63, 0x72, 0x65,
0x61, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61,
0x74, 0x65, 0x64, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a,
0x12, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x4f,
0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x11,
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x73, 0x12, 0x41, 0x0a, 0x12, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x65,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e,
0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x4c, 0x69, 0x73, 0x74, 0x48,
0x00, 0x52, 0x11, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x7c, 0x0a,
0x0f, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70,
0x12, 0x38, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18,
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75,
0x70, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a,
0x6c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2f, 0x0a, 0x06, 0x65, 0x72,
0x72, 0x6f, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x76, 0x31, 0x2e,
0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x45, 0x72,
0x72, 0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x60, 0x0a, 0x16, 0x4f,
0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x6e, 0x61,
0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x2e, 0x0a, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f,
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73,
0x74, 0x69, 0x63, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x08, 0x73, 0x6e, 0x61,
0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x67, 0x6f, 0x74, 0x18,
0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x67, 0x6f, 0x74, 0x22, 0x6a, 0x0a,
0x0f, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x67, 0x65, 0x74,
0x12, 0x2a, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x69, 0x63, 0x53, 0x6e, 0x61, 0x70,
0x73, 0x68, 0x6f, 0x74, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2b, 0x0a, 0x06,
0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76,
0x31, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63,
0x79, 0x52, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x51, 0x0a, 0x0e, 0x4f, 0x70, 0x65,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x12, 0x1a, 0x0a, 0x06, 0x6f,
0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52,
0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x75, 0x74, 0x70, 0x75,
0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4c, 0x6f, 0x67, 0x72, 0x65, 0x66, 0x22, 0x51, 0x0a, 0x0e,
0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1a,
0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02,
0x18, 0x01, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x75,
0x74, 0x70, 0x75, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x0c, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4c, 0x6f, 0x67, 0x72, 0x65, 0x66, 0x22,
0x79, 0x0a, 0x10, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x74,
0x6f, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65,
0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12,
0x39, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72,
0x65, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a,
0x6c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x35, 0x0a, 0x0e, 0x4f, 0x70,
0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x05,
0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x76, 0x31,
0x2e, 0x52, 0x65, 0x70, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74,
0x73, 0x22, 0x9a, 0x01, 0x0a, 0x10, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x75, 0x6e, 0x48, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74,
0x5f, 0x6f, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x70, 0x61, 0x72, 0x65, 0x6e,
0x74, 0x4f, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x75, 0x74, 0x70, 0x75,
0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4c, 0x6f, 0x67, 0x72, 0x65, 0x66, 0x12, 0x30, 0x0a, 0x09,
0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32,
0x12, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x6f, 0x6b, 0x2e, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74,
0x69, 0x6f, 0x6e, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x2a, 0x60,
0x0a, 0x12, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74,
0x54, 0x79, 0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x55, 0x4e,
0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x56, 0x45, 0x4e, 0x54,
0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x56,
0x45, 0x4e, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x11, 0x0a,
0x0d, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x03,
0x2a, 0xc2, 0x01, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74,
0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55,
0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54,
0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x15, 0x0a, 0x11,
0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x4e, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53,
0x53, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x55,
0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x55,
0x53, 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x07, 0x12, 0x10, 0x0a, 0x0c, 0x53,
0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x04, 0x12, 0x1b, 0x0a,
0x17, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x43,
0x41, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x05, 0x12, 0x19, 0x0a, 0x15, 0x53, 0x54,
0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c,
0x4c, 0x45, 0x44, 0x10, 0x06, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65,
0x2f, 0x62, 0x61, 0x63, 0x6b, 0x72, 0x65, 0x73, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f,
0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x6b, 0x12, 0x4d, 0x0a, 0x15, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72,
0x75, 0x6e, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x6c, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52,
0x75, 0x6e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x48, 0x00, 0x52, 0x13, 0x6f, 0x70, 0x65,
0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
0x42, 0x04, 0x0a, 0x02, 0x6f, 0x70, 0x22, 0x93, 0x02, 0x0a, 0x0e, 0x4f, 0x70, 0x65, 0x72, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2d, 0x0a, 0x0a, 0x6b, 0x65, 0x65,
0x70, 0x5f, 0x61, 0x6c, 0x69, 0x76, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e,
0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x48, 0x00, 0x52, 0x09, 0x6b,
0x65, 0x65, 0x70, 0x41, 0x6c, 0x69, 0x76, 0x65, 0x12, 0x42, 0x0a, 0x12, 0x63, 0x72, 0x65, 0x61,
0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x11, 0x63, 0x72, 0x65, 0x61, 0x74,
0x65, 0x64, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x42, 0x0a, 0x12,
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x76, 0x31, 0x2e, 0x4f, 0x70,
0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00, 0x52, 0x11, 0x75,
0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
0x12, 0x41, 0x0a, 0x12, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x65, 0x72,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74,
0x79, 0x70, 0x65, 0x73, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x4c, 0x69, 0x73, 0x74, 0x48, 0x00,
0x52, 0x11, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x7c, 0x0a, 0x0f,
0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x12,
0x38, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70,
0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x6c,
0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x2f, 0x0a, 0x06, 0x65, 0x72, 0x72,
0x6f, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x42,
0x61, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x45, 0x72, 0x72,
0x6f, 0x72, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x60, 0x0a, 0x16, 0x4f, 0x70,
0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x53, 0x6e, 0x61, 0x70,
0x73, 0x68, 0x6f, 0x74, 0x12, 0x2e, 0x0a, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x74,
0x69, 0x63, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x08, 0x73, 0x6e, 0x61, 0x70,
0x73, 0x68, 0x6f, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x67, 0x6f, 0x74, 0x18, 0x03,
0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x67, 0x6f, 0x74, 0x22, 0x6a, 0x0a, 0x0f,
0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x12,
0x2a, 0x0a, 0x06, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x12, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x69, 0x63, 0x53, 0x6e, 0x61, 0x70, 0x73,
0x68, 0x6f, 0x74, 0x52, 0x06, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x12, 0x2b, 0x0a, 0x06, 0x70,
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x76, 0x31,
0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79,
0x52, 0x06, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x51, 0x0a, 0x0e, 0x4f, 0x70, 0x65, 0x72,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x72, 0x75, 0x6e, 0x65, 0x12, 0x1a, 0x0a, 0x06, 0x6f, 0x75,
0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06,
0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74,
0x5f, 0x6c, 0x6f, 0x67, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f,
0x75, 0x74, 0x70, 0x75, 0x74, 0x4c, 0x6f, 0x67, 0x72, 0x65, 0x66, 0x22, 0x51, 0x0a, 0x0e, 0x4f,
0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x1a, 0x0a,
0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18,
0x01, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x6f, 0x75, 0x74,
0x70, 0x75, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0c, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4c, 0x6f, 0x67, 0x72, 0x65, 0x66, 0x22, 0x54,
0x0a, 0x13, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6e, 0x43, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12,
0x23, 0x0a, 0x0d, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x72, 0x65, 0x66,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4c, 0x6f,
0x67, 0x72, 0x65, 0x66, 0x22, 0x79, 0x0a, 0x10, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
0x6e, 0x52, 0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06,
0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x61,
0x72, 0x67, 0x65, 0x74, 0x12, 0x39, 0x0a, 0x0b, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x74, 0x61,
0x74, 0x75, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x52,
0x65, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x45, 0x6e,
0x74, 0x72, 0x79, 0x52, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22,
0x35, 0x0a, 0x0e, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74,
0x73, 0x12, 0x23, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x0d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x73, 0x52,
0x05, 0x73, 0x74, 0x61, 0x74, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x10, 0x4f, 0x70, 0x65, 0x72, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x52, 0x75, 0x6e, 0x48, 0x6f, 0x6f, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x70,
0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x6f, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08,
0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a, 0x0d,
0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x72, 0x65, 0x66, 0x18, 0x02, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0c, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4c, 0x6f, 0x67, 0x72, 0x65,
0x66, 0x12, 0x30, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x6f, 0x6b, 0x2e, 0x43,
0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74,
0x69, 0x6f, 0x6e, 0x2a, 0x60, 0x0a, 0x12, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x56, 0x45,
0x4e, 0x54, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d,
0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x44, 0x10, 0x01, 0x12,
0x11, 0x0a, 0x0d, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x44,
0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x5f, 0x44, 0x45, 0x4c, 0x45,
0x54, 0x45, 0x44, 0x10, 0x03, 0x2a, 0xc2, 0x01, 0x0a, 0x0f, 0x4f, 0x70, 0x65, 0x72, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41,
0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x12, 0x0a,
0x0e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x45, 0x4e, 0x44, 0x49, 0x4e, 0x47, 0x10,
0x01, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x49, 0x4e, 0x50, 0x52,
0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54,
0x55, 0x53, 0x5f, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x03, 0x12, 0x12, 0x0a, 0x0e,
0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x57, 0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x07,
0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52,
0x10, 0x04, 0x12, 0x1b, 0x0a, 0x17, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x53, 0x59, 0x53,
0x54, 0x45, 0x4d, 0x5f, 0x43, 0x41, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x05, 0x12,
0x19, 0x0a, 0x15, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x53, 0x45, 0x52, 0x5f, 0x43,
0x41, 0x4e, 0x43, 0x45, 0x4c, 0x4c, 0x45, 0x44, 0x10, 0x06, 0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, 0x68, 0x67,
0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x72, 0x65, 0x73, 0x74, 0x2f, 0x67,
0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -1198,7 +1278,7 @@ func file_v1_operations_proto_rawDescGZIP() []byte {
}
var file_v1_operations_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_v1_operations_proto_msgTypes = make([]protoimpl.MessageInfo, 11)
var file_v1_operations_proto_msgTypes = make([]protoimpl.MessageInfo, 12)
var file_v1_operations_proto_goTypes = []interface{}{
(OperationEventType)(0), // 0: v1.OperationEventType
(OperationStatus)(0), // 1: v1.OperationStatus
@@ -1210,18 +1290,19 @@ var file_v1_operations_proto_goTypes = []interface{}{
(*OperationForget)(nil), // 7: v1.OperationForget
(*OperationPrune)(nil), // 8: v1.OperationPrune
(*OperationCheck)(nil), // 9: v1.OperationCheck
(*OperationRestore)(nil), // 10: v1.OperationRestore
(*OperationStats)(nil), // 11: v1.OperationStats
(*OperationRunHook)(nil), // 12: v1.OperationRunHook
(*types.Empty)(nil), // 13: types.Empty
(*types.Int64List)(nil), // 14: types.Int64List
(*BackupProgressEntry)(nil), // 15: v1.BackupProgressEntry
(*BackupProgressError)(nil), // 16: v1.BackupProgressError
(*ResticSnapshot)(nil), // 17: v1.ResticSnapshot
(*RetentionPolicy)(nil), // 18: v1.RetentionPolicy
(*RestoreProgressEntry)(nil), // 19: v1.RestoreProgressEntry
(*RepoStats)(nil), // 20: v1.RepoStats
(Hook_Condition)(0), // 21: v1.Hook.Condition
(*OperationRunCommand)(nil), // 10: v1.OperationRunCommand
(*OperationRestore)(nil), // 11: v1.OperationRestore
(*OperationStats)(nil), // 12: v1.OperationStats
(*OperationRunHook)(nil), // 13: v1.OperationRunHook
(*types.Empty)(nil), // 14: types.Empty
(*types.Int64List)(nil), // 15: types.Int64List
(*BackupProgressEntry)(nil), // 16: v1.BackupProgressEntry
(*BackupProgressError)(nil), // 17: v1.BackupProgressError
(*ResticSnapshot)(nil), // 18: v1.ResticSnapshot
(*RetentionPolicy)(nil), // 19: v1.RetentionPolicy
(*RestoreProgressEntry)(nil), // 20: v1.RestoreProgressEntry
(*RepoStats)(nil), // 21: v1.RepoStats
(Hook_Condition)(0), // 22: v1.Hook.Condition
}
var file_v1_operations_proto_depIdxs = []int32{
3, // 0: v1.OperationList.operations:type_name -> v1.Operation
@@ -1230,27 +1311,28 @@ var file_v1_operations_proto_depIdxs = []int32{
6, // 3: v1.Operation.operation_index_snapshot:type_name -> v1.OperationIndexSnapshot
7, // 4: v1.Operation.operation_forget:type_name -> v1.OperationForget
8, // 5: v1.Operation.operation_prune:type_name -> v1.OperationPrune
10, // 6: v1.Operation.operation_restore:type_name -> v1.OperationRestore
11, // 7: v1.Operation.operation_stats:type_name -> v1.OperationStats
12, // 8: v1.Operation.operation_run_hook:type_name -> v1.OperationRunHook
11, // 6: v1.Operation.operation_restore:type_name -> v1.OperationRestore
12, // 7: v1.Operation.operation_stats:type_name -> v1.OperationStats
13, // 8: v1.Operation.operation_run_hook:type_name -> v1.OperationRunHook
9, // 9: v1.Operation.operation_check:type_name -> v1.OperationCheck
13, // 10: v1.OperationEvent.keep_alive:type_name -> types.Empty
2, // 11: v1.OperationEvent.created_operations:type_name -> v1.OperationList
2, // 12: v1.OperationEvent.updated_operations:type_name -> v1.OperationList
14, // 13: v1.OperationEvent.deleted_operations:type_name -> types.Int64List
15, // 14: v1.OperationBackup.last_status:type_name -> v1.BackupProgressEntry
16, // 15: v1.OperationBackup.errors:type_name -> v1.BackupProgressError
17, // 16: v1.OperationIndexSnapshot.snapshot:type_name -> v1.ResticSnapshot
17, // 17: v1.OperationForget.forget:type_name -> v1.ResticSnapshot
18, // 18: v1.OperationForget.policy:type_name -> v1.RetentionPolicy
19, // 19: v1.OperationRestore.last_status:type_name -> v1.RestoreProgressEntry
20, // 20: v1.OperationStats.stats:type_name -> v1.RepoStats
21, // 21: v1.OperationRunHook.condition:type_name -> v1.Hook.Condition
22, // [22:22] is the sub-list for method output_type
22, // [22:22] is the sub-list for method input_type
22, // [22:22] is the sub-list for extension type_name
22, // [22:22] is the sub-list for extension extendee
0, // [0:22] is the sub-list for field type_name
10, // 10: v1.Operation.operation_run_command:type_name -> v1.OperationRunCommand
14, // 11: v1.OperationEvent.keep_alive:type_name -> types.Empty
2, // 12: v1.OperationEvent.created_operations:type_name -> v1.OperationList
2, // 13: v1.OperationEvent.updated_operations:type_name -> v1.OperationList
15, // 14: v1.OperationEvent.deleted_operations:type_name -> types.Int64List
16, // 15: v1.OperationBackup.last_status:type_name -> v1.BackupProgressEntry
17, // 16: v1.OperationBackup.errors:type_name -> v1.BackupProgressError
18, // 17: v1.OperationIndexSnapshot.snapshot:type_name -> v1.ResticSnapshot
18, // 18: v1.OperationForget.forget:type_name -> v1.ResticSnapshot
19, // 19: v1.OperationForget.policy:type_name -> v1.RetentionPolicy
20, // 20: v1.OperationRestore.last_status:type_name -> v1.RestoreProgressEntry
21, // 21: v1.OperationStats.stats:type_name -> v1.RepoStats
22, // 22: v1.OperationRunHook.condition:type_name -> v1.Hook.Condition
23, // [23:23] is the sub-list for method output_type
23, // [23:23] is the sub-list for method input_type
23, // [23:23] is the sub-list for extension type_name
23, // [23:23] is the sub-list for extension extendee
0, // [0:23] is the sub-list for field type_name
}
func init() { file_v1_operations_proto_init() }
@@ -1358,7 +1440,7 @@ func file_v1_operations_proto_init() {
}
}
file_v1_operations_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*OperationRestore); i {
switch v := v.(*OperationRunCommand); i {
case 0:
return &v.state
case 1:
@@ -1370,7 +1452,7 @@ func file_v1_operations_proto_init() {
}
}
file_v1_operations_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*OperationStats); i {
switch v := v.(*OperationRestore); i {
case 0:
return &v.state
case 1:
@@ -1382,6 +1464,18 @@ func file_v1_operations_proto_init() {
}
}
file_v1_operations_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*OperationStats); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_v1_operations_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*OperationRunHook); i {
case 0:
return &v.state
@@ -1403,6 +1497,7 @@ func file_v1_operations_proto_init() {
(*Operation_OperationStats)(nil),
(*Operation_OperationRunHook)(nil),
(*Operation_OperationCheck)(nil),
(*Operation_OperationRunCommand)(nil),
}
file_v1_operations_proto_msgTypes[2].OneofWrappers = []interface{}{
(*OperationEvent_KeepAlive)(nil),
@@ -1416,7 +1511,7 @@ func file_v1_operations_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_v1_operations_proto_rawDesc,
NumEnums: 2,
NumMessages: 11,
NumMessages: 12,
NumExtensions: 0,
NumServices: 0,
},
+20 -20
View File
@@ -961,7 +961,7 @@ var file_v1_service_proto_rawDesc = []byte{
0x74, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x65, 0x70, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x70, 0x6f, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f,
0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d,
0x6d, 0x61, 0x6e, 0x64, 0x32, 0xf9, 0x07, 0x0a, 0x08, 0x42, 0x61, 0x63, 0x6b, 0x72, 0x65, 0x73,
0x6d, 0x61, 0x6e, 0x64, 0x32, 0xf7, 0x07, 0x0a, 0x08, 0x42, 0x61, 0x63, 0x6b, 0x72, 0x65, 0x73,
0x74, 0x12, 0x31, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66,
@@ -1009,26 +1009,26 @@ var file_v1_service_proto_rawDesc = []byte{
0x79, 0x22, 0x00, 0x12, 0x34, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x12,
0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x44, 0x61, 0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x73,
0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x3a, 0x0a, 0x0a, 0x52, 0x75, 0x6e,
0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x38, 0x0a, 0x0a, 0x52, 0x75, 0x6e,
0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x15, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x75, 0x6e,
0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x11,
0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x39, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x77, 0x6e,
0x6c, 0x6f, 0x61, 0x64, 0x55, 0x52, 0x4c, 0x12, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e,
0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x12, 0x2e, 0x74, 0x79, 0x70,
0x65, 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00,
0x12, 0x41, 0x0a, 0x0c, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79,
0x12, 0x17, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x48, 0x69, 0x73, 0x74, 0x6f,
0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74,
0x79, 0x22, 0x00, 0x12, 0x3b, 0x0a, 0x10, 0x50, 0x61, 0x74, 0x68, 0x41, 0x75, 0x74, 0x6f, 0x63,
0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x12, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e,
0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x11, 0x2e, 0x74, 0x79,
0x70, 0x65, 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00,
0x42, 0x2c, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67,
0x61, 0x72, 0x65, 0x74, 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x62, 0x61, 0x63, 0x6b,
0x72, 0x65, 0x73, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75,
0x65, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x77, 0x6e, 0x6c, 0x6f,
0x61, 0x64, 0x55, 0x52, 0x4c, 0x12, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x49, 0x6e,
0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x12, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73,
0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x00, 0x12, 0x41,
0x0a, 0x0c, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x17,
0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22,
0x00, 0x12, 0x3b, 0x0a, 0x10, 0x50, 0x61, 0x74, 0x68, 0x41, 0x75, 0x74, 0x6f, 0x63, 0x6f, 0x6d,
0x70, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x12, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x53, 0x74,
0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x1a, 0x11, 0x2e, 0x74, 0x79, 0x70, 0x65,
0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x42, 0x2c,
0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72,
0x65, 0x74, 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x62, 0x61, 0x63, 0x6b, 0x72, 0x65,
0x73, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -1105,7 +1105,7 @@ var file_v1_service_proto_depIdxs = []int32{
13, // 31: v1.Backrest.Restore:output_type -> google.protobuf.Empty
13, // 32: v1.Backrest.Cancel:output_type -> google.protobuf.Empty
21, // 33: v1.Backrest.GetLogs:output_type -> types.BytesValue
21, // 34: v1.Backrest.RunCommand:output_type -> types.BytesValue
17, // 34: v1.Backrest.RunCommand:output_type -> types.Int64Value
16, // 35: v1.Backrest.GetDownloadURL:output_type -> types.StringValue
13, // 36: v1.Backrest.ClearHistory:output_type -> google.protobuf.Empty
22, // 37: v1.Backrest.PathAutocomplete:output_type -> types.StringList
+27 -54
View File
@@ -64,7 +64,7 @@ type BackrestClient interface {
// GetLogs returns the keyed large data for the given operation.
GetLogs(ctx context.Context, in *LogDataRequest, opts ...grpc.CallOption) (Backrest_GetLogsClient, error)
// RunCommand executes a generic restic command on the repository.
RunCommand(ctx context.Context, in *RunCommandRequest, opts ...grpc.CallOption) (Backrest_RunCommandClient, error)
RunCommand(ctx context.Context, in *RunCommandRequest, opts ...grpc.CallOption) (*types.Int64Value, error)
// GetDownloadURL returns a signed download URL given a forget operation ID.
GetDownloadURL(ctx context.Context, in *types.Int64Value, opts ...grpc.CallOption) (*types.StringValue, error)
// Clears the history of operations
@@ -244,36 +244,13 @@ func (x *backrestGetLogsClient) Recv() (*types.BytesValue, error) {
return m, nil
}
func (c *backrestClient) RunCommand(ctx context.Context, in *RunCommandRequest, opts ...grpc.CallOption) (Backrest_RunCommandClient, error) {
stream, err := c.cc.NewStream(ctx, &Backrest_ServiceDesc.Streams[2], Backrest_RunCommand_FullMethodName, opts...)
func (c *backrestClient) RunCommand(ctx context.Context, in *RunCommandRequest, opts ...grpc.CallOption) (*types.Int64Value, error) {
out := new(types.Int64Value)
err := c.cc.Invoke(ctx, Backrest_RunCommand_FullMethodName, in, out, opts...)
if err != nil {
return nil, err
}
x := &backrestRunCommandClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type Backrest_RunCommandClient interface {
Recv() (*types.BytesValue, error)
grpc.ClientStream
}
type backrestRunCommandClient struct {
grpc.ClientStream
}
func (x *backrestRunCommandClient) Recv() (*types.BytesValue, error) {
m := new(types.BytesValue)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
return out, nil
}
func (c *backrestClient) GetDownloadURL(ctx context.Context, in *types.Int64Value, opts ...grpc.CallOption) (*types.StringValue, error) {
@@ -327,7 +304,7 @@ type BackrestServer interface {
// GetLogs returns the keyed large data for the given operation.
GetLogs(*LogDataRequest, Backrest_GetLogsServer) error
// RunCommand executes a generic restic command on the repository.
RunCommand(*RunCommandRequest, Backrest_RunCommandServer) error
RunCommand(context.Context, *RunCommandRequest) (*types.Int64Value, error)
// GetDownloadURL returns a signed download URL given a forget operation ID.
GetDownloadURL(context.Context, *types.Int64Value) (*types.StringValue, error)
// Clears the history of operations
@@ -380,8 +357,8 @@ func (UnimplementedBackrestServer) Cancel(context.Context, *types.Int64Value) (*
func (UnimplementedBackrestServer) GetLogs(*LogDataRequest, Backrest_GetLogsServer) error {
return status.Errorf(codes.Unimplemented, "method GetLogs not implemented")
}
func (UnimplementedBackrestServer) RunCommand(*RunCommandRequest, Backrest_RunCommandServer) error {
return status.Errorf(codes.Unimplemented, "method RunCommand not implemented")
func (UnimplementedBackrestServer) RunCommand(context.Context, *RunCommandRequest) (*types.Int64Value, error) {
return nil, status.Errorf(codes.Unimplemented, "method RunCommand not implemented")
}
func (UnimplementedBackrestServer) GetDownloadURL(context.Context, *types.Int64Value) (*types.StringValue, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetDownloadURL not implemented")
@@ -645,25 +622,22 @@ func (x *backrestGetLogsServer) Send(m *types.BytesValue) error {
return x.ServerStream.SendMsg(m)
}
func _Backrest_RunCommand_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(RunCommandRequest)
if err := stream.RecvMsg(m); err != nil {
return err
func _Backrest_RunCommand_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(RunCommandRequest)
if err := dec(in); err != nil {
return nil, err
}
return srv.(BackrestServer).RunCommand(m, &backrestRunCommandServer{stream})
}
type Backrest_RunCommandServer interface {
Send(*types.BytesValue) error
grpc.ServerStream
}
type backrestRunCommandServer struct {
grpc.ServerStream
}
func (x *backrestRunCommandServer) Send(m *types.BytesValue) error {
return x.ServerStream.SendMsg(m)
if interceptor == nil {
return srv.(BackrestServer).RunCommand(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Backrest_RunCommand_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BackrestServer).RunCommand(ctx, req.(*RunCommandRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Backrest_GetDownloadURL_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
@@ -771,6 +745,10 @@ var Backrest_ServiceDesc = grpc.ServiceDesc{
MethodName: "Cancel",
Handler: _Backrest_Cancel_Handler,
},
{
MethodName: "RunCommand",
Handler: _Backrest_RunCommand_Handler,
},
{
MethodName: "GetDownloadURL",
Handler: _Backrest_GetDownloadURL_Handler,
@@ -795,11 +773,6 @@ var Backrest_ServiceDesc = grpc.ServiceDesc{
Handler: _Backrest_GetLogs_Handler,
ServerStreams: true,
},
{
StreamName: "RunCommand",
Handler: _Backrest_RunCommand_Handler,
ServerStreams: true,
},
},
Metadata: "v1/service.proto",
}
+9 -9
View File
@@ -118,7 +118,7 @@ type BackrestClient interface {
// GetLogs returns the keyed large data for the given operation.
GetLogs(context.Context, *connect.Request[v1.LogDataRequest]) (*connect.ServerStreamForClient[types.BytesValue], error)
// RunCommand executes a generic restic command on the repository.
RunCommand(context.Context, *connect.Request[v1.RunCommandRequest]) (*connect.ServerStreamForClient[types.BytesValue], error)
RunCommand(context.Context, *connect.Request[v1.RunCommandRequest]) (*connect.Response[types.Int64Value], error)
// GetDownloadURL returns a signed download URL given a forget operation ID.
GetDownloadURL(context.Context, *connect.Request[types.Int64Value]) (*connect.Response[types.StringValue], error)
// Clears the history of operations
@@ -215,7 +215,7 @@ func NewBackrestClient(httpClient connect.HTTPClient, baseURL string, opts ...co
connect.WithSchema(backrestGetLogsMethodDescriptor),
connect.WithClientOptions(opts...),
),
runCommand: connect.NewClient[v1.RunCommandRequest, types.BytesValue](
runCommand: connect.NewClient[v1.RunCommandRequest, types.Int64Value](
httpClient,
baseURL+BackrestRunCommandProcedure,
connect.WithSchema(backrestRunCommandMethodDescriptor),
@@ -257,7 +257,7 @@ type backrestClient struct {
restore *connect.Client[v1.RestoreSnapshotRequest, emptypb.Empty]
cancel *connect.Client[types.Int64Value, emptypb.Empty]
getLogs *connect.Client[v1.LogDataRequest, types.BytesValue]
runCommand *connect.Client[v1.RunCommandRequest, types.BytesValue]
runCommand *connect.Client[v1.RunCommandRequest, types.Int64Value]
getDownloadURL *connect.Client[types.Int64Value, types.StringValue]
clearHistory *connect.Client[v1.ClearHistoryRequest, emptypb.Empty]
pathAutocomplete *connect.Client[types.StringValue, types.StringList]
@@ -329,8 +329,8 @@ func (c *backrestClient) GetLogs(ctx context.Context, req *connect.Request[v1.Lo
}
// RunCommand calls v1.Backrest.RunCommand.
func (c *backrestClient) RunCommand(ctx context.Context, req *connect.Request[v1.RunCommandRequest]) (*connect.ServerStreamForClient[types.BytesValue], error) {
return c.runCommand.CallServerStream(ctx, req)
func (c *backrestClient) RunCommand(ctx context.Context, req *connect.Request[v1.RunCommandRequest]) (*connect.Response[types.Int64Value], error) {
return c.runCommand.CallUnary(ctx, req)
}
// GetDownloadURL calls v1.Backrest.GetDownloadURL.
@@ -370,7 +370,7 @@ type BackrestHandler interface {
// GetLogs returns the keyed large data for the given operation.
GetLogs(context.Context, *connect.Request[v1.LogDataRequest], *connect.ServerStream[types.BytesValue]) error
// RunCommand executes a generic restic command on the repository.
RunCommand(context.Context, *connect.Request[v1.RunCommandRequest], *connect.ServerStream[types.BytesValue]) error
RunCommand(context.Context, *connect.Request[v1.RunCommandRequest]) (*connect.Response[types.Int64Value], error)
// GetDownloadURL returns a signed download URL given a forget operation ID.
GetDownloadURL(context.Context, *connect.Request[types.Int64Value]) (*connect.Response[types.StringValue], error)
// Clears the history of operations
@@ -463,7 +463,7 @@ func NewBackrestHandler(svc BackrestHandler, opts ...connect.HandlerOption) (str
connect.WithSchema(backrestGetLogsMethodDescriptor),
connect.WithHandlerOptions(opts...),
)
backrestRunCommandHandler := connect.NewServerStreamHandler(
backrestRunCommandHandler := connect.NewUnaryHandler(
BackrestRunCommandProcedure,
svc.RunCommand,
connect.WithSchema(backrestRunCommandMethodDescriptor),
@@ -584,8 +584,8 @@ func (UnimplementedBackrestHandler) GetLogs(context.Context, *connect.Request[v1
return connect.NewError(connect.CodeUnimplemented, errors.New("v1.Backrest.GetLogs is not implemented"))
}
func (UnimplementedBackrestHandler) RunCommand(context.Context, *connect.Request[v1.RunCommandRequest], *connect.ServerStream[types.BytesValue]) error {
return connect.NewError(connect.CodeUnimplemented, errors.New("v1.Backrest.RunCommand is not implemented"))
func (UnimplementedBackrestHandler) RunCommand(context.Context, *connect.Request[v1.RunCommandRequest]) (*connect.Response[types.Int64Value], error) {
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1.Backrest.RunCommand is not implemented"))
}
func (UnimplementedBackrestHandler) GetDownloadURL(context.Context, *connect.Request[types.Int64Value]) (*connect.Response[types.StringValue], error) {
+17 -62
View File
@@ -429,73 +429,28 @@ func (s *BackrestHandler) Restore(ctx context.Context, req *connect.Request[v1.R
return connect.NewResponse(&emptypb.Empty{}), nil
}
func (s *BackrestHandler) RunCommand(ctx context.Context, req *connect.Request[v1.RunCommandRequest], resp *connect.ServerStream[types.BytesValue]) error {
repo, err := s.orchestrator.GetRepoOrchestrator(req.Msg.RepoId)
if err != nil {
return fmt.Errorf("failed to get repo %q: %w", req.Msg.RepoId, err)
}
ctx, cancel := context.WithCancel(ctx)
outputs := make(chan []byte, 100)
errChan := make(chan error, 1)
go func() {
start := time.Now()
zap.S().Infof("running command for webui: %v", req.Msg.Command)
if err := repo.RunCommand(ctx, req.Msg.Command, func(output []byte) {
outputs <- bytes.Clone(output)
}); err != nil && ctx.Err() == nil {
zap.S().Errorf("error running command for webui: %v", err)
errChan <- err
} else {
zap.S().Infof("command completed for webui: %v", time.Since(start))
}
outputs <- []byte("took " + time.Since(start).String())
cancel()
}()
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
bufSize := 32 * 1024
buf := make([]byte, 0, bufSize)
flush := func() error {
if len(buf) > 0 {
if err := resp.Send(&types.BytesValue{Value: buf}); err != nil {
return fmt.Errorf("failed to write output: %w", err)
}
buf = buf[:0]
func (s *BackrestHandler) RunCommand(ctx context.Context, req *connect.Request[v1.RunCommandRequest]) (*connect.Response[types.Int64Value], error) {
// group commands within the last 24 hours (or 256 operations) into the same flow ID
var flowID int64
if s.oplog.Query(oplog.Query{RepoID: req.Msg.RepoId, Limit: 256, Reversed: true}, func(op *v1.Operation) error {
if op.GetOperationRunCommand() != nil && time.Since(time.UnixMilli(op.UnixTimeStartMs)) < 30*time.Minute {
flowID = op.FlowId
}
return nil
}) != nil {
return nil, fmt.Errorf("failed to query operations")
}
for {
select {
case err := <-errChan:
if err := flush(); err != nil {
return err
}
return err
case <-ctx.Done():
return flush()
case output := <-outputs:
if len(output)+len(buf) > bufSize {
flush()
}
if len(output) > bufSize {
if err := resp.Send(&types.BytesValue{Value: output}); err != nil {
return fmt.Errorf("failed to write output: %w", err)
}
continue
}
buf = append(buf, output...)
case <-ticker.C:
if len(buf) > 0 {
flush()
}
}
task := tasks.NewOneoffRunCommandTask(req.Msg.RepoId, tasks.PlanForSystemTasks, flowID, time.Now(), req.Msg.Command)
st, err := s.orchestrator.CreateUnscheduledTask(task, tasks.TaskPriorityInteractive, time.Now())
if err != nil {
return nil, fmt.Errorf("failed to create task: %w", err)
}
if err := s.orchestrator.RunTask(context.Background(), st); err != nil {
return nil, fmt.Errorf("failed to run command: %w", err)
}
return connect.NewResponse(&types.Int64Value{Value: st.Op.GetId()}), nil
}
func (s *BackrestHandler) Cancel(ctx context.Context, req *connect.Request[types.Int64Value]) (*connect.Response[emptypb.Empty], error) {
+62
View File
@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"io"
"io/fs"
"os"
"path"
@@ -764,6 +765,67 @@ func TestRestore(t *testing.T) {
}
}
func TestRunCommand(t *testing.T) {
sut := createSystemUnderTest(t, &config.MemoryStore{
Config: &v1.Config{
Modno: 1234,
Instance: "test",
Repos: []*v1.Repo{
{
Id: "local",
Uri: t.TempDir(),
Password: "test",
Flags: []string{"--no-cache"},
},
},
},
})
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
res, err := sut.handler.RunCommand(ctx, connect.NewRequest(&v1.RunCommandRequest{
RepoId: "local",
Command: "help",
}))
if err != nil {
t.Fatalf("RunCommand() error = %v", err)
}
op, err := sut.oplog.Get(res.Msg.Value)
if err != nil {
t.Fatalf("Failed to find runcommand operation: %v", err)
}
if op.Status != v1.OperationStatus_STATUS_SUCCESS {
t.Fatalf("Expected runcommand operation to succeed")
}
cmdOp := op.GetOperationRunCommand()
if cmdOp == nil {
t.Fatalf("Expected runcommand operation to be of type OperationRunCommand")
}
if cmdOp.Command != "help" {
t.Fatalf("Expected runcommand operation to have correct command")
}
if cmdOp.OutputLogref == "" {
t.Fatalf("Expected runcommand operation to have output logref")
}
log, err := sut.logStore.Open(cmdOp.OutputLogref)
if err != nil {
t.Fatalf("Failed to open log: %v", err)
}
defer log.Close()
data, err := io.ReadAll(log)
if err != nil {
t.Fatalf("Failed to read log: %v", err)
}
if !strings.Contains(string(data), "Usage") {
t.Fatalf("Expected log output to contain help text")
}
}
type systemUnderTest struct {
handler *BackrestHandler
oplog *oplog.OpLog
-1
View File
@@ -427,7 +427,6 @@ func (w *writer) Close() error {
}
}
w.ls.trackingMu.Unlock()
w.ls.maybeReleaseTempFile(w.fname)
})
+58 -67
View File
@@ -38,8 +38,8 @@ type Orchestrator struct {
taskQueue *queue.TimePriorityQueue[stContainer]
logStore *logstore.LogStore
// cancelNotify is a list of channels that are notified when a task should be cancelled.
cancelNotify []chan int64
taskCancelMu sync.Mutex
taskCancel map[int64]context.CancelFunc
// now for the purpose of testing; used by Run() to get the current time.
now func() time.Time
@@ -70,9 +70,10 @@ func NewOrchestrator(resticBin string, cfg *v1.Config, log *oplog.OpLog, logStor
OpLog: log,
config: cfg,
// repoPool created with a memory store to ensure the config is updated in an atomic operation with the repo pool's config value.
repoPool: newResticRepoPool(resticBin, cfg),
taskQueue: queue.NewTimePriorityQueue[stContainer](),
logStore: logStore,
repoPool: newResticRepoPool(resticBin, cfg),
taskQueue: queue.NewTimePriorityQueue[stContainer](),
logStore: logStore,
taskCancel: make(map[int64]context.CancelFunc),
}
// verify the operation log and mark any incomplete operations as failed.
@@ -166,10 +167,8 @@ func (o *Orchestrator) ScheduleDefaultTasks(config *v1.Config) error {
continue
}
if err := o.cancelHelper(t.Op, v1.OperationStatus_STATUS_SYSTEM_CANCELLED); err != nil {
zap.L().Error("failed to cancel queued task", zap.String("task", t.Task.Name()), zap.Error(err))
} else {
zap.L().Debug("queued task cancelled due to config change", zap.String("task", t.Task.Name()))
if err := o.OpLog.Delete(t.Op.Id); err != nil {
zap.S().Warnf("failed to cancel pending task %d: %v", t.Op.Id, err)
}
}
@@ -239,14 +238,11 @@ func (o *Orchestrator) GetPlan(planID string) (*v1.Plan, error) {
}
func (o *Orchestrator) CancelOperation(operationId int64, status v1.OperationStatus) error {
o.mu.Lock()
for _, c := range o.cancelNotify {
select {
case c <- operationId:
default:
}
o.taskCancelMu.Lock()
if cancel, ok := o.taskCancel[operationId]; ok {
cancel()
}
o.mu.Unlock()
o.taskCancelMu.Unlock()
allTasks := o.taskQueue.GetAll()
idx := slices.IndexFunc(allTasks, func(t stContainer) bool {
@@ -262,9 +258,15 @@ func (o *Orchestrator) CancelOperation(operationId int64, status v1.OperationSta
}
o.taskQueue.Remove(t)
if err := o.scheduleTaskHelper(t.Task, tasks.TaskPriorityDefault, t.RunAt); err != nil {
if st, err := o.CreateUnscheduledTask(t.Task, tasks.TaskPriorityDefault, t.RunAt); err != nil {
return fmt.Errorf("reschedule cancelled task: %w", err)
} else if !st.Eq(tasks.NeverScheduledTask) {
o.taskQueue.Enqueue(st.RunAt, tasks.TaskPriorityDefault, stContainer{
ScheduledTask: st,
configModno: o.config.Modno,
})
}
return nil
}
@@ -281,20 +283,6 @@ func (o *Orchestrator) cancelHelper(op *v1.Operation, status v1.OperationStatus)
func (o *Orchestrator) Run(ctx context.Context) {
zap.L().Info("starting orchestrator loop")
// subscribe to cancel notifications.
o.mu.Lock()
cancelNotifyChan := make(chan int64, 10) // buffered to queue up cancel notifications.
o.cancelNotify = append(o.cancelNotify, cancelNotifyChan)
o.mu.Unlock()
defer func() {
o.mu.Lock()
if idx := slices.Index(o.cancelNotify, cancelNotifyChan); idx != -1 {
o.cancelNotify = slices.Delete(o.cancelNotify, idx, idx+1)
}
o.mu.Unlock()
}()
for {
if ctx.Err() != nil {
zap.L().Info("shutting down orchestrator loop, context cancelled.")
@@ -306,20 +294,6 @@ func (o *Orchestrator) Run(ctx context.Context) {
continue
}
taskCtx, cancelTaskCtx := context.WithCancel(ctx)
go func() {
for {
select {
case <-taskCtx.Done():
return
case opID := <-cancelNotifyChan:
if t.Op != nil && opID == t.Op.GetId() {
cancelTaskCtx()
}
}
}
}()
// Clone the operation incase we need to reset changes and reschedule the task for a retry
originalOp := proto.Clone(t.Op).(*v1.Operation)
if t.Op != nil && t.retryCount != 0 {
@@ -340,7 +314,7 @@ func (o *Orchestrator) Run(ctx context.Context) {
}
}
err := o.RunTask(taskCtx, t.ScheduledTask)
err := o.RunTask(ctx, t.ScheduledTask)
o.mu.Lock()
curCfgModno := o.config.Modno
@@ -372,8 +346,6 @@ func (o *Orchestrator) Run(ctx context.Context) {
zap.L().Error("reschedule task", zap.String("task", t.Task.Name()), zap.Error(e))
}
}
cancelTaskCtx()
for _, cb := range t.callbacks {
go cb(err)
}
@@ -381,11 +353,22 @@ func (o *Orchestrator) Run(ctx context.Context) {
}
func (o *Orchestrator) RunTask(ctx context.Context, st tasks.ScheduledTask) error {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
zap.L().Info("running task", zap.String("task", st.Task.Name()), zap.String("runAt", st.RunAt.Format(time.RFC3339)))
var logWriter io.WriteCloser
op := st.Op
if op != nil {
var err error
o.taskCancelMu.Lock()
o.taskCancel[op.Id] = cancel
o.taskCancelMu.Unlock()
defer func() {
o.taskCancelMu.Lock()
delete(o.taskCancel, op.Id)
o.taskCancelMu.Unlock()
}()
randBytes := make([]byte, 8)
if _, err := rand.Read(randBytes); err != nil {
@@ -465,41 +448,49 @@ func (o *Orchestrator) RunTask(ctx context.Context, st tasks.ScheduledTask) erro
// ScheduleTask schedules a task to run at the next available time.
// note that o.mu must not be held when calling this function.
func (o *Orchestrator) ScheduleTask(t tasks.Task, priority int, callbacks ...func(error)) error {
return o.scheduleTaskHelper(t, priority, o.curTime(), callbacks...)
}
func (o *Orchestrator) scheduleTaskHelper(t tasks.Task, priority int, curTime time.Time, callbacks ...func(error)) error {
nextRun, err := t.Next(curTime, newTaskRunnerImpl(o, t, nil))
nextRun, err := o.CreateUnscheduledTask(t, priority, o.curTime())
if err != nil {
return fmt.Errorf("finding run time for task %q: %w", t.Name(), err)
return err
}
if nextRun.Eq(tasks.NeverScheduledTask) {
return nil
}
nextRun.Task = t
stc := stContainer{
ScheduledTask: nextRun,
configModno: o.config.Modno,
callbacks: callbacks,
}
if stc.Op != nil {
stc.Op.InstanceId = o.config.Instance
stc.Op.PlanId = t.PlanID()
stc.Op.RepoId = t.RepoID()
stc.Op.Status = v1.OperationStatus_STATUS_PENDING
stc.Op.UnixTimeStartMs = nextRun.RunAt.UnixMilli()
if err := o.OpLog.Add(nextRun.Op); err != nil {
return fmt.Errorf("add operation to oplog: %w", err)
}
}
o.taskQueue.Enqueue(nextRun.RunAt, priority, stc)
zap.L().Info("scheduled task", zap.String("task", t.Name()), zap.String("runAt", nextRun.RunAt.Format(time.RFC3339)))
return nil
}
func (o *Orchestrator) CreateUnscheduledTask(t tasks.Task, priority int, curTime time.Time) (tasks.ScheduledTask, error) {
nextRun, err := t.Next(curTime, newTaskRunnerImpl(o, t, nil))
if err != nil {
return tasks.NeverScheduledTask, fmt.Errorf("finding run time for task %q: %w", t.Name(), err)
}
if nextRun.Eq(tasks.NeverScheduledTask) {
return tasks.NeverScheduledTask, nil
}
nextRun.Task = t
if nextRun.Op != nil {
nextRun.Op.InstanceId = o.config.Instance
nextRun.Op.PlanId = t.PlanID()
nextRun.Op.RepoId = t.RepoID()
nextRun.Op.Status = v1.OperationStatus_STATUS_PENDING
nextRun.Op.UnixTimeStartMs = nextRun.RunAt.UnixMilli()
if err := o.OpLog.Add(nextRun.Op); err != nil {
return tasks.NeverScheduledTask, fmt.Errorf("add operation to oplog: %w", err)
}
}
return nextRun, nil
}
func (o *Orchestrator) Config() *v1.Config {
o.mu.Lock()
defer o.mu.Unlock()
+2 -12
View File
@@ -389,7 +389,7 @@ func (r *RepoOrchestrator) AddTags(ctx context.Context, snapshotIDs []string, ta
}
// RunCommand runs a command in the repo's environment. Output is buffered and sent to the onProgress callback in batches.
func (r *RepoOrchestrator) RunCommand(ctx context.Context, command string, onProgress func([]byte)) error {
func (r *RepoOrchestrator) RunCommand(ctx context.Context, command string, writer io.Writer) error {
r.mu.Lock()
defer r.mu.Unlock()
ctx, flush := forwardResticLogs(ctx)
@@ -401,8 +401,7 @@ func (r *RepoOrchestrator) RunCommand(ctx context.Context, command string, onPro
return fmt.Errorf("parse command: %w", err)
}
ctx = restic.ContextWithLogger(ctx, &callbackWriter{callback: onProgress})
ctx = restic.ContextWithLogger(ctx, writer)
return r.repo.GenericCommand(ctx, args)
}
@@ -425,12 +424,3 @@ func chunkBy[T any](items []T, chunkSize int) (chunks [][]T) {
}
return append(chunks, items)
}
type callbackWriter struct {
callback func([]byte) // note: callback must not retain the byte slice
}
func (w *callbackWriter) Write(p []byte) (n int, err error) {
w.callback(p)
return len(p), nil
}
+3 -4
View File
@@ -104,10 +104,9 @@ func (t *taskRunnerImpl) ExecuteHooks(ctx context.Context, events []v1.Hook_Cond
}
for _, task := range hookTasks {
st, _ := task.Next(time.Now(), t)
st.Task = task
if err := t.OpLog().Add(st.Op); err != nil {
return fmt.Errorf("%v: %w", task.Name(), err)
st, err := t.orchestrator.CreateUnscheduledTask(task, tasks.TaskPriorityDefault, time.Now())
if err != nil {
return fmt.Errorf("creating task for hook: %w", err)
}
if err := t.orchestrator.RunTask(ctx, st); hook.IsHaltingError(err) {
var cancelErr *hook.HookErrorRequestCancel
@@ -0,0 +1,68 @@
package tasks
import (
"context"
"fmt"
"time"
v1 "github.com/garethgeorge/backrest/gen/go/v1"
)
func NewOneoffRunCommandTask(repoID string, planID string, flowID int64, at time.Time, command string) Task {
return &GenericOneoffTask{
OneoffTask: OneoffTask{
BaseTask: BaseTask{
TaskType: "forget_snapshot",
TaskName: fmt.Sprintf("run command in repo %q", repoID),
TaskRepoID: repoID,
TaskPlanID: planID,
},
FlowID: flowID,
RunAt: at,
ProtoOp: &v1.Operation{
Op: &v1.Operation_OperationRunCommand{
OperationRunCommand: &v1.OperationRunCommand{
Command: command,
},
},
},
},
Do: func(ctx context.Context, st ScheduledTask, taskRunner TaskRunner) error {
op := st.Op
rc := op.GetOperationRunCommand()
if rc == nil {
panic("forget task with non-forget operation")
}
return runCommandHelper(ctx, st, taskRunner, command)
},
}
}
func runCommandHelper(ctx context.Context, st ScheduledTask, taskRunner TaskRunner, command string) error {
t := st.Task
repo, err := taskRunner.GetRepoOrchestrator(t.RepoID())
if err != nil {
return fmt.Errorf("get repo %q: %w", t.RepoID(), err)
}
id, writer, err := taskRunner.LogrefWriter()
if err != nil {
return fmt.Errorf("get logref writer: %w", err)
}
st.Op.GetOperationRunCommand().OutputLogref = id
if err := taskRunner.UpdateOperation(st.Op); err != nil {
return fmt.Errorf("update operation: %w", err)
}
if err := repo.RunCommand(ctx, command, writer); err != nil {
return fmt.Errorf("command %q: %w", command, err)
}
if err := writer.Close(); err != nil {
return fmt.Errorf("close logref writer: %w", err)
}
return err
}
+7
View File
@@ -41,6 +41,7 @@ message Operation {
OperationStats operation_stats = 105;
OperationRunHook operation_run_hook = 106;
OperationCheck operation_check = 107;
OperationRunCommand operation_run_command = 108;
}
}
@@ -102,6 +103,12 @@ message OperationCheck {
string output_logref = 2; // logref of the check output.
}
// OperationRunCommand tracks a long running command. Commands are grouped into a flow ID for each session.
message OperationRunCommand {
string command = 1;
string output_logref = 2;
}
// OperationRestore tracks a restore operation.
message OperationRestore {
string path = 1; // path in the snapshot to restore.
+1 -1
View File
@@ -45,7 +45,7 @@ service Backrest {
rpc GetLogs(LogDataRequest) returns (stream types.BytesValue) {}
// RunCommand executes a generic restic command on the repository.
rpc RunCommand(RunCommandRequest) returns (stream types.BytesValue) {}
rpc RunCommand(RunCommandRequest) returns (types.Int64Value) {}
// GetDownloadURL returns a signed download URL given a forget operation ID.
rpc GetDownloadURL(types.Int64Value) returns (types.StringValue) {}
+52
View File
@@ -278,6 +278,12 @@ export class Operation extends Message<Operation> {
*/
value: OperationCheck;
case: "operationCheck";
} | {
/**
* @generated from field: v1.OperationRunCommand operation_run_command = 108;
*/
value: OperationRunCommand;
case: "operationRunCommand";
} | { case: undefined; value?: undefined } = { case: undefined };
constructor(data?: PartialMessage<Operation>) {
@@ -307,6 +313,7 @@ export class Operation extends Message<Operation> {
{ no: 105, name: "operation_stats", kind: "message", T: OperationStats, oneof: "op" },
{ no: 106, name: "operation_run_hook", kind: "message", T: OperationRunHook, oneof: "op" },
{ no: 107, name: "operation_check", kind: "message", T: OperationCheck, oneof: "op" },
{ no: 108, name: "operation_run_command", kind: "message", T: OperationRunCommand, oneof: "op" },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Operation {
@@ -629,6 +636,51 @@ export class OperationCheck extends Message<OperationCheck> {
}
}
/**
* OperationRunCommand tracks a long running command. Commands are grouped into a flow ID for each session.
*
* @generated from message v1.OperationRunCommand
*/
export class OperationRunCommand extends Message<OperationRunCommand> {
/**
* @generated from field: string command = 1;
*/
command = "";
/**
* @generated from field: string output_logref = 2;
*/
outputLogref = "";
constructor(data?: PartialMessage<OperationRunCommand>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "v1.OperationRunCommand";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "command", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 2, name: "output_logref", kind: "scalar", T: 9 /* ScalarType.STRING */ },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): OperationRunCommand {
return new OperationRunCommand().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): OperationRunCommand {
return new OperationRunCommand().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): OperationRunCommand {
return new OperationRunCommand().fromJsonString(jsonString, options);
}
static equals(a: OperationRunCommand | PlainMessage<OperationRunCommand> | undefined, b: OperationRunCommand | PlainMessage<OperationRunCommand> | undefined): boolean {
return proto3.util.equals(OperationRunCommand, a, b);
}
}
/**
* OperationRestore tracks a restore operation.
*
+2 -2
View File
@@ -153,8 +153,8 @@ export const Backrest = {
runCommand: {
name: "RunCommand",
I: RunCommandRequest,
O: BytesValue,
kind: MethodKind.ServerStreaming,
O: Int64Value,
kind: MethodKind.Unary,
},
/**
* GetDownloadURL returns a signed download URL given a forget operation ID.
+19 -8
View File
@@ -1,16 +1,12 @@
import React, { useEffect, useState } from "react";
import { LogDataRequest } from "../../gen/ts/v1/service_pb";
import { backrestService } from "../api";
import AutoSizer from "react-virtualized/dist/commonjs/AutoSizer";
import List from "react-virtualized/dist/commonjs/List";
import { set } from "lodash";
import { Button } from "antd";
// TODO: refactor this to use the provider pattern
export const LogView = ({ logref }: { logref: string }) => {
const [lines, setLines] = useState<string[]>([""]);
console.log("LogView", logref);
const [limit, setLimit] = useState(100);
useEffect(() => {
if (!logref) {
@@ -47,7 +43,10 @@ export const LogView = ({ logref }: { logref: string }) => {
};
}, [logref]);
console.log("LogView", lines);
let displayLines = lines;
if (lines.length > limit) {
displayLines = lines.slice(0, limit);
}
return (
<div
@@ -56,7 +55,7 @@ export const LogView = ({ logref }: { logref: string }) => {
width: "100%",
}}
>
{lines.map((line, i) => (
{displayLines.map((line, i) => (
<pre
style={{ margin: "0px", whiteSpace: "pre", overflow: "visible" }}
key={i}
@@ -64,6 +63,18 @@ export const LogView = ({ logref }: { logref: string }) => {
{line}
</pre>
))}
{lines.length > limit ? (
<>
<Button
color="default"
type="link"
onClick={() => setLimit(limit * 10)}
>
Show {Math.min(limit * 9, lines.length - limit)} more lines out of{" "}
{lines.length} available...
</Button>
</>
) : null}
</div>
);
};
+6 -12
View File
@@ -1,6 +1,7 @@
import React from "react";
import { DisplayType, colorForStatus } from "../state/flowdisplayaggregator";
import {
CodeOutlined,
DeleteOutlined,
DownloadOutlined,
FileSearchOutlined,
@@ -23,20 +24,10 @@ export const OperationIcon = ({
let avatar: React.ReactNode;
switch (type) {
case DisplayType.BACKUP:
avatar = (
<SaveOutlined
style={{ color: color }}
spin={status === OperationStatus.STATUS_INPROGRESS}
/>
);
avatar = <SaveOutlined style={{ color: color }} />;
break;
case DisplayType.FORGET:
avatar = (
<DeleteOutlined
style={{ color: color }}
spin={status === OperationStatus.STATUS_INPROGRESS}
/>
);
avatar = <DeleteOutlined style={{ color: color }} />;
break;
case DisplayType.SNAPSHOT:
avatar = <PaperClipOutlined style={{ color: color }} />;
@@ -55,6 +46,9 @@ export const OperationIcon = ({
case DisplayType.STATS:
avatar = <InfoCircleOutlined style={{ color: color }} />;
break;
case DisplayType.RUNCOMMAND:
avatar = <CodeOutlined style={{ color: color }} />;
break;
}
return avatar;
+5 -1
View File
@@ -15,11 +15,13 @@ export const OperationList = ({
useOperations,
showPlan,
displayHooksInline,
filter,
}: React.PropsWithoutRef<{
req?: GetOperationsRequest;
useOperations?: Operation[]; // exact set of operations to display; no filtering will be applied.
showPlan?: boolean;
displayHooksInline?: boolean;
filter?: (op: Operation) => boolean;
}>) => {
const alertApi = useAlertApi();
@@ -27,7 +29,9 @@ export const OperationList = ({
if (req) {
useEffect(() => {
const logState = new OplogState((op) => !shouldHideStatus(op.status));
const logState = new OplogState(
(op) => !shouldHideStatus(op.status) && (!filter || filter(op))
);
logState.subscribe((ids, flowIDs, event) => {
setOperations(logState.getAll());
+12
View File
@@ -234,6 +234,18 @@ export const OperationRow = ({
<pre>{check.output}</pre>
),
});
} else if (operation.op.case === "operationRunCommand") {
const run = operation.op.value;
expandedBodyItems.push("run");
bodyItems.push({
key: "run",
label: "Command Output",
children: (
<>
<LogView logref={run.outputLogref} />
</>
),
});
} else if (operation.op.case === "operationRestore") {
expandedBodyItems.push("restore");
bodyItems.push({
+1 -1
View File
@@ -487,7 +487,7 @@ const BackupView = ({ backup }: { backup?: FlowDisplayInfo }) => {
backrestService.clearHistory(
new ClearHistoryRequest({
selector: new OpSelector({
ids: backup.operations.map((op) => op.id),
flowId: backup.flowID,
}),
})
);
+5
View File
@@ -11,6 +11,7 @@ export enum DisplayType {
RESTORE,
STATS,
RUNHOOK,
RUNCOMMAND,
}
export interface FlowDisplayInfo {
@@ -156,6 +157,8 @@ export const getTypeForDisplay = (op: Operation) => {
return DisplayType.STATS;
case "operationRunHook":
return DisplayType.RUNHOOK;
case "operationRunCommand":
return DisplayType.RUNCOMMAND;
default:
return DisplayType.UNKNOWN;
}
@@ -179,6 +182,8 @@ export const displayTypeToString = (type: DisplayType) => {
return "Stats";
case DisplayType.RUNHOOK:
return "Run Hook";
case DisplayType.RUNCOMMAND:
return "Run Command";
default:
return "Unknown";
}
+32 -52
View File
@@ -5,6 +5,11 @@ import { backrestService } from "../api";
import { SpinButton } from "../components/SpinButton";
import { ConnectError } from "@connectrpc/connect";
import { useAlertApi } from "../components/Alerts";
import {
GetOperationsRequest,
RunCommandRequest,
} from "../../gen/ts/v1/service_pb";
import { OperationList } from "../components/OperationList";
interface Invocation {
command: string;
@@ -17,64 +22,29 @@ export const RunCommandModal = ({ repoId }: { repoId: string }) => {
const alertApi = useAlertApi()!;
const [command, setCommand] = React.useState("");
const [running, setRunning] = React.useState(false);
const [invocations, setInvocations] = React.useState<Invocation[]>([]);
const [abortController, setAbortController] = React.useState<
AbortController | undefined
>();
const handleCancel = () => {
if (abortController) {
alertApi.warning("In-progress restic command was aborted");
abortController.abort();
}
showModal(null);
};
const doExecute = async () => {
if (running) return;
if (!command) return;
setRunning(true);
const newInvocation = { command, output: "", error: "" };
setInvocations((invocations) => [newInvocation, ...invocations]);
let segments: string[] = [];
const toRun = command.trim();
setCommand("");
try {
const abortController = new AbortController();
setAbortController(abortController);
for await (const bytes of backrestService.runCommand(
{
const opID = await backrestService.runCommand(
new RunCommandRequest({
repoId,
command,
},
{
signal: abortController.signal,
}
)) {
const output = new TextDecoder("utf-8").decode(bytes.value);
segments.push(output);
setInvocations((invocations) => {
const copy = [...invocations];
copy[0] = {
...copy[0],
output: segments.join(""),
};
return copy;
});
}
command: toRun,
})
);
} catch (e: any) {
setInvocations((invocations) => {
const copy = [...invocations];
copy[0] = {
...copy[0],
error: (e as Error).message,
};
return copy;
});
alertApi.error("Command failed: " + e.message);
} finally {
setRunning(false);
setAbortController(undefined);
}
};
@@ -89,6 +59,7 @@ export const RunCommandModal = ({ repoId }: { repoId: string }) => {
<Space.Compact style={{ width: "100%" }}>
<Input
placeholder="Run a restic comamnd e.g. 'help' to print help text"
value={command}
onChange={(e) => setCommand(e.target.value)}
onKeyUp={(e) => {
if (e.key === "Enter") {
@@ -100,14 +71,23 @@ export const RunCommandModal = ({ repoId }: { repoId: string }) => {
Execute
</SpinButton>
</Space.Compact>
{invocations.map((invocation, i) => (
<div key={i}>
{invocation.output ? <pre>{invocation.output}</pre> : null}
{invocation.error ? (
<pre style={{ color: "red" }}>{invocation.error}</pre>
) : null}
</div>
))}
{running && command ? (
<em style={{ color: "gray" }}>
Warning: another command is already running. Wait for it to finish
before running another operation that requires the repo lock.
</em>
) : null}
<OperationList
req={
new GetOperationsRequest({
selector: {
repoId: repoId,
planId: "_system_", // run commands are not associated with a plan
},
})
}
filter={(op) => op.op.case === "operationRunCommand"}
/>
</Modal>
);
};