diff --git a/cmd/resticui/resticui.go b/cmd/resticui/resticui.go index 3f83f2b..7b6fd4f 100644 --- a/cmd/resticui/resticui.go +++ b/cmd/resticui/resticui.go @@ -10,7 +10,9 @@ import ( "syscall" "github.com/garethgeorge/resticui/internal/api" - "github.com/garethgeorge/resticui/static" + "github.com/garethgeorge/resticui/internal/config" + "github.com/garethgeorge/resticui/internal/orchestrator" + static "github.com/garethgeorge/resticui/webui" "go.uber.org/zap" _ "embed" @@ -21,6 +23,10 @@ func main() { ctx, cancel := context.WithCancel(ctx) go onterm(cancel) + if _, err := config.Default.Get(); err != nil { + zap.S().Fatalf("Error loading config: %v", err) + } + var wg sync.WaitGroup // Configure the HTTP mux @@ -32,11 +38,13 @@ func main() { Handler: mux, } + orchestrator := orchestrator.NewOrchestrator(config.Default) + // Serve the API wg.Add(1) go func() { defer wg.Done() - err := api.ServeAPI(ctx, mux) + err := api.ServeAPI(ctx, orchestrator, mux) if err != nil { zap.S().Fatal("Error serving API", zap.Error(err)) } diff --git a/gen/go/v1/config.pb.go b/gen/go/v1/config.pb.go index e937f02..605ac79 100644 --- a/gen/go/v1/config.pb.go +++ b/gen/go/v1/config.pb.go @@ -25,9 +25,12 @@ type Config struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - Repos []*Repo `protobuf:"bytes,3,rep,name=repos,proto3" json:"repos,omitempty"` - Plans []*Plan `protobuf:"bytes,4,rep,name=plans,proto3" json:"plans,omitempty"` + // modification number, used for read-modify-write consistency in the UI. Incremented on every write. + Modno int32 `protobuf:"varint,1,opt,name=modno,proto3" json:"modno,omitempty"` + // override the hostname tagged on backups. If provided it will be used in addition to tags to group backups. + HostOverride string `protobuf:"bytes,2,opt,name=host_override,proto3" json:"host_override,omitempty"` + Repos []*Repo `protobuf:"bytes,3,rep,name=repos,proto3" json:"repos,omitempty"` + Plans []*Plan `protobuf:"bytes,4,rep,name=plans,proto3" json:"plans,omitempty"` } func (x *Config) Reset() { @@ -62,13 +65,20 @@ func (*Config) Descriptor() ([]byte, []int) { return file_v1_config_proto_rawDescGZIP(), []int{0} } -func (x *Config) GetVersion() int32 { +func (x *Config) GetModno() int32 { if x != nil { - return x.Version + return x.Modno } return 0 } +func (x *Config) GetHostOverride() string { + if x != nil { + return x.HostOverride + } + return "" +} + func (x *Config) GetRepos() []*Repo { if x != nil { return x.Repos @@ -83,61 +93,6 @@ func (x *Config) GetPlans() []*Plan { return nil } -type User struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` // plaintext password -} - -func (x *User) Reset() { - *x = User{} - if protoimpl.UnsafeEnabled { - mi := &file_v1_config_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *User) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*User) ProtoMessage() {} - -func (x *User) ProtoReflect() protoreflect.Message { - mi := &file_v1_config_proto_msgTypes[1] - 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 User.ProtoReflect.Descriptor instead. -func (*User) Descriptor() ([]byte, []int) { - return file_v1_config_proto_rawDescGZIP(), []int{1} -} - -func (x *User) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *User) GetPassword() string { - if x != nil { - return x.Password - } - return "" -} - type Repo struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -153,7 +108,7 @@ type Repo struct { func (x *Repo) Reset() { *x = Repo{} if protoimpl.UnsafeEnabled { - mi := &file_v1_config_proto_msgTypes[2] + mi := &file_v1_config_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -166,7 +121,7 @@ func (x *Repo) String() string { func (*Repo) ProtoMessage() {} func (x *Repo) ProtoReflect() protoreflect.Message { - mi := &file_v1_config_proto_msgTypes[2] + mi := &file_v1_config_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -179,7 +134,7 @@ func (x *Repo) ProtoReflect() protoreflect.Message { // Deprecated: Use Repo.ProtoReflect.Descriptor instead. func (*Repo) Descriptor() ([]byte, []int) { - return file_v1_config_proto_rawDescGZIP(), []int{2} + return file_v1_config_proto_rawDescGZIP(), []int{1} } func (x *Repo) GetId() string { @@ -233,7 +188,7 @@ type Plan struct { func (x *Plan) Reset() { *x = Plan{} if protoimpl.UnsafeEnabled { - mi := &file_v1_config_proto_msgTypes[3] + mi := &file_v1_config_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -246,7 +201,7 @@ func (x *Plan) String() string { func (*Plan) ProtoMessage() {} func (x *Plan) ProtoReflect() protoreflect.Message { - mi := &file_v1_config_proto_msgTypes[3] + mi := &file_v1_config_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -259,7 +214,7 @@ func (x *Plan) ProtoReflect() protoreflect.Message { // Deprecated: Use Plan.ProtoReflect.Descriptor instead. func (*Plan) Descriptor() ([]byte, []int) { - return file_v1_config_proto_rawDescGZIP(), []int{3} + return file_v1_config_proto_rawDescGZIP(), []int{2} } func (x *Plan) GetId() string { @@ -323,7 +278,7 @@ type RetentionPolicy struct { func (x *RetentionPolicy) Reset() { *x = RetentionPolicy{} if protoimpl.UnsafeEnabled { - mi := &file_v1_config_proto_msgTypes[4] + mi := &file_v1_config_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -336,7 +291,7 @@ func (x *RetentionPolicy) String() string { func (*RetentionPolicy) ProtoMessage() {} func (x *RetentionPolicy) ProtoReflect() protoreflect.Message { - mi := &file_v1_config_proto_msgTypes[4] + mi := &file_v1_config_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -349,7 +304,7 @@ func (x *RetentionPolicy) ProtoReflect() protoreflect.Message { // Deprecated: Use RetentionPolicy.ProtoReflect.Descriptor instead. func (*RetentionPolicy) Descriptor() ([]byte, []int) { - return file_v1_config_proto_rawDescGZIP(), []int{4} + return file_v1_config_proto_rawDescGZIP(), []int{3} } func (x *RetentionPolicy) GetMaxUnusedLimit() string { @@ -412,57 +367,55 @@ var File_v1_config_proto protoreflect.FileDescriptor var file_v1_config_proto_rawDesc = []byte{ 0x0a, 0x0f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x12, 0x02, 0x76, 0x31, 0x22, 0x62, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, - 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x05, 0x72, 0x65, 0x70, - 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, - 0x70, 0x6f, 0x52, 0x05, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x12, 0x1e, 0x0a, 0x05, 0x70, 0x6c, 0x61, - 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x6c, - 0x61, 0x6e, 0x52, 0x05, 0x70, 0x6c, 0x61, 0x6e, 0x73, 0x22, 0x36, 0x0a, 0x04, 0x55, 0x73, 0x65, - 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, - 0x64, 0x22, 0x6c, 0x0a, 0x04, 0x52, 0x65, 0x70, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x1a, 0x0a, 0x08, 0x70, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, - 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x04, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, - 0x67, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x22, - 0xa3, 0x01, 0x0a, 0x04, 0x50, 0x6c, 0x61, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x65, 0x70, 0x6f, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x12, 0x14, 0x0a, 0x05, - 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, 0x61, 0x74, - 0x68, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x73, 0x18, 0x05, - 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x73, 0x12, 0x12, - 0x0a, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x72, - 0x6f, 0x6e, 0x12, 0x31, 0x0a, 0x09, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x18, - 0x07, 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, 0x09, 0x72, 0x65, 0x74, 0x65, - 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xb2, 0x02, 0x0a, 0x0f, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, - 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x61, 0x78, - 0x5f, 0x75, 0x6e, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x61, 0x78, 0x55, 0x6e, 0x75, 0x73, 0x65, 0x64, 0x4c, 0x69, - 0x6d, 0x69, 0x74, 0x12, 0x1e, 0x0a, 0x0b, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x6c, 0x61, 0x73, 0x74, - 0x5f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x4c, 0x61, - 0x73, 0x74, 0x4e, 0x12, 0x1f, 0x0a, 0x0b, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x68, 0x6f, 0x75, 0x72, - 0x6c, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x48, 0x6f, - 0x75, 0x72, 0x6c, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x64, 0x61, 0x69, - 0x6c, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x44, 0x61, - 0x69, 0x6c, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x77, 0x65, 0x65, 0x6b, - 0x6c, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x57, 0x65, - 0x65, 0x6b, 0x6c, 0x79, 0x12, 0x21, 0x0a, 0x0c, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x6d, 0x6f, 0x6e, - 0x74, 0x68, 0x6c, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6b, 0x65, 0x65, 0x70, - 0x4d, 0x6f, 0x6e, 0x74, 0x68, 0x6c, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x6b, 0x65, 0x65, 0x70, 0x5f, - 0x79, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6b, 0x65, - 0x65, 0x70, 0x59, 0x65, 0x61, 0x72, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x6b, 0x65, 0x65, 0x70, - 0x5f, 0x77, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6b, 0x65, 0x65, 0x70, 0x57, 0x69, 0x74, 0x68, - 0x69, 0x6e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, 0x68, 0x67, - 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x69, 0x63, 0x75, 0x69, 0x2f, 0x67, - 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x6f, 0x12, 0x02, 0x76, 0x31, 0x22, 0x84, 0x01, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x6f, 0x64, 0x6e, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x05, 0x6d, 0x6f, 0x64, 0x6e, 0x6f, 0x12, 0x24, 0x0a, 0x0d, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x6f, + 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x68, + 0x6f, 0x73, 0x74, 0x5f, 0x6f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x12, 0x1e, 0x0a, 0x05, + 0x72, 0x65, 0x70, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x76, 0x31, + 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x52, 0x05, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x12, 0x1e, 0x0a, 0x05, + 0x70, 0x6c, 0x61, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x08, 0x2e, 0x76, 0x31, + 0x2e, 0x50, 0x6c, 0x61, 0x6e, 0x52, 0x05, 0x70, 0x6c, 0x61, 0x6e, 0x73, 0x22, 0x6c, 0x0a, 0x04, + 0x52, 0x65, 0x70, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x03, 0x65, 0x6e, 0x76, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x18, 0x05, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x05, 0x66, 0x6c, 0x61, 0x67, 0x73, 0x22, 0xa3, 0x01, 0x0a, 0x04, 0x50, + 0x6c, 0x61, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x12, 0x1a, 0x0a, + 0x08, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x08, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x72, 0x6f, + 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x63, 0x72, 0x6f, 0x6e, 0x12, 0x31, 0x0a, + 0x09, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x07, 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, 0x09, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0xb2, 0x02, 0x0a, 0x0f, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x12, 0x28, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x5f, 0x75, 0x6e, 0x75, 0x73, + 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x6d, 0x61, 0x78, 0x55, 0x6e, 0x75, 0x73, 0x65, 0x64, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1e, + 0x0a, 0x0b, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x4c, 0x61, 0x73, 0x74, 0x4e, 0x12, 0x1f, + 0x0a, 0x0b, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x68, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x48, 0x6f, 0x75, 0x72, 0x6c, 0x79, 0x12, + 0x1d, 0x0a, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x09, 0x6b, 0x65, 0x65, 0x70, 0x44, 0x61, 0x69, 0x6c, 0x79, 0x12, 0x1f, + 0x0a, 0x0b, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x77, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x57, 0x65, 0x65, 0x6b, 0x6c, 0x79, 0x12, + 0x21, 0x0a, 0x0c, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x6d, 0x6f, 0x6e, 0x74, 0x68, 0x6c, 0x79, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x6b, 0x65, 0x65, 0x70, 0x4d, 0x6f, 0x6e, 0x74, 0x68, + 0x6c, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x79, 0x65, 0x61, 0x72, 0x6c, + 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0a, 0x6b, 0x65, 0x65, 0x70, 0x59, 0x65, 0x61, + 0x72, 0x6c, 0x79, 0x12, 0x30, 0x0a, 0x14, 0x6b, 0x65, 0x65, 0x70, 0x5f, 0x77, 0x69, 0x74, 0x68, + 0x69, 0x6e, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x12, 0x6b, 0x65, 0x65, 0x70, 0x57, 0x69, 0x74, 0x68, 0x69, 0x6e, 0x44, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, + 0x2f, 0x72, 0x65, 0x73, 0x74, 0x69, 0x63, 0x75, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -477,18 +430,17 @@ func file_v1_config_proto_rawDescGZIP() []byte { return file_v1_config_proto_rawDescData } -var file_v1_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_v1_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_v1_config_proto_goTypes = []interface{}{ (*Config)(nil), // 0: v1.Config - (*User)(nil), // 1: v1.User - (*Repo)(nil), // 2: v1.Repo - (*Plan)(nil), // 3: v1.Plan - (*RetentionPolicy)(nil), // 4: v1.RetentionPolicy + (*Repo)(nil), // 1: v1.Repo + (*Plan)(nil), // 2: v1.Plan + (*RetentionPolicy)(nil), // 3: v1.RetentionPolicy } var file_v1_config_proto_depIdxs = []int32{ - 2, // 0: v1.Config.repos:type_name -> v1.Repo - 3, // 1: v1.Config.plans:type_name -> v1.Plan - 4, // 2: v1.Plan.retention:type_name -> v1.RetentionPolicy + 1, // 0: v1.Config.repos:type_name -> v1.Repo + 2, // 1: v1.Config.plans:type_name -> v1.Plan + 3, // 2: v1.Plan.retention:type_name -> v1.RetentionPolicy 3, // [3:3] is the sub-list for method output_type 3, // [3:3] is the sub-list for method input_type 3, // [3:3] is the sub-list for extension type_name @@ -515,18 +467,6 @@ func file_v1_config_proto_init() { } } file_v1_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*User); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_v1_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Repo); i { case 0: return &v.state @@ -538,7 +478,7 @@ func file_v1_config_proto_init() { return nil } } - file_v1_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_v1_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Plan); i { case 0: return &v.state @@ -550,7 +490,7 @@ func file_v1_config_proto_init() { return nil } } - file_v1_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_v1_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RetentionPolicy); i { case 0: return &v.state @@ -569,7 +509,7 @@ func file_v1_config_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_v1_config_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, diff --git a/gen/go/v1/events.pb.go b/gen/go/v1/events.pb.go index be6d736..a3b19ab 100644 --- a/gen/go/v1/events.pb.go +++ b/gen/go/v1/events.pb.go @@ -155,7 +155,7 @@ type Event_Log struct { } type Event_BackupStatusChange struct { - BackupStatusChange *BackupStatusEvent `protobuf:"bytes,4,opt,name=backup_status_change,json=backup_status,proto3,oneof"` + BackupStatusChange *BackupStatusEvent `protobuf:"bytes,4,opt,name=backup_status_change,json=backupStatusChange,proto3,oneof"` } func (*Event_Log) isEvent_Event() {} @@ -276,33 +276,33 @@ var File_v1_events_proto protoreflect.FileDescriptor var file_v1_events_proto_rawDesc = []byte{ 0x0a, 0x0f, 0x76, 0x31, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x12, 0x02, 0x76, 0x31, 0x22, 0x96, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x6f, 0x12, 0x02, 0x76, 0x31, 0x22, 0x9b, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x20, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x6c, 0x6f, 0x67, 0x12, - 0x44, 0x0a, 0x14, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x49, 0x0a, 0x14, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x5f, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x24, - 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x22, 0x65, 0x0a, 0x11, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6c, 0x61, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x12, 0x22, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x07, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x2a, 0x3f, 0x0a, 0x06, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, - 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4e, 0x5f, 0x50, 0x52, 0x4f, 0x47, 0x52, 0x45, 0x53, - 0x53, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, 0x45, 0x53, 0x53, 0x10, 0x02, - 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, 0x03, 0x42, 0x2e, 0x5a, 0x2c, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, - 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x69, 0x63, 0x75, 0x69, - 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x12, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x22, 0x24, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x65, 0x0a, 0x11, 0x42, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x70, 0x6c, 0x61, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x6c, + 0x61, 0x6e, 0x12, 0x22, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, + 0x2a, 0x3f, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, + 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0f, 0x0a, 0x0b, 0x49, 0x4e, 0x5f, 0x50, 0x52, + 0x4f, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x53, 0x55, 0x43, 0x43, + 0x45, 0x53, 0x53, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x41, 0x49, 0x4c, 0x45, 0x44, 0x10, + 0x03, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x67, 0x61, 0x72, 0x65, 0x74, 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x72, 0x65, 0x73, + 0x74, 0x69, 0x63, 0x75, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, + 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gen/go/v1/restic.pb.go b/gen/go/v1/restic.pb.go index 87e1bb9..a44f02a 100644 --- a/gen/go/v1/restic.pb.go +++ b/gen/go/v1/restic.pb.go @@ -26,7 +26,7 @@ type ResticSnapshot struct { unknownFields protoimpl.UnknownFields Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - UnixTimeMs int64 `protobuf:"varint,2,opt,name=unix_time_ms,json=time,proto3" json:"unix_time_ms,omitempty"` + UnixTimeMs int64 `protobuf:"varint,2,opt,name=unix_time_ms,json=unixTimeMs,proto3" json:"unix_time_ms,omitempty"` Hostname string `protobuf:"bytes,3,opt,name=hostname,proto3" json:"hostname,omitempty"` Username string `protobuf:"bytes,4,opt,name=username,proto3" json:"username,omitempty"` Tree string `protobuf:"bytes,5,opt,name=tree,proto3" json:"tree,omitempty"` // tree hash @@ -123,6 +123,53 @@ func (x *ResticSnapshot) GetTags() []string { return nil } +type ResticSnapshotList struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Snapshots []*ResticSnapshot `protobuf:"bytes,1,rep,name=snapshots,proto3" json:"snapshots,omitempty"` +} + +func (x *ResticSnapshotList) Reset() { + *x = ResticSnapshotList{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_restic_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ResticSnapshotList) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ResticSnapshotList) ProtoMessage() {} + +func (x *ResticSnapshotList) ProtoReflect() protoreflect.Message { + mi := &file_v1_restic_proto_msgTypes[1] + 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 ResticSnapshotList.ProtoReflect.Descriptor instead. +func (*ResticSnapshotList) Descriptor() ([]byte, []int) { + return file_v1_restic_proto_rawDescGZIP(), []int{1} +} + +func (x *ResticSnapshotList) GetSnapshots() []*ResticSnapshot { + if x != nil { + return x.Snapshots + } + return nil +} + type BackupProgressEntry struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -138,7 +185,7 @@ type BackupProgressEntry struct { func (x *BackupProgressEntry) Reset() { *x = BackupProgressEntry{} if protoimpl.UnsafeEnabled { - mi := &file_v1_restic_proto_msgTypes[1] + mi := &file_v1_restic_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -151,7 +198,7 @@ func (x *BackupProgressEntry) String() string { func (*BackupProgressEntry) ProtoMessage() {} func (x *BackupProgressEntry) ProtoReflect() protoreflect.Message { - mi := &file_v1_restic_proto_msgTypes[1] + mi := &file_v1_restic_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -164,7 +211,7 @@ func (x *BackupProgressEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use BackupProgressEntry.ProtoReflect.Descriptor instead. func (*BackupProgressEntry) Descriptor() ([]byte, []int) { - return file_v1_restic_proto_rawDescGZIP(), []int{1} + return file_v1_restic_proto_rawDescGZIP(), []int{2} } func (m *BackupProgressEntry) GetEntry() isBackupProgressEntry_Entry { @@ -209,17 +256,17 @@ type BackupProgressStatusEntry struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - PercentDone float64 `protobuf:"fixed64,1,opt,name=percent_done,json=percent,proto3" json:"percent_done,omitempty"` // 0.0 - 1.0 - TotalFiles int64 `protobuf:"varint,2,opt,name=total_files,proto3" json:"total_files,omitempty"` - TotalBytes int64 `protobuf:"varint,3,opt,name=total_bytes,proto3" json:"total_bytes,omitempty"` - FilesDone int64 `protobuf:"varint,4,opt,name=files_done,proto3" json:"files_done,omitempty"` - BytesDone int64 `protobuf:"varint,5,opt,name=bytes_done,proto3" json:"bytes_done,omitempty"` + PercentDone float64 `protobuf:"fixed64,1,opt,name=percent_done,json=percentDone,proto3" json:"percent_done,omitempty"` // 0.0 - 1.0 + TotalFiles int64 `protobuf:"varint,2,opt,name=total_files,json=totalFiles,proto3" json:"total_files,omitempty"` + TotalBytes int64 `protobuf:"varint,3,opt,name=total_bytes,json=totalBytes,proto3" json:"total_bytes,omitempty"` + FilesDone int64 `protobuf:"varint,4,opt,name=files_done,json=filesDone,proto3" json:"files_done,omitempty"` + BytesDone int64 `protobuf:"varint,5,opt,name=bytes_done,json=bytesDone,proto3" json:"bytes_done,omitempty"` } func (x *BackupProgressStatusEntry) Reset() { *x = BackupProgressStatusEntry{} if protoimpl.UnsafeEnabled { - mi := &file_v1_restic_proto_msgTypes[2] + mi := &file_v1_restic_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -232,7 +279,7 @@ func (x *BackupProgressStatusEntry) String() string { func (*BackupProgressStatusEntry) ProtoMessage() {} func (x *BackupProgressStatusEntry) ProtoReflect() protoreflect.Message { - mi := &file_v1_restic_proto_msgTypes[2] + mi := &file_v1_restic_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -245,7 +292,7 @@ func (x *BackupProgressStatusEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use BackupProgressStatusEntry.ProtoReflect.Descriptor instead. func (*BackupProgressStatusEntry) Descriptor() ([]byte, []int) { - return file_v1_restic_proto_rawDescGZIP(), []int{2} + return file_v1_restic_proto_rawDescGZIP(), []int{3} } func (x *BackupProgressStatusEntry) GetPercentDone() float64 { @@ -288,25 +335,25 @@ type BackupProgressSummary struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - FilesNew int64 `protobuf:"varint,1,opt,name=files_new,proto3" json:"files_new,omitempty"` - FilesChanged int64 `protobuf:"varint,2,opt,name=files_changed,proto3" json:"files_changed,omitempty"` - FilesUnmodified int64 `protobuf:"varint,3,opt,name=files_unmodified,proto3" json:"files_unmodified,omitempty"` - DirsNew int64 `protobuf:"varint,4,opt,name=dirs_new,proto3" json:"dirs_new,omitempty"` - DirsChanged int64 `protobuf:"varint,5,opt,name=dirs_changed,proto3" json:"dirs_changed,omitempty"` - DirsUnmodified int64 `protobuf:"varint,6,opt,name=dirs_unmodified,proto3" json:"dirs_unmodified,omitempty"` - DataBlobs int64 `protobuf:"varint,7,opt,name=data_blobs,proto3" json:"data_blobs,omitempty"` - TreeBlobs int64 `protobuf:"varint,8,opt,name=tree_blobs,proto3" json:"tree_blobs,omitempty"` - DataAdded int64 `protobuf:"varint,9,opt,name=data_added,proto3" json:"data_added,omitempty"` - TotalFilesProcessed int64 `protobuf:"varint,10,opt,name=total_files_processed,proto3" json:"total_files_processed,omitempty"` - TotalBytesProcessed int64 `protobuf:"varint,11,opt,name=total_bytes_processed,proto3" json:"total_bytes_processed,omitempty"` - TotalDuration int64 `protobuf:"varint,12,opt,name=total_duration,proto3" json:"total_duration,omitempty"` - SnapshotId string `protobuf:"bytes,13,opt,name=snapshot_id,proto3" json:"snapshot_id,omitempty"` + FilesNew int64 `protobuf:"varint,1,opt,name=files_new,json=filesNew,proto3" json:"files_new,omitempty"` + FilesChanged int64 `protobuf:"varint,2,opt,name=files_changed,json=filesChanged,proto3" json:"files_changed,omitempty"` + FilesUnmodified int64 `protobuf:"varint,3,opt,name=files_unmodified,json=filesUnmodified,proto3" json:"files_unmodified,omitempty"` + DirsNew int64 `protobuf:"varint,4,opt,name=dirs_new,json=dirsNew,proto3" json:"dirs_new,omitempty"` + DirsChanged int64 `protobuf:"varint,5,opt,name=dirs_changed,json=dirsChanged,proto3" json:"dirs_changed,omitempty"` + DirsUnmodified int64 `protobuf:"varint,6,opt,name=dirs_unmodified,json=dirsUnmodified,proto3" json:"dirs_unmodified,omitempty"` + DataBlobs int64 `protobuf:"varint,7,opt,name=data_blobs,json=dataBlobs,proto3" json:"data_blobs,omitempty"` + TreeBlobs int64 `protobuf:"varint,8,opt,name=tree_blobs,json=treeBlobs,proto3" json:"tree_blobs,omitempty"` + DataAdded int64 `protobuf:"varint,9,opt,name=data_added,json=dataAdded,proto3" json:"data_added,omitempty"` + TotalFilesProcessed int64 `protobuf:"varint,10,opt,name=total_files_processed,json=totalFilesProcessed,proto3" json:"total_files_processed,omitempty"` + TotalBytesProcessed int64 `protobuf:"varint,11,opt,name=total_bytes_processed,json=totalBytesProcessed,proto3" json:"total_bytes_processed,omitempty"` + TotalDuration int64 `protobuf:"varint,12,opt,name=total_duration,json=totalDuration,proto3" json:"total_duration,omitempty"` + SnapshotId string `protobuf:"bytes,13,opt,name=snapshot_id,json=snapshotId,proto3" json:"snapshot_id,omitempty"` } func (x *BackupProgressSummary) Reset() { *x = BackupProgressSummary{} if protoimpl.UnsafeEnabled { - mi := &file_v1_restic_proto_msgTypes[3] + mi := &file_v1_restic_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -319,7 +366,7 @@ func (x *BackupProgressSummary) String() string { func (*BackupProgressSummary) ProtoMessage() {} func (x *BackupProgressSummary) ProtoReflect() protoreflect.Message { - mi := &file_v1_restic_proto_msgTypes[3] + mi := &file_v1_restic_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -332,7 +379,7 @@ func (x *BackupProgressSummary) ProtoReflect() protoreflect.Message { // Deprecated: Use BackupProgressSummary.ProtoReflect.Descriptor instead. func (*BackupProgressSummary) Descriptor() ([]byte, []int) { - return file_v1_restic_proto_rawDescGZIP(), []int{3} + return file_v1_restic_proto_rawDescGZIP(), []int{4} } func (x *BackupProgressSummary) GetFilesNew() int64 { @@ -430,77 +477,81 @@ var File_v1_restic_proto protoreflect.FileDescriptor var file_v1_restic_proto_rawDesc = []byte{ 0x0a, 0x0f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x12, 0x02, 0x76, 0x31, 0x22, 0xca, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x74, 0x69, 0x63, + 0x6f, 0x12, 0x02, 0x76, 0x31, 0x22, 0xd0, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x73, 0x74, 0x69, 0x63, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x0c, 0x75, 0x6e, 0x69, 0x78, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, - 0x74, 0x69, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x74, 0x72, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, - 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, - 0x67, 0x73, 0x22, 0x8e, 0x01, 0x0a, 0x13, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x72, 0x6f, - 0x67, 0x72, 0x65, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x37, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x76, 0x31, 0x2e, - 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x35, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, - 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x48, - 0x00, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x6e, - 0x74, 0x72, 0x79, 0x22, 0xbe, 0x01, 0x0a, 0x19, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x72, - 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x1d, 0x0a, 0x0c, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x6f, 0x6e, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x07, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, - 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, - 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, - 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, - 0x79, 0x74, 0x65, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x64, 0x6f, - 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, - 0x64, 0x6f, 0x6e, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x64, 0x6f, - 0x6e, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, - 0x64, 0x6f, 0x6e, 0x65, 0x22, 0x87, 0x04, 0x0a, 0x15, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x50, - 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x1c, - 0x0a, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x6e, 0x65, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x6e, 0x65, 0x77, 0x12, 0x24, 0x0a, 0x0d, - 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0d, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, - 0x65, 0x64, 0x12, 0x2a, 0x0a, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x75, 0x6e, 0x6d, 0x6f, - 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x66, 0x69, - 0x6c, 0x65, 0x73, 0x5f, 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x1a, - 0x0a, 0x08, 0x64, 0x69, 0x72, 0x73, 0x5f, 0x6e, 0x65, 0x77, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x08, 0x64, 0x69, 0x72, 0x73, 0x5f, 0x6e, 0x65, 0x77, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x69, - 0x72, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x0c, 0x64, 0x69, 0x72, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x12, 0x28, - 0x0a, 0x0f, 0x64, 0x69, 0x72, 0x73, 0x5f, 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, - 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x64, 0x69, 0x72, 0x73, 0x5f, 0x75, 0x6e, - 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61, - 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x61, - 0x74, 0x61, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x74, 0x72, 0x65, 0x65, - 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x72, - 0x65, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61, - 0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x64, 0x61, - 0x74, 0x61, 0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x12, 0x34, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, - 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x66, - 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x34, - 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x70, 0x72, - 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x15, 0x74, - 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, - 0x73, 0x73, 0x65, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x64, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x74, 0x6f, - 0x74, 0x61, 0x6c, 0x5f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0c, 0x75, 0x6e, 0x69, 0x78, + 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, + 0x75, 0x6e, 0x69, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, + 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, + 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x72, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x14, + 0x0a, 0x05, 0x70, 0x61, 0x74, 0x68, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x70, + 0x61, 0x74, 0x68, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x08, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x22, 0x46, 0x0a, 0x12, 0x52, 0x65, 0x73, 0x74, + 0x69, 0x63, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x30, + 0x0a, 0x09, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, 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, 0x09, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, + 0x22, 0x8e, 0x01, 0x0a, 0x13, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x67, 0x72, + 0x65, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x37, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x12, 0x35, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x72, + 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x48, 0x00, 0x52, + 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x42, 0x07, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, + 0x79, 0x22, 0xbe, 0x01, 0x0a, 0x19, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x72, 0x6f, 0x67, + 0x72, 0x65, 0x73, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x21, 0x0a, 0x0c, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x5f, 0x64, 0x6f, 0x6e, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x44, 0x6f, + 0x6e, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x46, 0x69, + 0x6c, 0x65, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, + 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, + 0x79, 0x74, 0x65, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x64, 0x6f, + 0x6e, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x44, + 0x6f, 0x6e, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x64, 0x6f, 0x6e, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x62, 0x79, 0x74, 0x65, 0x73, 0x44, 0x6f, + 0x6e, 0x65, 0x22, 0xf8, 0x03, 0x0a, 0x15, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x50, 0x72, 0x6f, + 0x67, 0x72, 0x65, 0x73, 0x73, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x1b, 0x0a, 0x09, + 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x6e, 0x65, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x08, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4e, 0x65, 0x77, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x6c, + 0x65, 0x73, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0c, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x12, 0x29, + 0x0a, 0x10, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, + 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x55, + 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x64, 0x69, 0x72, + 0x73, 0x5f, 0x6e, 0x65, 0x77, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x64, 0x69, 0x72, + 0x73, 0x4e, 0x65, 0x77, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x69, 0x72, 0x73, 0x5f, 0x63, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x64, 0x69, 0x72, 0x73, + 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x69, 0x72, 0x73, 0x5f, + 0x75, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0e, 0x64, 0x69, 0x72, 0x73, 0x55, 0x6e, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, + 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x64, 0x61, 0x74, 0x61, 0x42, 0x6c, 0x6f, 0x62, 0x73, 0x12, + 0x1d, 0x0a, 0x0a, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x62, 0x73, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x72, 0x65, 0x65, 0x42, 0x6c, 0x6f, 0x62, 0x73, 0x12, 0x1d, + 0x0a, 0x0a, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x61, 0x64, 0x64, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x09, 0x64, 0x61, 0x74, 0x61, 0x41, 0x64, 0x64, 0x65, 0x64, 0x12, 0x32, 0x0a, + 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5f, 0x70, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x13, 0x74, 0x6f, + 0x74, 0x61, 0x6c, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, + 0x64, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, + 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x79, 0x74, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x64, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x5f, 0x69, 0x64, 0x42, 0x2e, - 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, - 0x65, 0x74, 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x69, 0x63, - 0x75, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x09, 0x52, 0x0a, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x49, 0x64, 0x42, 0x2e, 0x5a, + 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, + 0x74, 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x69, 0x63, 0x75, + 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -515,21 +566,23 @@ func file_v1_restic_proto_rawDescGZIP() []byte { return file_v1_restic_proto_rawDescData } -var file_v1_restic_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_v1_restic_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_v1_restic_proto_goTypes = []interface{}{ (*ResticSnapshot)(nil), // 0: v1.ResticSnapshot - (*BackupProgressEntry)(nil), // 1: v1.BackupProgressEntry - (*BackupProgressStatusEntry)(nil), // 2: v1.BackupProgressStatusEntry - (*BackupProgressSummary)(nil), // 3: v1.BackupProgressSummary + (*ResticSnapshotList)(nil), // 1: v1.ResticSnapshotList + (*BackupProgressEntry)(nil), // 2: v1.BackupProgressEntry + (*BackupProgressStatusEntry)(nil), // 3: v1.BackupProgressStatusEntry + (*BackupProgressSummary)(nil), // 4: v1.BackupProgressSummary } var file_v1_restic_proto_depIdxs = []int32{ - 2, // 0: v1.BackupProgressEntry.status:type_name -> v1.BackupProgressStatusEntry - 3, // 1: v1.BackupProgressEntry.summary:type_name -> v1.BackupProgressSummary - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 0, // 0: v1.ResticSnapshotList.snapshots:type_name -> v1.ResticSnapshot + 3, // 1: v1.BackupProgressEntry.status:type_name -> v1.BackupProgressStatusEntry + 4, // 2: v1.BackupProgressEntry.summary:type_name -> v1.BackupProgressSummary + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_v1_restic_proto_init() } @@ -551,7 +604,7 @@ func file_v1_restic_proto_init() { } } file_v1_restic_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BackupProgressEntry); i { + switch v := v.(*ResticSnapshotList); i { case 0: return &v.state case 1: @@ -563,7 +616,7 @@ func file_v1_restic_proto_init() { } } file_v1_restic_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BackupProgressStatusEntry); i { + switch v := v.(*BackupProgressEntry); i { case 0: return &v.state case 1: @@ -575,6 +628,18 @@ func file_v1_restic_proto_init() { } } file_v1_restic_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BackupProgressStatusEntry); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_v1_restic_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*BackupProgressSummary); i { case 0: return &v.state @@ -587,7 +652,7 @@ func file_v1_restic_proto_init() { } } } - file_v1_restic_proto_msgTypes[1].OneofWrappers = []interface{}{ + file_v1_restic_proto_msgTypes[2].OneofWrappers = []interface{}{ (*BackupProgressEntry_Status)(nil), (*BackupProgressEntry_Summary)(nil), } @@ -597,7 +662,7 @@ func file_v1_restic_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_v1_restic_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 5, NumExtensions: 0, NumServices: 0, }, diff --git a/gen/go/v1/service.pb.go b/gen/go/v1/service.pb.go index 74c1a6f..cf80021 100644 --- a/gen/go/v1/service.pb.go +++ b/gen/go/v1/service.pb.go @@ -13,6 +13,7 @@ import ( protoimpl "google.golang.org/protobuf/runtime/protoimpl" emptypb "google.golang.org/protobuf/types/known/emptypb" reflect "reflect" + sync "sync" ) const ( @@ -22,67 +23,151 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type ListSnapshotsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RepoId string `protobuf:"bytes,1,opt,name=repo_id,json=repoId,proto3" json:"repo_id,omitempty"` + PlanId string `protobuf:"bytes,2,opt,name=plan_id,json=planId,proto3" json:"plan_id,omitempty"` +} + +func (x *ListSnapshotsRequest) Reset() { + *x = ListSnapshotsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_v1_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ListSnapshotsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListSnapshotsRequest) ProtoMessage() {} + +func (x *ListSnapshotsRequest) ProtoReflect() protoreflect.Message { + mi := &file_v1_service_proto_msgTypes[0] + 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 ListSnapshotsRequest.ProtoReflect.Descriptor instead. +func (*ListSnapshotsRequest) Descriptor() ([]byte, []int) { + return file_v1_service_proto_rawDescGZIP(), []int{0} +} + +func (x *ListSnapshotsRequest) GetRepoId() string { + if x != nil { + return x.RepoId + } + return "" +} + +func (x *ListSnapshotsRequest) GetPlanId() string { + if x != nil { + return x.PlanId + } + return "" +} + var File_v1_service_proto protoreflect.FileDescriptor var file_v1_service_proto_rawDesc = []byte{ 0x0a, 0x10, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, 0x1a, 0x0f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0f, 0x76, 0x31, 0x2f, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x11, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, - 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xeb, 0x02, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x74, 0x69, - 0x63, 0x55, 0x49, 0x12, 0x43, 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, 0x69, 0x67, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x76, - 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3a, 0x0a, 0x09, 0x53, 0x65, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x1a, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x15, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x3a, 0x01, 0x2a, 0x22, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3b, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x52, 0x65, 0x70, 0x6f, 0x12, - 0x08, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x1a, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x3a, 0x01, 0x2a, - 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x70, - 0x6f, 0x12, 0x44, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 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, 0x09, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x22, 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x30, 0x01, 0x12, 0x5b, 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, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, - 0x76, 0x31, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x2f, - 0x70, 0x61, 0x74, 0x68, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, - 0x72, 0x65, 0x73, 0x74, 0x69, 0x63, 0x75, 0x69, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0f, 0x76, 0x31, 0x2f, 0x72, 0x65, 0x73, + 0x74, 0x69, 0x63, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x11, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, + 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x48, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x53, + 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 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, 0x17, 0x0a, 0x07, 0x70, 0x6c, 0x61, 0x6e, + 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x6c, 0x61, 0x6e, 0x49, + 0x64, 0x32, 0xc8, 0x03, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x74, 0x69, 0x63, 0x55, 0x49, 0x12, 0x43, + 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, 0x69, 0x67, 0x22, + 0x12, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x12, 0x3a, 0x0a, 0x09, 0x53, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x12, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x0a, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, + 0x3a, 0x01, 0x2a, 0x22, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, + 0x3b, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x52, 0x65, 0x70, 0x6f, 0x12, 0x08, 0x2e, 0x76, 0x31, 0x2e, + 0x52, 0x65, 0x70, 0x6f, 0x1a, 0x0a, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x3a, 0x01, 0x2a, 0x22, 0x0f, 0x2f, 0x76, 0x31, + 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x12, 0x44, 0x0a, 0x09, + 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 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, 0x09, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x12, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x0c, 0x12, 0x0a, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, + 0x6f, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6e, 0x61, + 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x73, 0x74, 0x69, 0x63, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, + 0x74, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x18, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x12, 0x3a, 0x01, 0x2a, + 0x22, 0x0d, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x73, 0x12, + 0x5b, 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, 0x20, 0x82, 0xd3, 0xe4, 0x93, + 0x02, 0x1a, 0x3a, 0x01, 0x2a, 0x22, 0x15, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x63, + 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x2f, 0x70, 0x61, 0x74, 0x68, 0x42, 0x2e, 0x5a, 0x2c, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x61, 0x72, 0x65, 0x74, + 0x68, 0x67, 0x65, 0x6f, 0x72, 0x67, 0x65, 0x2f, 0x72, 0x65, 0x73, 0x74, 0x69, 0x63, 0x75, 0x69, + 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } +var ( + file_v1_service_proto_rawDescOnce sync.Once + file_v1_service_proto_rawDescData = file_v1_service_proto_rawDesc +) + +func file_v1_service_proto_rawDescGZIP() []byte { + file_v1_service_proto_rawDescOnce.Do(func() { + file_v1_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_v1_service_proto_rawDescData) + }) + return file_v1_service_proto_rawDescData +} + +var file_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_v1_service_proto_goTypes = []interface{}{ - (*emptypb.Empty)(nil), // 0: google.protobuf.Empty - (*Config)(nil), // 1: v1.Config - (*Repo)(nil), // 2: v1.Repo - (*types.StringValue)(nil), // 3: types.StringValue - (*Event)(nil), // 4: v1.Event - (*types.StringList)(nil), // 5: types.StringList + (*ListSnapshotsRequest)(nil), // 0: v1.ListSnapshotsRequest + (*emptypb.Empty)(nil), // 1: google.protobuf.Empty + (*Config)(nil), // 2: v1.Config + (*Repo)(nil), // 3: v1.Repo + (*types.StringValue)(nil), // 4: types.StringValue + (*Event)(nil), // 5: v1.Event + (*ResticSnapshotList)(nil), // 6: v1.ResticSnapshotList + (*types.StringList)(nil), // 7: types.StringList } var file_v1_service_proto_depIdxs = []int32{ - 0, // 0: v1.ResticUI.GetConfig:input_type -> google.protobuf.Empty - 1, // 1: v1.ResticUI.SetConfig:input_type -> v1.Config - 2, // 2: v1.ResticUI.AddRepo:input_type -> v1.Repo - 0, // 3: v1.ResticUI.GetEvents:input_type -> google.protobuf.Empty - 3, // 4: v1.ResticUI.PathAutocomplete:input_type -> types.StringValue - 1, // 5: v1.ResticUI.GetConfig:output_type -> v1.Config - 1, // 6: v1.ResticUI.SetConfig:output_type -> v1.Config - 1, // 7: v1.ResticUI.AddRepo:output_type -> v1.Config - 4, // 8: v1.ResticUI.GetEvents:output_type -> v1.Event - 5, // 9: v1.ResticUI.PathAutocomplete:output_type -> types.StringList - 5, // [5:10] is the sub-list for method output_type - 0, // [0:5] is the sub-list for method input_type + 1, // 0: v1.ResticUI.GetConfig:input_type -> google.protobuf.Empty + 2, // 1: v1.ResticUI.SetConfig:input_type -> v1.Config + 3, // 2: v1.ResticUI.AddRepo:input_type -> v1.Repo + 1, // 3: v1.ResticUI.GetEvents:input_type -> google.protobuf.Empty + 0, // 4: v1.ResticUI.ListSnapshots:input_type -> v1.ListSnapshotsRequest + 4, // 5: v1.ResticUI.PathAutocomplete:input_type -> types.StringValue + 2, // 6: v1.ResticUI.GetConfig:output_type -> v1.Config + 2, // 7: v1.ResticUI.SetConfig:output_type -> v1.Config + 2, // 8: v1.ResticUI.AddRepo:output_type -> v1.Config + 5, // 9: v1.ResticUI.GetEvents:output_type -> v1.Event + 6, // 10: v1.ResticUI.ListSnapshots:output_type -> v1.ResticSnapshotList + 7, // 11: v1.ResticUI.PathAutocomplete:output_type -> types.StringList + 6, // [6:12] is the sub-list for method output_type + 0, // [0:6] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -95,18 +180,34 @@ func file_v1_service_proto_init() { } file_v1_config_proto_init() file_v1_events_proto_init() + file_v1_restic_proto_init() + if !protoimpl.UnsafeEnabled { + file_v1_service_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ListSnapshotsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_v1_service_proto_rawDesc, NumEnums: 0, - NumMessages: 0, + NumMessages: 1, NumExtensions: 0, NumServices: 1, }, GoTypes: file_v1_service_proto_goTypes, DependencyIndexes: file_v1_service_proto_depIdxs, + MessageInfos: file_v1_service_proto_msgTypes, }.Build() File_v1_service_proto = out.File file_v1_service_proto_rawDesc = nil diff --git a/gen/go/v1/service.pb.gw.go b/gen/go/v1/service.pb.gw.go index 8835f20..be2f0d7 100644 --- a/gen/go/v1/service.pb.gw.go +++ b/gen/go/v1/service.pb.gw.go @@ -136,6 +136,40 @@ func request_ResticUI_GetEvents_0(ctx context.Context, marshaler runtime.Marshal } +func request_ResticUI_ListSnapshots_0(ctx context.Context, marshaler runtime.Marshaler, client ResticUIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListSnapshotsRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.ListSnapshots(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_ResticUI_ListSnapshots_0(ctx context.Context, marshaler runtime.Marshaler, server ResticUIServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq ListSnapshotsRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.ListSnapshots(ctx, &protoReq) + return msg, metadata, err + +} + func request_ResticUI_PathAutocomplete_0(ctx context.Context, marshaler runtime.Marshaler, client ResticUIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq types.StringValue var metadata runtime.ServerMetadata @@ -258,6 +292,31 @@ func RegisterResticUIHandlerServer(ctx context.Context, mux *runtime.ServeMux, s return }) + mux.Handle("POST", pattern_ResticUI_ListSnapshots_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/v1.ResticUI/ListSnapshots", runtime.WithHTTPPathPattern("/v1/snapshots")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_ResticUI_ListSnapshots_0(annotatedContext, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_ResticUI_ListSnapshots_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_ResticUI_PathAutocomplete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -412,6 +471,28 @@ func RegisterResticUIHandlerClient(ctx context.Context, mux *runtime.ServeMux, c }) + mux.Handle("POST", pattern_ResticUI_ListSnapshots_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + var err error + var annotatedContext context.Context + annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/v1.ResticUI/ListSnapshots", runtime.WithHTTPPathPattern("/v1/snapshots")) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_ResticUI_ListSnapshots_0(annotatedContext, inboundMarshaler, client, req, pathParams) + annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md) + if err != nil { + runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err) + return + } + + forward_ResticUI_ListSnapshots_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_ResticUI_PathAutocomplete_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -446,6 +527,8 @@ var ( pattern_ResticUI_GetEvents_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "events"}, "")) + pattern_ResticUI_ListSnapshots_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "snapshots"}, "")) + pattern_ResticUI_PathAutocomplete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"v1", "autocomplete", "path"}, "")) ) @@ -458,5 +541,7 @@ var ( forward_ResticUI_GetEvents_0 = runtime.ForwardResponseStream + forward_ResticUI_ListSnapshots_0 = runtime.ForwardResponseMessage + forward_ResticUI_PathAutocomplete_0 = runtime.ForwardResponseMessage ) diff --git a/gen/go/v1/service_grpc.pb.go b/gen/go/v1/service_grpc.pb.go index cc46df0..c2bbe64 100644 --- a/gen/go/v1/service_grpc.pb.go +++ b/gen/go/v1/service_grpc.pb.go @@ -25,6 +25,7 @@ const ( ResticUI_SetConfig_FullMethodName = "/v1.ResticUI/SetConfig" ResticUI_AddRepo_FullMethodName = "/v1.ResticUI/AddRepo" ResticUI_GetEvents_FullMethodName = "/v1.ResticUI/GetEvents" + ResticUI_ListSnapshots_FullMethodName = "/v1.ResticUI/ListSnapshots" ResticUI_PathAutocomplete_FullMethodName = "/v1.ResticUI/PathAutocomplete" ) @@ -36,6 +37,7 @@ type ResticUIClient interface { SetConfig(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Config, error) AddRepo(ctx context.Context, in *Repo, opts ...grpc.CallOption) (*Config, error) GetEvents(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (ResticUI_GetEventsClient, error) + ListSnapshots(ctx context.Context, in *ListSnapshotsRequest, opts ...grpc.CallOption) (*ResticSnapshotList, error) PathAutocomplete(ctx context.Context, in *types.StringValue, opts ...grpc.CallOption) (*types.StringList, error) } @@ -106,6 +108,15 @@ func (x *resticUIGetEventsClient) Recv() (*Event, error) { return m, nil } +func (c *resticUIClient) ListSnapshots(ctx context.Context, in *ListSnapshotsRequest, opts ...grpc.CallOption) (*ResticSnapshotList, error) { + out := new(ResticSnapshotList) + err := c.cc.Invoke(ctx, ResticUI_ListSnapshots_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *resticUIClient) PathAutocomplete(ctx context.Context, in *types.StringValue, opts ...grpc.CallOption) (*types.StringList, error) { out := new(types.StringList) err := c.cc.Invoke(ctx, ResticUI_PathAutocomplete_FullMethodName, in, out, opts...) @@ -123,6 +134,7 @@ type ResticUIServer interface { SetConfig(context.Context, *Config) (*Config, error) AddRepo(context.Context, *Repo) (*Config, error) GetEvents(*emptypb.Empty, ResticUI_GetEventsServer) error + ListSnapshots(context.Context, *ListSnapshotsRequest) (*ResticSnapshotList, error) PathAutocomplete(context.Context, *types.StringValue) (*types.StringList, error) mustEmbedUnimplementedResticUIServer() } @@ -143,6 +155,9 @@ func (UnimplementedResticUIServer) AddRepo(context.Context, *Repo) (*Config, err func (UnimplementedResticUIServer) GetEvents(*emptypb.Empty, ResticUI_GetEventsServer) error { return status.Errorf(codes.Unimplemented, "method GetEvents not implemented") } +func (UnimplementedResticUIServer) ListSnapshots(context.Context, *ListSnapshotsRequest) (*ResticSnapshotList, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListSnapshots not implemented") +} func (UnimplementedResticUIServer) PathAutocomplete(context.Context, *types.StringValue) (*types.StringList, error) { return nil, status.Errorf(codes.Unimplemented, "method PathAutocomplete not implemented") } @@ -234,6 +249,24 @@ func (x *resticUIGetEventsServer) Send(m *Event) error { return x.ServerStream.SendMsg(m) } +func _ResticUI_ListSnapshots_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListSnapshotsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ResticUIServer).ListSnapshots(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ResticUI_ListSnapshots_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ResticUIServer).ListSnapshots(ctx, req.(*ListSnapshotsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _ResticUI_PathAutocomplete_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(types.StringValue) if err := dec(in); err != nil { @@ -271,6 +304,10 @@ var ResticUI_ServiceDesc = grpc.ServiceDesc{ MethodName: "AddRepo", Handler: _ResticUI_AddRepo_Handler, }, + { + MethodName: "ListSnapshots", + Handler: _ResticUI_ListSnapshots_Handler, + }, { MethodName: "PathAutocomplete", Handler: _ResticUI_PathAutocomplete_Handler, diff --git a/go.mod b/go.mod index 44f4369..be2dab0 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.21.3 require ( github.com/gitploy-io/cronexpr v0.2.2 github.com/google/renameio v1.0.1 + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1 github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 github.com/hashicorp/go-multierror v1.1.1 go.uber.org/zap v1.26.0 diff --git a/go.sum b/go.sum index b8ebe35..39002e6 100644 --- a/go.sum +++ b/go.sum @@ -28,6 +28,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU= github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1 h1:HcUWd006luQPljE73d5sk+/VgYPGUReEVz2y1/qylwY= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.0.1/go.mod h1:w9Y7gY31krpLmrVU5ZPG9H7l9fZuRu5/3R3S3FMtVQ4= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.2 h1:dygLcbEBA+t/P7ck6a8AkXv6juQ4cK0RHBoh32jxhHM= diff --git a/internal/api/api.go b/internal/api/api.go index dca7f54..4910b13 100644 --- a/internal/api/api.go +++ b/internal/api/api.go @@ -9,18 +9,68 @@ import ( "path/filepath" v1 "github.com/garethgeorge/resticui/gen/go/v1" + "github.com/garethgeorge/resticui/internal/orchestrator" + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" "github.com/grpc-ecosystem/grpc-gateway/v2/runtime" + "go.uber.org/zap" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" ) -func serveGRPC(ctx context.Context, socket string) error { +func loggingFunc(l *zap.Logger) logging.Logger { + return logging.LoggerFunc(func(ctx context.Context, lvl logging.Level, msg string, fields ...any) { + f := make([]zap.Field, 0, len(fields)/2) + + for i := 0; i < len(fields); i += 2 { + key := fields[i] + value := fields[i+1] + + switch v := value.(type) { + case string: + f = append(f, zap.String(key.(string), v)) + case int: + f = append(f, zap.Int(key.(string), v)) + case bool: + f = append(f, zap.Bool(key.(string), v)) + default: + f = append(f, zap.Any(key.(string), v)) + } + } + + logger := l.WithOptions(zap.AddCallerSkip(1)).With(f...) + + switch lvl { + case logging.LevelDebug: + logger.Debug(msg) + case logging.LevelInfo: + logger.Info(msg) + case logging.LevelWarn: + logger.Warn(msg) + case logging.LevelError: + logger.Error(msg) + default: + panic(fmt.Sprintf("unknown level %v", lvl)) + } + }) +} + + +func serveGRPC(ctx context.Context, orchestrator *orchestrator.Orchestrator, socket string) error { lis, err := net.Listen("unix", socket) if err != nil { return fmt.Errorf("failed to listen: %w", err) } - grpcServer := grpc.NewServer() - v1.RegisterResticUIServer(grpcServer, NewServer()) + + logger := zap.L() + grpcServer := grpc.NewServer( + grpc.ChainUnaryInterceptor( + logging.UnaryServerInterceptor(loggingFunc(logger)), + ), + grpc.ChainStreamInterceptor( + logging.StreamServerInterceptor(loggingFunc(logger)), + ), + ) + v1.RegisterResticUIServer(grpcServer, NewServer(orchestrator)) go func() { <-ctx.Done() grpcServer.GracefulStop() @@ -32,7 +82,7 @@ func serveGRPC(ctx context.Context, socket string) error { return nil } -func serveHTTPHandlers(ctx context.Context, mux *runtime.ServeMux) error { +func serveHTTPHandlers(ctx context.Context, orchestrator *orchestrator.Orchestrator, mux *runtime.ServeMux) error { tmpDir, err := os.MkdirTemp("", "resticui") if err != nil { return fmt.Errorf("failed to create temp dir for unix domain socket: %w", err) @@ -49,7 +99,7 @@ func serveHTTPHandlers(ctx context.Context, mux *runtime.ServeMux) error { return fmt.Errorf("failed to register gateway: %w", err) } - if err := serveGRPC(ctx, socket); err != nil { + if err := serveGRPC(ctx, orchestrator, socket); err != nil { return err } @@ -57,8 +107,8 @@ func serveHTTPHandlers(ctx context.Context, mux *runtime.ServeMux) error { } // Handler returns an http.Handler serving the API, cancel the context to cleanly shut down the server. -func ServeAPI(ctx context.Context, mux *http.ServeMux) error { +func ServeAPI(ctx context.Context, orchestrator *orchestrator.Orchestrator, mux *http.ServeMux) error { apiMux := runtime.NewServeMux() mux.Handle("/api/", http.StripPrefix("/api", apiMux)) - return serveHTTPHandlers(ctx, apiMux) + return serveHTTPHandlers(ctx, orchestrator, apiMux) } \ No newline at end of file diff --git a/internal/api/server.go b/internal/api/server.go index 012bc6f..be5c726 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -7,18 +7,20 @@ import ( "os" "sync" "sync/atomic" - "time" "github.com/garethgeorge/resticui/gen/go/types" v1 "github.com/garethgeorge/resticui/gen/go/v1" "github.com/garethgeorge/resticui/internal/config" + "github.com/garethgeorge/resticui/internal/orchestrator" "github.com/garethgeorge/resticui/pkg/restic" "go.uber.org/zap" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/emptypb" ) type Server struct { *v1.UnimplementedResticUIServer + orchestrator *orchestrator.Orchestrator reqId atomic.Uint64 eventChannelsMu sync.Mutex @@ -27,23 +29,25 @@ type Server struct { var _ v1.ResticUIServer = &Server{} -func NewServer() *Server { +func NewServer(orchestrator *orchestrator.Orchestrator) *Server { s := &Server{ eventChannels: make(map[uint64]chan *v1.Event), + orchestrator: orchestrator, } - go func() { - for { - time.Sleep(3 * time.Second) - s.PublishEvent(&v1.Event{ - Event: &v1.Event_Log{ - Log: &v1.LogEvent{ - Message: fmt.Sprintf("event push test, it is %v", time.Now().Format(time.RFC3339)), - }, - }, - }) - } - }() + // go func() { + // // TODO: disable this when proper event sources are implemented. + // for { + // time.Sleep(3 * time.Second) + // s.PublishEvent(&v1.Event{ + // Event: &v1.Event_Log{ + // Log: &v1.LogEvent{ + // Message: fmt.Sprintf("event push test, it is %v", time.Now().Format(time.RFC3339)), + // }, + // }, + // }) + // } + // }() return s } @@ -55,8 +59,18 @@ func (s *Server) GetConfig(ctx context.Context, empty *emptypb.Empty) (*v1.Confi // SetConfig implements POST /v1/config func (s *Server) SetConfig(ctx context.Context, c *v1.Config) (*v1.Config, error) { - err := config.Default.Update(c) + existing, err := config.Default.Get() if err != nil { + return nil, fmt.Errorf("failed to check current config: %w", err) + } + + // Compare and increment modno + if existing.Modno != c.Modno { + return nil, errors.New("config modno mismatch, reload and try again") + } + c.Modno += 1 + + if err := config.Default.Update(c); err != nil { return nil, fmt.Errorf("failed to update config: %w", err) } return config.Default.Get() @@ -69,14 +83,20 @@ func (s *Server) AddRepo(ctx context.Context, repo *v1.Repo) (*v1.Config, error) return nil, fmt.Errorf("failed to get config: %w", err) } + c = proto.Clone(c).(*v1.Config) + c.Repos = append(c.Repos, repo) + + if err := config.ValidateConfig(c); err != nil { + return nil, fmt.Errorf("validation error: %w", err) + } + r := restic.NewRepo(repo) // use background context such that the init op can try to complete even if the connection is closed. if err := r.Init(context.Background()); err != nil { return nil, fmt.Errorf("failed to init repo: %w", err) } - - c.Repos = append(c.Repos, repo) + zap.S().Debug("Updating config") if err := config.Default.Update(c); err != nil { return nil, fmt.Errorf("failed to update config: %w", err) } @@ -84,6 +104,40 @@ func (s *Server) AddRepo(ctx context.Context, repo *v1.Repo) (*v1.Config, error) return c, nil } +// ListSnapshots implements GET /v1/snapshots/{repo.id}/{plan.id?} +func (s *Server) ListSnapshots(ctx context.Context, query *v1.ListSnapshotsRequest) (*v1.ResticSnapshotList, error) { + repo, err := s.orchestrator.GetRepo(query.RepoId) + if err != nil { + return nil, fmt.Errorf("failed to get repo: %w", err) + } + + var snapshots []*restic.Snapshot + if query.PlanId != "" { + var plan *v1.Plan + plan, err = s.orchestrator.GetPlan(query.PlanId) + if err != nil { + return nil, fmt.Errorf("failed to get plan %q: %w", query.PlanId, err) + } + + snapshots, err = repo.SnapshotsForPlan(ctx, plan) + } else { + snapshots, err = repo.Snapshots(ctx) + } + + if err != nil { + return nil, fmt.Errorf("failed to list snapshots: %w", err) + } + + // Transform the snapshots and return them. + var rs []*v1.ResticSnapshot + for _, snapshot := range snapshots { + rs = append(rs, snapshot.ToProto()) + } + + return &v1.ResticSnapshotList{ + Snapshots: rs, + }, nil +} // GetEvents implements GET /v1/events func (s *Server) GetEvents(_ *emptypb.Empty, stream v1.ResticUI_GetEventsServer) error { diff --git a/internal/config/config.go b/internal/config/config.go index f987bbc..9880cc5 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,18 +1,23 @@ package config import ( + "errors" "flag" "fmt" "os" "path" + "sync" v1 "github.com/garethgeorge/resticui/gen/go/v1" ) +var ErrConfigNotFound = fmt.Errorf("config not found") var configDirFlag = flag.String("config_dir", "", "The directory to store the config file") -var Default ConfigStore = &YamlFileStore{ - Path: path.Join(configDir(*configDirFlag), "config.yaml"), +var Default ConfigStore = &CachingValidatingStore{ + ConfigStore: &YamlFileStore{ + Path: path.Join(configDir(*configDirFlag), "config.yaml"), + }, } type ConfigStore interface { @@ -42,4 +47,51 @@ func configDir(override string) string { } return fmt.Sprintf("%v/.config/resticui", home) +} + +type CachingValidatingStore struct { + ConfigStore + mu sync.Mutex + config *v1.Config +} + +func (c *CachingValidatingStore) Get() (*v1.Config, error) { + c.mu.Lock() + defer c.mu.Unlock() + + if c.config != nil { + return c.config, nil + } + + config, err := c.ConfigStore.Get() + if err != nil { + if errors.Is(err, ErrConfigNotFound) { + c.config = NewDefaultConfig() + return c.config, nil + } + return c.config, err + } + + if err := ValidateConfig(config); err != nil { + return nil, err + } + + c.config = config + return config, nil +} + +func (c *CachingValidatingStore) Update(config *v1.Config) error { + c.mu.Lock() + defer c.mu.Unlock() + + if err := ValidateConfig(config); err != nil { + return err + } + + if err := c.ConfigStore.Update(config); err != nil { + return err + } + + c.config = config + return nil } \ No newline at end of file diff --git a/internal/config/config_test.go b/internal/config/config_test.go new file mode 100644 index 0000000..601aa5a --- /dev/null +++ b/internal/config/config_test.go @@ -0,0 +1,114 @@ +package config + +import ( + "strings" + "testing" + + v1 "github.com/garethgeorge/resticui/gen/go/v1" + "google.golang.org/protobuf/proto" +) + + +func TestConfig(t *testing.T) { + dir := t.TempDir() + + testRepo := &v1.Repo{ + Id: "test-repo", + Uri: "/tmp/test", + Password: "test", + } + + testPlan := &v1.Plan{ + Id: "test-plan", + Repo: "test-repo", + Paths: []string{"/tmp/foo"}, + Cron: "* * * * *", + } + + tests := []struct { + name string + config *v1.Config + wantErr bool + wantErrContains string + store ConfigStore + }{ + { + name: "default config", + config: NewDefaultConfig(), + store: &CachingValidatingStore{ConfigStore: &YamlFileStore{Path: dir + "/default-config.yaml"}}, + }, + { + name: "simple valid config", + config: &v1.Config{ + Repos: []*v1.Repo{testRepo}, + Plans: []*v1.Plan{testPlan}, + }, + store: &CachingValidatingStore{ConfigStore: &YamlFileStore{Path: dir + "/valid-config.yaml"}}, + }, + { + name: "plan references non-existent repo", + config: &v1.Config{ + Plans: []*v1.Plan{testPlan}, + }, + store: &CachingValidatingStore{ConfigStore: &YamlFileStore{Path: dir + "/invalid-config.yaml"}}, + wantErr: true, + wantErrContains: "repo \"test-repo\" not found", + }, + { + name: "repo with duplicate id", + config: &v1.Config{ + Repos: []*v1.Repo{ + testRepo, + testRepo, + }, + }, + store: &CachingValidatingStore{ConfigStore: &YamlFileStore{Path: dir + "/invalid-config2.yaml"}}, + wantErr: true, + wantErrContains: "repo test-repo: duplicate id", + }, + { + name: "plan with bad cron", + config: &v1.Config{ + Repos: []*v1.Repo{ + testRepo, + }, + Plans: []*v1.Plan{ + { + Id: "test-plan", + Repo: "test-repo", + Paths: []string{"/tmp/foo"}, + Cron: "bad cron", + }, + }, + }, + store: &CachingValidatingStore{ConfigStore: &YamlFileStore{Path: dir + "/invalid-config3.yaml"}}, + wantErr: true, + wantErrContains: "invalid cron \"bad cron\"", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + err := tc.store.Update(tc.config) + if (err != nil) != tc.wantErr { + t.Errorf("Config.Update() error = %v, wantErr %v", err, tc.wantErr) + } + + if tc.wantErrContains != "" && (err == nil || !strings.Contains(err.Error(), tc.wantErrContains)) { + t.Errorf("Config.Update() error = %v, wantErrContains %v", err, tc.wantErrContains) + } + + if err == nil { + config, err := tc.store.Get() + if err != nil { + t.Errorf("Config.Get() error = %v, wantErr nil", err) + } + + if !proto.Equal(config, tc.config) { + t.Errorf("Config.Get() = %v, want %v", config, tc.config) + } + } + }) + } +} \ No newline at end of file diff --git a/internal/config/validate.go b/internal/config/validate.go index da8aab0..7253a20 100644 --- a/internal/config/validate.go +++ b/internal/config/validate.go @@ -11,7 +11,7 @@ import ( "github.com/hashicorp/go-multierror" ) -func validateConfig(c *v1.Config) error { +func ValidateConfig(c *v1.Config) error { var err error repos := make(map[string]*v1.Repo) if c.Repos != nil { @@ -19,15 +19,17 @@ func validateConfig(c *v1.Config) error { if e := validateRepo(repo); e != nil { err = multierror.Append(e, fmt.Errorf("repo %s: %w", repo.GetId(), err)) } + if _, ok := repos[repo.GetId()]; ok { + err = multierror.Append(err, fmt.Errorf("repo %s: duplicate id", repo.GetId())) + } repos[repo.GetId()] = repo } } if c.Plans != nil { for _, plan := range c.Plans { - err := validatePlan(plan, repos); - if err != nil { - err = multierror.Append(err, fmt.Errorf("plan %s: %w", plan.GetId(), err)) + if e := validatePlan(plan, repos); e != nil { + err = multierror.Append(err, fmt.Errorf("plan %s: %w", plan.GetId(), e)) } } } @@ -73,7 +75,7 @@ func validatePlan(plan *v1.Plan, repos map[string]*v1.Repo) error { } - if _, e := cronexpr.Parse(plan.GetCron()); err != nil { + if _, e := cronexpr.Parse(plan.GetCron()); e != nil { err = multierror.Append(err, fmt.Errorf("invalid cron %q: %w", plan.GetCron(), e)) } diff --git a/internal/config/yamlstore.go b/internal/config/yamlstore.go index 830f36c..ac3ad47 100644 --- a/internal/config/yamlstore.go +++ b/internal/config/yamlstore.go @@ -17,31 +17,22 @@ import ( type YamlFileStore struct { Path string mu sync.Mutex - config *v1.Config } var _ ConfigStore = &YamlFileStore{} func (f *YamlFileStore) Get() (*v1.Config, error) { f.mu.Lock() - - if f.config != nil { - f.mu.Unlock() - return f.config, nil - } + defer f.mu.Unlock() data, err := os.ReadFile(f.Path) if err != nil { if errors.Is(err, os.ErrNotExist) { - f.config = NewDefaultConfig() - f.mu.Unlock() - f.Update(f.config) - return f.config, nil + return nil, ErrConfigNotFound } return nil, fmt.Errorf("failed to read config file: %w", err) } - defer f.mu.Unlock() data, err = yamlToJson(data) if err != nil { @@ -54,19 +45,18 @@ func (f *YamlFileStore) Get() (*v1.Config, error) { return nil, fmt.Errorf("failed to unmarshal config: %w", err) } - if err := validateConfig(&config); err != nil { + if err := ValidateConfig(&config); err != nil { return nil, fmt.Errorf("invalid config: %w", err) } - f.config = &config - return f.config, nil + return &config, nil } func (f *YamlFileStore) Update(config *v1.Config) error { f.mu.Lock() defer f.mu.Unlock() - if err := validateConfig(config); err != nil { + if err := ValidateConfig(config); err != nil { return fmt.Errorf("invalid config: %w", err) } @@ -90,7 +80,6 @@ func (f *YamlFileStore) Update(config *v1.Config) error { return fmt.Errorf("failed to write config file: %w", err) } - f.config = config return nil } diff --git a/internal/orchestrator/orchestrator.go b/internal/orchestrator/orchestrator.go new file mode 100644 index 0000000..3ff57e2 --- /dev/null +++ b/internal/orchestrator/orchestrator.go @@ -0,0 +1,121 @@ +package orchestrator + +import ( + "errors" + "fmt" + "sync" + + v1 "github.com/garethgeorge/resticui/gen/go/v1" + "github.com/garethgeorge/resticui/internal/config" + "github.com/garethgeorge/resticui/pkg/restic" + "google.golang.org/protobuf/proto" +) + +var ErrRepoNotFound = errors.New("repo not found") +var ErrRepoInitializationFailed = errors.New("repo initialization failed") +var ErrPlanNotFound = errors.New("plan not found") + + + +// Orchestrator is responsible for managing repos and backups. +type Orchestrator struct { + configProvider config.ConfigStore + repoPool *resticRepoPool +} + +func NewOrchestrator(configProvider config.ConfigStore) *Orchestrator { + return &Orchestrator{ + configProvider: configProvider, + repoPool: newResticRepoPool(configProvider), + } +} + +func (o *Orchestrator) GetRepo(repoId string) (repo *RepoOrchestrator, err error) { + r, err := o.repoPool.GetRepo(repoId) + if err != nil { + return nil, fmt.Errorf("failed to get repo %q: %w", repoId, err) + } + return r, nil +} + +func (o *Orchestrator) GetPlan(planId string) (*v1.Plan, error) { + cfg, err := o.configProvider.Get() + if err != nil { + return nil, fmt.Errorf("failed to get config: %w", err) + } + + if cfg.Plans == nil { + return nil, ErrPlanNotFound + } + + for _, p := range cfg.Plans { + if p.Id == planId { + return p, nil + } + } + + return nil, ErrPlanNotFound +} + +// resticRepoPool caches restic repos. +type resticRepoPool struct { + mu sync.Mutex + repos map[string]*RepoOrchestrator + configProvider config.ConfigStore +} + + +func newResticRepoPool(configProvider config.ConfigStore) *resticRepoPool { + return &resticRepoPool{ + repos: make(map[string]*RepoOrchestrator), + configProvider: configProvider, + } +} + +func (rp *resticRepoPool) GetRepo(repoId string) (repo *RepoOrchestrator, err error) { + cfg, err := rp.configProvider.Get() + if err != nil { + return nil, fmt.Errorf("failed to get config: %w", err) + } + + rp.mu.Lock() + defer rp.mu.Unlock() + + if cfg.Repos == nil { + return nil, ErrRepoNotFound + } + + var repoProto *v1.Repo + for _, r := range cfg.Repos { + if r.GetId() == repoId { + repoProto = r + } + } + + if repoProto == nil { + return nil, ErrRepoNotFound + } + + // Check if we already have a repo for this id, if we do return it. + repo, ok := rp.repos[repoId] + if ok && proto.Equal(repo.repoConfig, repoProto) { + return repo, nil + } + delete(rp.repos, repoId); + + var opts []restic.GenericOption + if len(repoProto.GetEnv()) > 0 { + opts = append(opts, restic.WithEnv(repoProto.GetEnv()...)) + } + if len(repoProto.GetFlags()) > 0 { + opts = append(opts, restic.WithFlags(repoProto.GetFlags()...)) + } + + // Otherwise create a new repo. + repo = &RepoOrchestrator{ + repoConfig: repoProto, + repo: restic.NewRepo(repoProto, opts...), + } + rp.repos[repoId] = repo + return repo, nil +} diff --git a/internal/orchestrator/repo.go b/internal/orchestrator/repo.go new file mode 100644 index 0000000..595d33c --- /dev/null +++ b/internal/orchestrator/repo.go @@ -0,0 +1,108 @@ +package orchestrator + +import ( + "context" + "fmt" + "slices" + "sort" + "sync" + "time" + + v1 "github.com/garethgeorge/resticui/gen/go/v1" + "github.com/garethgeorge/resticui/pkg/restic" +) + +// RepoOrchestrator is responsible for managing a single repo. +type RepoOrchestrator struct { + mu sync.Mutex + + repoConfig *v1.Repo + repo *restic.Repo + + snapshotsAge time.Time + snapshots []*restic.Snapshot +} + +func (r *RepoOrchestrator) updateSnapshotsIfNeeded(ctx context.Context) error { + if time.Since(r.snapshotsAge) > 10 * time.Minute { + r.snapshots = nil + } + + if r.snapshots != nil { + return nil + } + + snapshots, err := r.repo.Snapshots(ctx, restic.WithPropagatedEnvVars(restic.EnvToPropagate...)) + if err != nil { + return fmt.Errorf("failed to update snapshots: %w", err) + } + + sort.SliceStable(snapshots, func(i, j int) bool { + return snapshots[i].Time > snapshots[j].Time + }) + + r.snapshots = snapshots + + return nil +} + +func (r *RepoOrchestrator) Snapshots(ctx context.Context) ([]*restic.Snapshot, error) { + r.mu.Lock() + defer r.mu.Unlock() + + if err := r.updateSnapshotsIfNeeded(ctx); err != nil { + return nil, err + } + + return r.snapshots, nil +} + +func (r *RepoOrchestrator) SnapshotsForPlan(ctx context.Context, plan *v1.Plan) ([]*restic.Snapshot, error) { + r.mu.Lock() + defer r.mu.Unlock() + + if err := r.updateSnapshotsIfNeeded(ctx); err != nil { + return nil, err + } + + return filterSnapshotsForPlan(r.snapshots, plan), nil +} + +func (r *RepoOrchestrator) Backup(ctx context.Context, plan *v1.Plan, progressCallback func(event *restic.BackupProgressEntry)) error { + snapshots, err := r.SnapshotsForPlan(ctx, plan) + if err != nil { + return err + } + + var opts []restic.BackupOption + opts = append(opts, restic.WithBackupPaths(plan.Paths...)) + opts = append(opts, restic.WithBackupExcludes(plan.Excludes...)) + opts = append(opts, restic.WithBackupTags(tagForPlan(plan))) + + if len(snapshots) > 0 { + // TODO: design a test strategy to verify that the backup parent is used correctly. + opts = append(opts, restic.WithBackupParent(snapshots[len(snapshots) - 1].Id)) + } + + panic("not yet implemented") +} + +func filterSnapshotsForPlan(snapshots []*restic.Snapshot, plan *v1.Plan) []*restic.Snapshot { + wantTag := tagForPlan(plan) + var filtered []*restic.Snapshot + for _, snapshot := range snapshots { + if snapshot.Tags == nil { + continue + } + + if slices.Contains(snapshot.Tags, wantTag) { + filtered = append(filtered, snapshot) + } + } + + return filtered +} + +func tagForPlan(plan *v1.Plan) string { + return fmt.Sprintf("plan:%s", plan.Id) +} diff --git a/pkg/restic/outputs.go b/pkg/restic/outputs.go index 044a6bc..efe4ee5 100644 --- a/pkg/restic/outputs.go +++ b/pkg/restic/outputs.go @@ -7,6 +7,9 @@ import ( "io" "os/exec" "slices" + "time" + + v1 "github.com/garethgeorge/resticui/gen/go/v1" ) type LsEntry struct { @@ -33,6 +36,23 @@ type Snapshot struct { Parent string `json:"parent"` } +func (s *Snapshot) ToProto() *v1.ResticSnapshot { + t, err := time.Parse(time.RFC3339Nano, s.Time) + if err != nil { + t = time.Unix(0, 0) + } + return &v1.ResticSnapshot{ + Id: s.Id, + UnixTimeMs: t.UnixMilli(), + Tree: s.Tree, + Paths: s.Paths, + Hostname: s.Hostname, + Username: s.Username, + Tags: s.Tags, + Parent: s.Parent, + } +} + type BackupProgressEntry struct { // Common fields MessageType string `json:"message_type"` // "summary" or "status" diff --git a/pkg/restic/outputs_test.go b/pkg/restic/outputs_test.go index da27506..63aafb4 100644 --- a/pkg/restic/outputs_test.go +++ b/pkg/restic/outputs_test.go @@ -27,6 +27,7 @@ func TestReadBackupProgressEntries(t *testing.T) { } } + func TestReadLs(t *testing.T) { testInput := `{"time":"2023-11-10T19:14:17.053824063-08:00","tree":"3e2918b261948e69602ee9504b8f475bcc7cdc4dcec0b3f34ecdb014287d07b2","paths":["/resticui"],"hostname":"pop-os","username":"dontpanic","uid":1000,"gid":1000,"id":"db155169d788e6e432e320aedbdff5a54cc439653093bb56944a67682528aa52","short_id":"db155169","struct_type":"snapshot"} {"name":".git","type":"dir","path":"/.git","uid":1000,"gid":1000,"mode":2147484157,"mtime":"2023-11-10T18:32:38.156599473-08:00","atime":"2023-11-10T18:32:38.156599473-08:00","ctime":"2023-11-10T18:32:38.156599473-08:00","struct_type":"node"} @@ -45,4 +46,41 @@ func TestReadLs(t *testing.T) { if len(entries) != 3 { t.Errorf("wanted 3 entries, got: %d", len(entries)) } +} + +func TestSnapshotToProto(t *testing.T) { + snapshot := &Snapshot{ + Id: "db155169d788e6e432e320aedbdff5a54cc439653093bb56944a67682528aa52", + Time: "2023-11-10T19:14:17.053824063-08:00", + Tree: "3e2918b261948e69602ee9504b8f475bcc7cdc4dcec0b3f34ecdb014287d07b2", + Paths: []string{"/resticui"}, + Hostname: "pop-os", + Username: "dontpanic", + Tags: []string{}, + Parent: "", + } + + proto := snapshot.ToProto() + + if proto.Id != snapshot.Id { + t.Errorf("wanted id %q, got: %q", snapshot.Id, proto.Id) + } + if proto.Tree != snapshot.Tree { + t.Errorf("wanted tree %q, got: %q", snapshot.Tree, proto.Tree) + } + if proto.Hostname != snapshot.Hostname { + t.Errorf("wanted hostname %q, got: %q", snapshot.Hostname, proto.Hostname) + } + if proto.Username != snapshot.Username { + t.Errorf("wanted username %q, got: %q", snapshot.Username, proto.Username) + } + if len(proto.Tags) != len(snapshot.Tags) { + t.Errorf("wanted %d tags, got: %d", len(snapshot.Tags), len(proto.Tags)) + } + if proto.Parent != snapshot.Parent { + t.Errorf("wanted parent %q, got: %q", snapshot.Parent, proto.Parent) + } + if proto.UnixTimeMs != 1699672457053 { + t.Errorf("wanted unix time %d, got: %d", 1699672457053, proto.UnixTimeMs) + } } \ No newline at end of file diff --git a/pkg/restic/restic.go b/pkg/restic/restic.go index 6472261..efb3bf0 100644 --- a/pkg/restic/restic.go +++ b/pkg/restic/restic.go @@ -9,6 +9,7 @@ import ( "io" "os" "os/exec" + "strings" "sync" v1 "github.com/garethgeorge/resticui/gen/go/v1" @@ -25,6 +26,7 @@ type Repo struct { extraEnv []string } +// NewRepo instantiates a new repository. TODO: should not accept a v1.Repo func NewRepo(repo *v1.Repo, opts ...GenericOption) *Repo { opt := &GenericOpts{} for _, o := range opts { @@ -63,7 +65,9 @@ func (r *Repo) init(ctx context.Context) error { cmd.Env = append(cmd.Env, r.buildEnv()...) if output, err := cmd.CombinedOutput(); err != nil { - return NewCmdError(cmd, output, err) + if !strings.Contains(string(output), "config file already exists") { + return NewCmdError(cmd, output, err) + } } r.initialized = true @@ -242,6 +246,12 @@ func WithBackupTags(tags ...string) BackupOption { } } +func WithBackupParent(parent string) BackupOption { + return func(opts *BackupOpts) { + opts.extraArgs = append(opts.extraArgs, "--parent", parent) + } +} + type GenericOpts struct { extraArgs []string extraEnv []string diff --git a/pkg/restic/restic_test.go b/pkg/restic/restic_test.go index f854172..3a9df43 100644 --- a/pkg/restic/restic_test.go +++ b/pkg/restic/restic_test.go @@ -140,6 +140,13 @@ func TestSnapshot(t *testing.T) { if len(snapshots) != tc.count { t.Errorf("wanted %d snapshots, got: %d", tc.count, len(snapshots)) } + + // Ensure that snapshot timestamps are set, this is critical for correct ordering in the orchestrator. + for _, snapshot := range snapshots { + if p := snapshot.ToProto(); p.UnixTimeMs == 0 { + t.Errorf("wanted snapshot time to be non-zero, got: %v", p.UnixTimeMs) + } + } }) } } diff --git a/proto/v1/config.proto b/proto/v1/config.proto index 6ae089e..398e346 100644 --- a/proto/v1/config.proto +++ b/proto/v1/config.proto @@ -5,14 +5,15 @@ package v1; option go_package = "github.com/garethgeorge/resticui/go/proto/v1"; message Config { - int32 version = 1; - repeated Repo repos = 3 [json_name="repos"]; - repeated Plan plans = 4 [json_name="plans"]; -} + // modification number, used for read-modify-write consistency in the UI. Incremented on every write. + int32 modno = 1 [json_name="modno"]; -message User { - string name = 1; - string password = 2; // plaintext password + // override the hostname tagged on backups. If provided it will be used in addition to tags to group backups. + string host_override = 2 [json_name="host_override"]; + + repeated Repo repos = 3 [json_name="repos"]; + + repeated Plan plans = 4 [json_name="plans"]; } message Repo { diff --git a/proto/v1/events.proto b/proto/v1/events.proto index 7e85a1c..9339dc5 100644 --- a/proto/v1/events.proto +++ b/proto/v1/events.proto @@ -6,22 +6,22 @@ option go_package = "github.com/garethgeorge/resticui/go/proto/v1"; message Event { // timestamp is the number of milliseconds since the Unix epoch. - int64 timestamp = 1 [json_name="timestamp"]; + int64 timestamp = 1; oneof event { - LogEvent log = 3 [json_name="log"]; - BackupStatusEvent backup_status_change = 4 [json_name="backup_status"]; + LogEvent log = 3; + BackupStatusEvent backup_status_change = 4; } } message LogEvent { - string message = 1 [json_name="message"]; + string message = 1; } message BackupStatusEvent { - string plan = 1 [json_name="plan"]; - Status status = 2 [json_name="status"]; - uint32 percent = 3 [json_name="percent"]; + string plan = 1; + Status status = 2; + uint32 percent = 3; } enum Status { diff --git a/proto/v1/restic.proto b/proto/v1/restic.proto index b06f252..d21f53a 100644 --- a/proto/v1/restic.proto +++ b/proto/v1/restic.proto @@ -5,43 +5,47 @@ package v1; option go_package = "github.com/garethgeorge/resticui/go/proto/v1"; message ResticSnapshot { - string id = 1 [json_name = "id"]; - int64 unix_time_ms = 2 [json_name = "time"]; - string hostname = 3 [json_name = "hostname"]; - string username = 4 [json_name = "username"]; - string tree = 5 [json_name = "tree"]; // tree hash - string parent = 6 [json_name = "parent"]; // parent snapshot's id - repeated string paths = 7 [json_name = "paths"]; - repeated string tags = 8 [json_name = "tags"]; + string id = 1; + int64 unix_time_ms = 2; + string hostname = 3; + string username = 4; + string tree = 5; // tree hash + string parent = 6; // parent snapshot's id + repeated string paths = 7; + repeated string tags = 8; +} + +message ResticSnapshotList { + repeated ResticSnapshot snapshots = 1; } message BackupProgressEntry { oneof entry { - BackupProgressStatusEntry status = 1 [json_name = "status"]; - BackupProgressSummary summary = 2 [json_name = "summary"]; + BackupProgressStatusEntry status = 1; + BackupProgressSummary summary = 2; } } message BackupProgressStatusEntry { - double percent_done = 1 [json_name = "percent"]; // 0.0 - 1.0 - int64 total_files = 2 [json_name = "total_files"]; - int64 total_bytes = 3 [json_name = "total_bytes"]; - int64 files_done = 4 [json_name = "files_done"]; - int64 bytes_done = 5 [json_name = "bytes_done"]; + double percent_done = 1; // 0.0 - 1.0 + int64 total_files = 2; + int64 total_bytes = 3; + int64 files_done = 4; + int64 bytes_done = 5; } message BackupProgressSummary { - int64 files_new = 1 [json_name = "files_new"]; - int64 files_changed = 2 [json_name = "files_changed"]; - int64 files_unmodified = 3 [json_name = "files_unmodified"]; - int64 dirs_new = 4 [json_name = "dirs_new"]; - int64 dirs_changed = 5 [json_name = "dirs_changed"]; - int64 dirs_unmodified = 6 [json_name = "dirs_unmodified"]; - int64 data_blobs = 7 [json_name = "data_blobs"]; - int64 tree_blobs = 8 [json_name = "tree_blobs"]; - int64 data_added = 9 [json_name = "data_added"]; - int64 total_files_processed = 10 [json_name = "total_files_processed"]; - int64 total_bytes_processed = 11 [json_name = "total_bytes_processed"]; - int64 total_duration = 12 [json_name = "total_duration"]; - string snapshot_id = 13 [json_name = "snapshot_id"]; + int64 files_new = 1; + int64 files_changed = 2; + int64 files_unmodified = 3; + int64 dirs_new = 4; + int64 dirs_changed = 5; + int64 dirs_unmodified = 6; + int64 data_blobs = 7; + int64 tree_blobs = 8; + int64 data_added = 9; + int64 total_files_processed = 10; + int64 total_bytes_processed = 11; + int64 total_duration = 12; + string snapshot_id = 13; } diff --git a/proto/v1/service.proto b/proto/v1/service.proto index baff26a..4991628 100644 --- a/proto/v1/service.proto +++ b/proto/v1/service.proto @@ -6,6 +6,7 @@ option go_package = "github.com/garethgeorge/resticui/go/proto/v1"; import "v1/config.proto"; import "v1/events.proto"; +import "v1/restic.proto"; import "types/value.proto"; import "google/protobuf/empty.proto"; import "google/api/annotations.proto"; @@ -37,6 +38,13 @@ service ResticUI { }; } + rpc ListSnapshots(ListSnapshotsRequest) returns (ResticSnapshotList) { + option (google.api.http) = { + post: "/v1/snapshots" + body: "*" + }; + } + rpc PathAutocomplete (types.StringValue) returns (types.StringList) { option (google.api.http) = { post: "/v1/autocomplete/path" @@ -44,3 +52,8 @@ service ResticUI { }; } } + +message ListSnapshotsRequest { + string repo_id = 1; + string plan_id = 2; +} diff --git a/webui/gen/ts/v1/config.pb.ts b/webui/gen/ts/v1/config.pb.ts index 3144820..0a03211 100644 --- a/webui/gen/ts/v1/config.pb.ts +++ b/webui/gen/ts/v1/config.pb.ts @@ -4,16 +4,12 @@ * This file is a generated Typescript file for GRPC Gateway, DO NOT MODIFY */ export type Config = { - version?: number + modno?: number + hostOverride?: string repos?: Repo[] plans?: Plan[] } -export type User = { - name?: string - password?: string -} - export type Repo = { id?: string uri?: string diff --git a/webui/gen/ts/v1/restic.pb.ts b/webui/gen/ts/v1/restic.pb.ts index 2368423..f0c5966 100644 --- a/webui/gen/ts/v1/restic.pb.ts +++ b/webui/gen/ts/v1/restic.pb.ts @@ -23,6 +23,10 @@ export type ResticSnapshot = { tags?: string[] } +export type ResticSnapshotList = { + snapshots?: ResticSnapshot[] +} + type BaseBackupProgressEntry = { } diff --git a/webui/gen/ts/v1/service.pb.ts b/webui/gen/ts/v1/service.pb.ts index 8cd208b..5b75e6c 100644 --- a/webui/gen/ts/v1/service.pb.ts +++ b/webui/gen/ts/v1/service.pb.ts @@ -9,6 +9,12 @@ import * as GoogleProtobufEmpty from "../google/protobuf/empty.pb" import * as TypesValue from "../types/value.pb" import * as V1Config from "./config.pb" import * as V1Events from "./events.pb" +import * as V1Restic from "./restic.pb" +export type ListSnapshotsRequest = { + repoId?: string + planId?: string +} + export class ResticUI { static GetConfig(req: GoogleProtobufEmpty.Empty, initReq?: fm.InitReq): Promise { return fm.fetchReq(`/v1/config?${fm.renderURLSearchParams(req, [])}`, {...initReq, method: "GET"}) @@ -22,6 +28,9 @@ export class ResticUI { static GetEvents(req: GoogleProtobufEmpty.Empty, entityNotifier?: fm.NotifyStreamEntityArrival, initReq?: fm.InitReq): Promise { return fm.fetchStreamingRequest(`/v1/events?${fm.renderURLSearchParams(req, [])}`, entityNotifier, {...initReq, method: "GET"}) } + static ListSnapshots(req: ListSnapshotsRequest, initReq?: fm.InitReq): Promise { + return fm.fetchReq(`/v1/snapshots`, {...initReq, method: "POST", body: JSON.stringify(req, fm.replacer)}) + } static PathAutocomplete(req: TypesValue.StringValue, initReq?: fm.InitReq): Promise { return fm.fetchReq(`/v1/autocomplete/path`, {...initReq, method: "POST", body: JSON.stringify(req, fm.replacer)}) } diff --git a/webui/src/components/URIAutocomplete.tsx b/webui/src/components/URIAutocomplete.tsx index c2d0cba..92783eb 100644 --- a/webui/src/components/URIAutocomplete.tsx +++ b/webui/src/components/URIAutocomplete.tsx @@ -5,7 +5,9 @@ import { StringList } from "../../gen/ts/types/value.pb"; let timeout: NodeJS.Timeout | undefined = undefined; -export const URIAutocomplete = (props: React.PropsWithChildren) => { +export const URIAutocomplete = ( + props: React.PropsWithChildren<{ disabled: boolean }> +) => { const [value, setValue] = useState(""); const [options, setOptions] = useState<{ value: string }[]>([]); const [showOptions, setShowOptions] = useState<{ value: string }[]>([]); diff --git a/webui/src/state/config.ts b/webui/src/state/config.ts index a2baee4..c951939 100644 --- a/webui/src/state/config.ts +++ b/webui/src/state/config.ts @@ -17,7 +17,7 @@ export const addRepo = async (repo: Repo): Promise => { }); }; -export const setConfig = async (config: Config): Promise => { +export const updateConfig = async (config: Config): Promise => { return await ResticUI.SetConfig(config, { pathPrefix: "/api/", }); diff --git a/webui/src/views/AddRepoModel.tsx b/webui/src/views/AddRepoModel.tsx index 553a017..885e72b 100644 --- a/webui/src/views/AddRepoModel.tsx +++ b/webui/src/views/AddRepoModel.tsx @@ -14,20 +14,26 @@ import { URIAutocomplete } from "../components/URIAutocomplete"; import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons"; import { useAlertApi } from "../components/Alerts"; import { ResticUI } from "../../gen/ts/v1/service.pb"; +import { + addRepo, + configState, + fetchConfig, + updateConfig, +} from "../state/config"; +import { useRecoilState, useSetRecoilState } from "recoil"; export const AddRepoModel = ({ template, }: { template: Partial | null; }) => { + const setConfig = useSetRecoilState(configState); const [confirmLoading, setConfirmLoading] = useState(false); const showModal = useShowModal(); const alertsApi = useAlertApi()!; const [form] = Form.useForm(); - template = template || {}; - - const handleOk = () => { + const handleOk = async () => { const errors = form .getFieldsError() .map((e) => e.errors) @@ -40,22 +46,43 @@ export const AddRepoModel = ({ const repo = form.getFieldsValue() as Repo; - if (template === null) { - // We are in the create repo flow, create the new repo via the service - ResticUI.AddRepo(repo, { - pathPrefix: "/api", - }) - .then((res) => { - showModal(null); - alertsApi.success("Added repo " + repo.uri); - }) - .catch((e) => { - alertsApi.error("Error adding repo: " + e.message, 15); - }) - .finally(() => { - setConfirmLoading(false); - }); + if (template !== null) { + // We are in the edit repo flow, update the repo in the config + try { + let config = await fetchConfig(); + const idx = config.repos!.findIndex((r) => r.id === template!.id); + if (idx === -1) { + alertsApi.error("Can't update repo, not found"); + return; + } + config.repos![idx] = { ...repo }; + setConfig(await updateConfig(config)); + showModal(null); + alertsApi.success("Updated repo " + repo.uri); + + // Update the snapshots for the repo + await ResticUI.ListSnapshots( + { + repoId: repo.id, + }, + { pathPrefix: "/api" } + ); + } catch (e: any) { + alertsApi.error("Error updating repo: " + e.message, 15); + } finally { + setConfirmLoading(false); + } } else { + // We are in the create repo flow, create the new repo via the service + try { + setConfig(await addRepo(repo)); + showModal(null); + alertsApi.success("Added repo " + repo.uri); + } catch (e: any) { + alertsApi.error("Error adding repo: " + e.message, 15); + } finally { + setConfirmLoading(false); + } } }; @@ -67,7 +94,7 @@ export const AddRepoModel = ({ <> - + {/* Repo.uri */} @@ -118,8 +145,8 @@ export const AddRepoModel = ({ hasFeedback name="uri" - label="Repo URI" - initialValue={template.id} + label="Repository URI (e.g. ./local-path or s3://bucket-name/path)" + initialValue={template && template.uri} validateTrigger={["onChange", "onBlur"]} rules={[ { @@ -128,7 +155,7 @@ export const AddRepoModel = ({ }, ]} > - + @@ -137,7 +164,7 @@ export const AddRepoModel = ({ hasFeedback name="password" label="Password" - initialValue={template.password} + initialValue={template && template.password} validateTrigger={["onChange", "onBlur"]} rules={[ { @@ -150,7 +177,7 @@ export const AddRepoModel = ({ }, ]} > - + {/* Repo.env */} @@ -164,7 +191,7 @@ export const AddRepoModel = ({ }, }, ]} - initialValue={[]} + initialValue={(template && template.env) || undefined} > {(fields, { add, remove }, { errors }) => ( <> diff --git a/webui/src/views/App.tsx b/webui/src/views/App.tsx index 377e825..5f25d0b 100644 --- a/webui/src/views/App.tsx +++ b/webui/src/views/App.tsx @@ -11,7 +11,7 @@ import { configState, fetchConfig } from "../state/config"; import { useRecoilState } from "recoil"; import { Config } from "../../gen/ts/v1/config.pb"; import { AlertContextProvider, useAlertApi } from "../components/Alerts"; -import { useShowModal, useShowSpinner } from "../components/ModalManager"; +import { useShowModal } from "../components/ModalManager"; import { AddPlanModal } from "./AddPlanModel"; import { AddRepoModel } from "./AddRepoModel"; @@ -41,6 +41,7 @@ export const App: React.FC = () => { }); }, []); + console.log("rerender config ", config); const items = getSidenavItems(config); return ( diff --git a/static/static.go b/webui/static.go similarity index 54% rename from static/static.go rename to webui/static.go index 9845722..3ddcab1 100644 --- a/static/static.go +++ b/webui/static.go @@ -2,8 +2,7 @@ package static import ( "embed" - _ "embed" ) -//go:embed * +//go:embed dist/*.js dist/*.css dist/*.html var FS embed.FS \ No newline at end of file