mirror of
https://github.com/garethgeorge/backrest.git
synced 2026-05-04 20:10:36 +00:00
feat: SFTP configuration UI (enabled for sftp: URIs) with support for SSH key bootstrapping (#961)
Release Please / release-please (push) Has been cancelled
Release Preview / call-reusable-release (push) Has been cancelled
Test / test-nix (push) Has been cancelled
Test / test-win (push) Has been cancelled
Update Restic / update-restic-version (push) Has been cancelled
Release Please / release-please (push) Has been cancelled
Release Preview / call-reusable-release (push) Has been cancelled
Test / test-nix (push) Has been cancelled
Test / test-win (push) Has been cancelled
Update Restic / update-restic-version (push) Has been cancelled
Co-authored-by: Gareth <garethgeorge97@gmail.com>
This commit is contained in:
+466
-133
@@ -79,7 +79,7 @@ func (x DoRepoTaskRequest_Task) Number() protoreflect.EnumNumber {
|
||||
|
||||
// Deprecated: Use DoRepoTaskRequest_Task.Descriptor instead.
|
||||
func (DoRepoTaskRequest_Task) EnumDescriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{1, 0}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{6, 0}
|
||||
}
|
||||
|
||||
// OpSelector is a message that can be used to select operations e.g. by query.
|
||||
@@ -183,6 +183,306 @@ func (x *OpSelector) GetModnoGte() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
type SetupSftpRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Host string `protobuf:"bytes,1,opt,name=host,proto3" json:"host,omitempty"`
|
||||
Port string `protobuf:"bytes,2,opt,name=port,proto3" json:"port,omitempty"`
|
||||
Username string `protobuf:"bytes,3,opt,name=username,proto3" json:"username,omitempty"`
|
||||
Password *string `protobuf:"bytes,4,opt,name=password,proto3,oneof" json:"password,omitempty"` // If not provided, we only generate the key and add host to known_hosts
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SetupSftpRequest) Reset() {
|
||||
*x = SetupSftpRequest{}
|
||||
mi := &file_v1_service_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *SetupSftpRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SetupSftpRequest) ProtoMessage() {}
|
||||
|
||||
func (x *SetupSftpRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[1]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SetupSftpRequest.ProtoReflect.Descriptor instead.
|
||||
func (*SetupSftpRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *SetupSftpRequest) GetHost() string {
|
||||
if x != nil {
|
||||
return x.Host
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SetupSftpRequest) GetPort() string {
|
||||
if x != nil {
|
||||
return x.Port
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SetupSftpRequest) GetUsername() string {
|
||||
if x != nil {
|
||||
return x.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SetupSftpRequest) GetPassword() string {
|
||||
if x != nil && x.Password != nil {
|
||||
return *x.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type SetupSftpResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
PublicKey string `protobuf:"bytes,1,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
|
||||
KeyPath string `protobuf:"bytes,2,opt,name=key_path,json=keyPath,proto3" json:"key_path,omitempty"`
|
||||
KnownHostsPath string `protobuf:"bytes,3,opt,name=known_hosts_path,json=knownHostsPath,proto3" json:"known_hosts_path,omitempty"`
|
||||
Error string `protobuf:"bytes,4,opt,name=error,proto3" json:"error,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *SetupSftpResponse) Reset() {
|
||||
*x = SetupSftpResponse{}
|
||||
mi := &file_v1_service_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *SetupSftpResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SetupSftpResponse) ProtoMessage() {}
|
||||
|
||||
func (x *SetupSftpResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[2]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SetupSftpResponse.ProtoReflect.Descriptor instead.
|
||||
func (*SetupSftpResponse) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *SetupSftpResponse) GetPublicKey() string {
|
||||
if x != nil {
|
||||
return x.PublicKey
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SetupSftpResponse) GetKeyPath() string {
|
||||
if x != nil {
|
||||
return x.KeyPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SetupSftpResponse) GetKnownHostsPath() string {
|
||||
if x != nil {
|
||||
return x.KnownHostsPath
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *SetupSftpResponse) GetError() string {
|
||||
if x != nil {
|
||||
return x.Error
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type CheckRepoExistsRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Repo *Repo `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
|
||||
TrustSftpHostKey bool `protobuf:"varint,2,opt,name=trust_sftp_host_key,json=trustSftpHostKey,proto3" json:"trust_sftp_host_key,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *CheckRepoExistsRequest) Reset() {
|
||||
*x = CheckRepoExistsRequest{}
|
||||
mi := &file_v1_service_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CheckRepoExistsRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CheckRepoExistsRequest) ProtoMessage() {}
|
||||
|
||||
func (x *CheckRepoExistsRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[3]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CheckRepoExistsRequest.ProtoReflect.Descriptor instead.
|
||||
func (*CheckRepoExistsRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *CheckRepoExistsRequest) GetRepo() *Repo {
|
||||
if x != nil {
|
||||
return x.Repo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CheckRepoExistsRequest) GetTrustSftpHostKey() bool {
|
||||
if x != nil {
|
||||
return x.TrustSftpHostKey
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type CheckRepoExistsResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Exists bool `protobuf:"varint,1,opt,name=exists,proto3" json:"exists,omitempty"`
|
||||
Error string `protobuf:"bytes,2,opt,name=error,proto3" json:"error,omitempty"`
|
||||
HostKeyUntrusted bool `protobuf:"varint,5,opt,name=host_key_untrusted,json=hostKeyUntrusted,proto3" json:"host_key_untrusted,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *CheckRepoExistsResponse) Reset() {
|
||||
*x = CheckRepoExistsResponse{}
|
||||
mi := &file_v1_service_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CheckRepoExistsResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CheckRepoExistsResponse) ProtoMessage() {}
|
||||
|
||||
func (x *CheckRepoExistsResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[4]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CheckRepoExistsResponse.ProtoReflect.Descriptor instead.
|
||||
func (*CheckRepoExistsResponse) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *CheckRepoExistsResponse) GetExists() bool {
|
||||
if x != nil {
|
||||
return x.Exists
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *CheckRepoExistsResponse) GetError() string {
|
||||
if x != nil {
|
||||
return x.Error
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *CheckRepoExistsResponse) GetHostKeyUntrusted() bool {
|
||||
if x != nil {
|
||||
return x.HostKeyUntrusted
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type AddRepoRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Repo *Repo `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
|
||||
TrustSftpHostKey bool `protobuf:"varint,2,opt,name=trust_sftp_host_key,json=trustSftpHostKey,proto3" json:"trust_sftp_host_key,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AddRepoRequest) Reset() {
|
||||
*x = AddRepoRequest{}
|
||||
mi := &file_v1_service_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AddRepoRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AddRepoRequest) ProtoMessage() {}
|
||||
|
||||
func (x *AddRepoRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[5]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AddRepoRequest.ProtoReflect.Descriptor instead.
|
||||
func (*AddRepoRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *AddRepoRequest) GetRepo() *Repo {
|
||||
if x != nil {
|
||||
return x.Repo
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AddRepoRequest) GetTrustSftpHostKey() bool {
|
||||
if x != nil {
|
||||
return x.TrustSftpHostKey
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type DoRepoTaskRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
RepoId string `protobuf:"bytes,1,opt,name=repo_id,json=repoId,proto3" json:"repo_id,omitempty"`
|
||||
@@ -193,7 +493,7 @@ type DoRepoTaskRequest struct {
|
||||
|
||||
func (x *DoRepoTaskRequest) Reset() {
|
||||
*x = DoRepoTaskRequest{}
|
||||
mi := &file_v1_service_proto_msgTypes[1]
|
||||
mi := &file_v1_service_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -205,7 +505,7 @@ func (x *DoRepoTaskRequest) String() string {
|
||||
func (*DoRepoTaskRequest) ProtoMessage() {}
|
||||
|
||||
func (x *DoRepoTaskRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[1]
|
||||
mi := &file_v1_service_proto_msgTypes[6]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -218,7 +518,7 @@ func (x *DoRepoTaskRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use DoRepoTaskRequest.ProtoReflect.Descriptor instead.
|
||||
func (*DoRepoTaskRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{1}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *DoRepoTaskRequest) GetRepoId() string {
|
||||
@@ -245,7 +545,7 @@ type ClearHistoryRequest struct {
|
||||
|
||||
func (x *ClearHistoryRequest) Reset() {
|
||||
*x = ClearHistoryRequest{}
|
||||
mi := &file_v1_service_proto_msgTypes[2]
|
||||
mi := &file_v1_service_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -257,7 +557,7 @@ func (x *ClearHistoryRequest) String() string {
|
||||
func (*ClearHistoryRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ClearHistoryRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[2]
|
||||
mi := &file_v1_service_proto_msgTypes[7]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -270,7 +570,7 @@ func (x *ClearHistoryRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ClearHistoryRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ClearHistoryRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{2}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *ClearHistoryRequest) GetSelector() *OpSelector {
|
||||
@@ -298,7 +598,7 @@ type ForgetRequest struct {
|
||||
|
||||
func (x *ForgetRequest) Reset() {
|
||||
*x = ForgetRequest{}
|
||||
mi := &file_v1_service_proto_msgTypes[3]
|
||||
mi := &file_v1_service_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -310,7 +610,7 @@ func (x *ForgetRequest) String() string {
|
||||
func (*ForgetRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ForgetRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[3]
|
||||
mi := &file_v1_service_proto_msgTypes[8]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -323,7 +623,7 @@ func (x *ForgetRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ForgetRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ForgetRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{3}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *ForgetRequest) GetRepoId() string {
|
||||
@@ -357,7 +657,7 @@ type ListSnapshotsRequest struct {
|
||||
|
||||
func (x *ListSnapshotsRequest) Reset() {
|
||||
*x = ListSnapshotsRequest{}
|
||||
mi := &file_v1_service_proto_msgTypes[4]
|
||||
mi := &file_v1_service_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -369,7 +669,7 @@ func (x *ListSnapshotsRequest) String() string {
|
||||
func (*ListSnapshotsRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ListSnapshotsRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[4]
|
||||
mi := &file_v1_service_proto_msgTypes[9]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -382,7 +682,7 @@ func (x *ListSnapshotsRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ListSnapshotsRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ListSnapshotsRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{4}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *ListSnapshotsRequest) GetRepoId() string {
|
||||
@@ -409,7 +709,7 @@ type GetOperationsRequest struct {
|
||||
|
||||
func (x *GetOperationsRequest) Reset() {
|
||||
*x = GetOperationsRequest{}
|
||||
mi := &file_v1_service_proto_msgTypes[5]
|
||||
mi := &file_v1_service_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -421,7 +721,7 @@ func (x *GetOperationsRequest) String() string {
|
||||
func (*GetOperationsRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetOperationsRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[5]
|
||||
mi := &file_v1_service_proto_msgTypes[10]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -434,7 +734,7 @@ func (x *GetOperationsRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use GetOperationsRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetOperationsRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{5}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *GetOperationsRequest) GetSelector() *OpSelector {
|
||||
@@ -464,7 +764,7 @@ type RestoreSnapshotRequest struct {
|
||||
|
||||
func (x *RestoreSnapshotRequest) Reset() {
|
||||
*x = RestoreSnapshotRequest{}
|
||||
mi := &file_v1_service_proto_msgTypes[6]
|
||||
mi := &file_v1_service_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -476,7 +776,7 @@ func (x *RestoreSnapshotRequest) String() string {
|
||||
func (*RestoreSnapshotRequest) ProtoMessage() {}
|
||||
|
||||
func (x *RestoreSnapshotRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[6]
|
||||
mi := &file_v1_service_proto_msgTypes[11]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -489,7 +789,7 @@ func (x *RestoreSnapshotRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use RestoreSnapshotRequest.ProtoReflect.Descriptor instead.
|
||||
func (*RestoreSnapshotRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{6}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
func (x *RestoreSnapshotRequest) GetPlanId() string {
|
||||
@@ -538,7 +838,7 @@ type ListSnapshotFilesRequest struct {
|
||||
|
||||
func (x *ListSnapshotFilesRequest) Reset() {
|
||||
*x = ListSnapshotFilesRequest{}
|
||||
mi := &file_v1_service_proto_msgTypes[7]
|
||||
mi := &file_v1_service_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -550,7 +850,7 @@ func (x *ListSnapshotFilesRequest) String() string {
|
||||
func (*ListSnapshotFilesRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ListSnapshotFilesRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[7]
|
||||
mi := &file_v1_service_proto_msgTypes[12]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -563,7 +863,7 @@ func (x *ListSnapshotFilesRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ListSnapshotFilesRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ListSnapshotFilesRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{7}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{12}
|
||||
}
|
||||
|
||||
func (x *ListSnapshotFilesRequest) GetRepoGuid() string {
|
||||
@@ -597,7 +897,7 @@ type ListSnapshotFilesResponse struct {
|
||||
|
||||
func (x *ListSnapshotFilesResponse) Reset() {
|
||||
*x = ListSnapshotFilesResponse{}
|
||||
mi := &file_v1_service_proto_msgTypes[8]
|
||||
mi := &file_v1_service_proto_msgTypes[13]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -609,7 +909,7 @@ func (x *ListSnapshotFilesResponse) String() string {
|
||||
func (*ListSnapshotFilesResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ListSnapshotFilesResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[8]
|
||||
mi := &file_v1_service_proto_msgTypes[13]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -622,7 +922,7 @@ func (x *ListSnapshotFilesResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use ListSnapshotFilesResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ListSnapshotFilesResponse) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{8}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{13}
|
||||
}
|
||||
|
||||
func (x *ListSnapshotFilesResponse) GetPath() string {
|
||||
@@ -648,7 +948,7 @@ type LogDataRequest struct {
|
||||
|
||||
func (x *LogDataRequest) Reset() {
|
||||
*x = LogDataRequest{}
|
||||
mi := &file_v1_service_proto_msgTypes[9]
|
||||
mi := &file_v1_service_proto_msgTypes[14]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -660,7 +960,7 @@ func (x *LogDataRequest) String() string {
|
||||
func (*LogDataRequest) ProtoMessage() {}
|
||||
|
||||
func (x *LogDataRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[9]
|
||||
mi := &file_v1_service_proto_msgTypes[14]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -673,7 +973,7 @@ func (x *LogDataRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use LogDataRequest.ProtoReflect.Descriptor instead.
|
||||
func (*LogDataRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{9}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{14}
|
||||
}
|
||||
|
||||
func (x *LogDataRequest) GetRef() string {
|
||||
@@ -693,7 +993,7 @@ type GetDownloadURLRequest struct {
|
||||
|
||||
func (x *GetDownloadURLRequest) Reset() {
|
||||
*x = GetDownloadURLRequest{}
|
||||
mi := &file_v1_service_proto_msgTypes[10]
|
||||
mi := &file_v1_service_proto_msgTypes[15]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -705,7 +1005,7 @@ func (x *GetDownloadURLRequest) String() string {
|
||||
func (*GetDownloadURLRequest) ProtoMessage() {}
|
||||
|
||||
func (x *GetDownloadURLRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[10]
|
||||
mi := &file_v1_service_proto_msgTypes[15]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -718,7 +1018,7 @@ func (x *GetDownloadURLRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use GetDownloadURLRequest.ProtoReflect.Descriptor instead.
|
||||
func (*GetDownloadURLRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{10}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{15}
|
||||
}
|
||||
|
||||
func (x *GetDownloadURLRequest) GetOpId() int64 {
|
||||
@@ -753,7 +1053,7 @@ type LsEntry struct {
|
||||
|
||||
func (x *LsEntry) Reset() {
|
||||
*x = LsEntry{}
|
||||
mi := &file_v1_service_proto_msgTypes[11]
|
||||
mi := &file_v1_service_proto_msgTypes[16]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -765,7 +1065,7 @@ func (x *LsEntry) String() string {
|
||||
func (*LsEntry) ProtoMessage() {}
|
||||
|
||||
func (x *LsEntry) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[11]
|
||||
mi := &file_v1_service_proto_msgTypes[16]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -778,7 +1078,7 @@ func (x *LsEntry) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use LsEntry.ProtoReflect.Descriptor instead.
|
||||
func (*LsEntry) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{11}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{16}
|
||||
}
|
||||
|
||||
func (x *LsEntry) GetName() string {
|
||||
@@ -861,7 +1161,7 @@ type RunCommandRequest struct {
|
||||
|
||||
func (x *RunCommandRequest) Reset() {
|
||||
*x = RunCommandRequest{}
|
||||
mi := &file_v1_service_proto_msgTypes[12]
|
||||
mi := &file_v1_service_proto_msgTypes[17]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -873,7 +1173,7 @@ func (x *RunCommandRequest) String() string {
|
||||
func (*RunCommandRequest) ProtoMessage() {}
|
||||
|
||||
func (x *RunCommandRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[12]
|
||||
mi := &file_v1_service_proto_msgTypes[17]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -886,7 +1186,7 @@ func (x *RunCommandRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use RunCommandRequest.ProtoReflect.Descriptor instead.
|
||||
func (*RunCommandRequest) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{12}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{17}
|
||||
}
|
||||
|
||||
func (x *RunCommandRequest) GetRepoId() string {
|
||||
@@ -915,7 +1215,7 @@ type SummaryDashboardResponse struct {
|
||||
|
||||
func (x *SummaryDashboardResponse) Reset() {
|
||||
*x = SummaryDashboardResponse{}
|
||||
mi := &file_v1_service_proto_msgTypes[13]
|
||||
mi := &file_v1_service_proto_msgTypes[18]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -927,7 +1227,7 @@ func (x *SummaryDashboardResponse) String() string {
|
||||
func (*SummaryDashboardResponse) ProtoMessage() {}
|
||||
|
||||
func (x *SummaryDashboardResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[13]
|
||||
mi := &file_v1_service_proto_msgTypes[18]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -940,7 +1240,7 @@ func (x *SummaryDashboardResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SummaryDashboardResponse.ProtoReflect.Descriptor instead.
|
||||
func (*SummaryDashboardResponse) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{13}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{18}
|
||||
}
|
||||
|
||||
func (x *SummaryDashboardResponse) GetRepoSummaries() []*SummaryDashboardResponse_Summary {
|
||||
@@ -991,7 +1291,7 @@ type SummaryDashboardResponse_Summary struct {
|
||||
|
||||
func (x *SummaryDashboardResponse_Summary) Reset() {
|
||||
*x = SummaryDashboardResponse_Summary{}
|
||||
mi := &file_v1_service_proto_msgTypes[14]
|
||||
mi := &file_v1_service_proto_msgTypes[19]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1003,7 +1303,7 @@ func (x *SummaryDashboardResponse_Summary) String() string {
|
||||
func (*SummaryDashboardResponse_Summary) ProtoMessage() {}
|
||||
|
||||
func (x *SummaryDashboardResponse_Summary) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[14]
|
||||
mi := &file_v1_service_proto_msgTypes[19]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1016,7 +1316,7 @@ func (x *SummaryDashboardResponse_Summary) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use SummaryDashboardResponse_Summary.ProtoReflect.Descriptor instead.
|
||||
func (*SummaryDashboardResponse_Summary) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{13, 0}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{18, 0}
|
||||
}
|
||||
|
||||
func (x *SummaryDashboardResponse_Summary) GetId() string {
|
||||
@@ -1109,7 +1409,7 @@ type SummaryDashboardResponse_BackupChart struct {
|
||||
|
||||
func (x *SummaryDashboardResponse_BackupChart) Reset() {
|
||||
*x = SummaryDashboardResponse_BackupChart{}
|
||||
mi := &file_v1_service_proto_msgTypes[15]
|
||||
mi := &file_v1_service_proto_msgTypes[20]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1121,7 +1421,7 @@ func (x *SummaryDashboardResponse_BackupChart) String() string {
|
||||
func (*SummaryDashboardResponse_BackupChart) ProtoMessage() {}
|
||||
|
||||
func (x *SummaryDashboardResponse_BackupChart) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_v1_service_proto_msgTypes[15]
|
||||
mi := &file_v1_service_proto_msgTypes[20]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1134,7 +1434,7 @@ func (x *SummaryDashboardResponse_BackupChart) ProtoReflect() protoreflect.Messa
|
||||
|
||||
// Deprecated: Use SummaryDashboardResponse_BackupChart.ProtoReflect.Descriptor instead.
|
||||
func (*SummaryDashboardResponse_BackupChart) Descriptor() ([]byte, []int) {
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{13, 1}
|
||||
return file_v1_service_proto_rawDescGZIP(), []int{18, 1}
|
||||
}
|
||||
|
||||
func (x *SummaryDashboardResponse_BackupChart) GetFlowId() []int64 {
|
||||
@@ -1199,7 +1499,29 @@ const file_v1_service_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\b_flow_idB\f\n" +
|
||||
"\n" +
|
||||
"_modno_gte\"\xce\x01\n" +
|
||||
"_modno_gte\"\x84\x01\n" +
|
||||
"\x10SetupSftpRequest\x12\x12\n" +
|
||||
"\x04host\x18\x01 \x01(\tR\x04host\x12\x12\n" +
|
||||
"\x04port\x18\x02 \x01(\tR\x04port\x12\x1a\n" +
|
||||
"\busername\x18\x03 \x01(\tR\busername\x12\x1f\n" +
|
||||
"\bpassword\x18\x04 \x01(\tH\x00R\bpassword\x88\x01\x01B\v\n" +
|
||||
"\t_password\"\x8d\x01\n" +
|
||||
"\x11SetupSftpResponse\x12\x1d\n" +
|
||||
"\n" +
|
||||
"public_key\x18\x01 \x01(\tR\tpublicKey\x12\x19\n" +
|
||||
"\bkey_path\x18\x02 \x01(\tR\akeyPath\x12(\n" +
|
||||
"\x10known_hosts_path\x18\x03 \x01(\tR\x0eknownHostsPath\x12\x14\n" +
|
||||
"\x05error\x18\x04 \x01(\tR\x05error\"e\n" +
|
||||
"\x16CheckRepoExistsRequest\x12\x1c\n" +
|
||||
"\x04repo\x18\x01 \x01(\v2\b.v1.RepoR\x04repo\x12-\n" +
|
||||
"\x13trust_sftp_host_key\x18\x02 \x01(\bR\x10trustSftpHostKey\"u\n" +
|
||||
"\x17CheckRepoExistsResponse\x12\x16\n" +
|
||||
"\x06exists\x18\x01 \x01(\bR\x06exists\x12\x14\n" +
|
||||
"\x05error\x18\x02 \x01(\tR\x05error\x12,\n" +
|
||||
"\x12host_key_untrusted\x18\x05 \x01(\bR\x10hostKeyUntrusted\"]\n" +
|
||||
"\x0eAddRepoRequest\x12\x1c\n" +
|
||||
"\x04repo\x18\x01 \x01(\v2\b.v1.RepoR\x04repo\x12-\n" +
|
||||
"\x13trust_sftp_host_key\x18\x02 \x01(\bR\x10trustSftpHostKey\"\xce\x01\n" +
|
||||
"\x11DoRepoTaskRequest\x12\x17\n" +
|
||||
"\arepo_id\x18\x01 \x01(\tR\x06repoId\x12.\n" +
|
||||
"\x04task\x18\x02 \x01(\x0e2\x1a.v1.DoRepoTaskRequest.TaskR\x04task\"p\n" +
|
||||
@@ -1290,15 +1612,17 @@ const file_v1_service_proto_rawDesc = "" +
|
||||
"durationMs\x12+\n" +
|
||||
"\x06status\x18\x04 \x03(\x0e2\x13.v1.OperationStatusR\x06status\x12\x1f\n" +
|
||||
"\vbytes_added\x18\x05 \x03(\x03R\n" +
|
||||
"bytesAdded2\xaf\t\n" +
|
||||
"bytesAdded2\x92\n" +
|
||||
"\n" +
|
||||
"\bBackrest\x121\n" +
|
||||
"\tGetConfig\x12\x16.google.protobuf.Empty\x1a\n" +
|
||||
".v1.Config\"\x00\x12%\n" +
|
||||
"\tSetConfig\x12\n" +
|
||||
".v1.Config\x1a\n" +
|
||||
".v1.Config\"\x00\x12/\n" +
|
||||
"\x0fCheckRepoExists\x12\b.v1.Repo\x1a\x10.types.BoolValue\"\x00\x12!\n" +
|
||||
"\aAddRepo\x12\b.v1.Repo\x1a\n" +
|
||||
".v1.Config\"\x00\x12:\n" +
|
||||
"\tSetupSftp\x12\x14.v1.SetupSftpRequest\x1a\x15.v1.SetupSftpResponse\"\x00\x12L\n" +
|
||||
"\x0fCheckRepoExists\x12\x1a.v1.CheckRepoExistsRequest\x1a\x1b.v1.CheckRepoExistsResponse\"\x00\x12+\n" +
|
||||
"\aAddRepo\x12\x12.v1.AddRepoRequest\x1a\n" +
|
||||
".v1.Config\"\x00\x12.\n" +
|
||||
"\n" +
|
||||
"RemoveRepo\x12\x12.types.StringValue\x1a\n" +
|
||||
@@ -1334,92 +1658,100 @@ func file_v1_service_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_v1_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 16)
|
||||
var file_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 21)
|
||||
var file_v1_service_proto_goTypes = []any{
|
||||
(DoRepoTaskRequest_Task)(0), // 0: v1.DoRepoTaskRequest.Task
|
||||
(*OpSelector)(nil), // 1: v1.OpSelector
|
||||
(*DoRepoTaskRequest)(nil), // 2: v1.DoRepoTaskRequest
|
||||
(*ClearHistoryRequest)(nil), // 3: v1.ClearHistoryRequest
|
||||
(*ForgetRequest)(nil), // 4: v1.ForgetRequest
|
||||
(*ListSnapshotsRequest)(nil), // 5: v1.ListSnapshotsRequest
|
||||
(*GetOperationsRequest)(nil), // 6: v1.GetOperationsRequest
|
||||
(*RestoreSnapshotRequest)(nil), // 7: v1.RestoreSnapshotRequest
|
||||
(*ListSnapshotFilesRequest)(nil), // 8: v1.ListSnapshotFilesRequest
|
||||
(*ListSnapshotFilesResponse)(nil), // 9: v1.ListSnapshotFilesResponse
|
||||
(*LogDataRequest)(nil), // 10: v1.LogDataRequest
|
||||
(*GetDownloadURLRequest)(nil), // 11: v1.GetDownloadURLRequest
|
||||
(*LsEntry)(nil), // 12: v1.LsEntry
|
||||
(*RunCommandRequest)(nil), // 13: v1.RunCommandRequest
|
||||
(*SummaryDashboardResponse)(nil), // 14: v1.SummaryDashboardResponse
|
||||
(*SummaryDashboardResponse_Summary)(nil), // 15: v1.SummaryDashboardResponse.Summary
|
||||
(*SummaryDashboardResponse_BackupChart)(nil), // 16: v1.SummaryDashboardResponse.BackupChart
|
||||
(OperationStatus)(0), // 17: v1.OperationStatus
|
||||
(*emptypb.Empty)(nil), // 18: google.protobuf.Empty
|
||||
(*Config)(nil), // 19: v1.Config
|
||||
(*Repo)(nil), // 20: v1.Repo
|
||||
(*types.StringValue)(nil), // 21: types.StringValue
|
||||
(*types.Int64Value)(nil), // 22: types.Int64Value
|
||||
(*types.BoolValue)(nil), // 23: types.BoolValue
|
||||
(*OperationEvent)(nil), // 24: v1.OperationEvent
|
||||
(*OperationList)(nil), // 25: v1.OperationList
|
||||
(*ResticSnapshotList)(nil), // 26: v1.ResticSnapshotList
|
||||
(*types.BytesValue)(nil), // 27: types.BytesValue
|
||||
(*types.StringList)(nil), // 28: types.StringList
|
||||
(*SetupSftpRequest)(nil), // 2: v1.SetupSftpRequest
|
||||
(*SetupSftpResponse)(nil), // 3: v1.SetupSftpResponse
|
||||
(*CheckRepoExistsRequest)(nil), // 4: v1.CheckRepoExistsRequest
|
||||
(*CheckRepoExistsResponse)(nil), // 5: v1.CheckRepoExistsResponse
|
||||
(*AddRepoRequest)(nil), // 6: v1.AddRepoRequest
|
||||
(*DoRepoTaskRequest)(nil), // 7: v1.DoRepoTaskRequest
|
||||
(*ClearHistoryRequest)(nil), // 8: v1.ClearHistoryRequest
|
||||
(*ForgetRequest)(nil), // 9: v1.ForgetRequest
|
||||
(*ListSnapshotsRequest)(nil), // 10: v1.ListSnapshotsRequest
|
||||
(*GetOperationsRequest)(nil), // 11: v1.GetOperationsRequest
|
||||
(*RestoreSnapshotRequest)(nil), // 12: v1.RestoreSnapshotRequest
|
||||
(*ListSnapshotFilesRequest)(nil), // 13: v1.ListSnapshotFilesRequest
|
||||
(*ListSnapshotFilesResponse)(nil), // 14: v1.ListSnapshotFilesResponse
|
||||
(*LogDataRequest)(nil), // 15: v1.LogDataRequest
|
||||
(*GetDownloadURLRequest)(nil), // 16: v1.GetDownloadURLRequest
|
||||
(*LsEntry)(nil), // 17: v1.LsEntry
|
||||
(*RunCommandRequest)(nil), // 18: v1.RunCommandRequest
|
||||
(*SummaryDashboardResponse)(nil), // 19: v1.SummaryDashboardResponse
|
||||
(*SummaryDashboardResponse_Summary)(nil), // 20: v1.SummaryDashboardResponse.Summary
|
||||
(*SummaryDashboardResponse_BackupChart)(nil), // 21: v1.SummaryDashboardResponse.BackupChart
|
||||
(*Repo)(nil), // 22: v1.Repo
|
||||
(OperationStatus)(0), // 23: v1.OperationStatus
|
||||
(*emptypb.Empty)(nil), // 24: google.protobuf.Empty
|
||||
(*Config)(nil), // 25: v1.Config
|
||||
(*types.StringValue)(nil), // 26: types.StringValue
|
||||
(*types.Int64Value)(nil), // 27: types.Int64Value
|
||||
(*OperationEvent)(nil), // 28: v1.OperationEvent
|
||||
(*OperationList)(nil), // 29: v1.OperationList
|
||||
(*ResticSnapshotList)(nil), // 30: v1.ResticSnapshotList
|
||||
(*types.BytesValue)(nil), // 31: types.BytesValue
|
||||
(*types.StringList)(nil), // 32: types.StringList
|
||||
}
|
||||
var file_v1_service_proto_depIdxs = []int32{
|
||||
0, // 0: v1.DoRepoTaskRequest.task:type_name -> v1.DoRepoTaskRequest.Task
|
||||
1, // 1: v1.ClearHistoryRequest.selector:type_name -> v1.OpSelector
|
||||
1, // 2: v1.GetOperationsRequest.selector:type_name -> v1.OpSelector
|
||||
12, // 3: v1.ListSnapshotFilesResponse.entries:type_name -> v1.LsEntry
|
||||
15, // 4: v1.SummaryDashboardResponse.repo_summaries:type_name -> v1.SummaryDashboardResponse.Summary
|
||||
15, // 5: v1.SummaryDashboardResponse.plan_summaries:type_name -> v1.SummaryDashboardResponse.Summary
|
||||
16, // 6: v1.SummaryDashboardResponse.Summary.recent_backups:type_name -> v1.SummaryDashboardResponse.BackupChart
|
||||
17, // 7: v1.SummaryDashboardResponse.BackupChart.status:type_name -> v1.OperationStatus
|
||||
18, // 8: v1.Backrest.GetConfig:input_type -> google.protobuf.Empty
|
||||
19, // 9: v1.Backrest.SetConfig:input_type -> v1.Config
|
||||
20, // 10: v1.Backrest.CheckRepoExists:input_type -> v1.Repo
|
||||
20, // 11: v1.Backrest.AddRepo:input_type -> v1.Repo
|
||||
21, // 12: v1.Backrest.RemoveRepo:input_type -> types.StringValue
|
||||
18, // 13: v1.Backrest.GetOperationEvents:input_type -> google.protobuf.Empty
|
||||
6, // 14: v1.Backrest.GetOperations:input_type -> v1.GetOperationsRequest
|
||||
5, // 15: v1.Backrest.ListSnapshots:input_type -> v1.ListSnapshotsRequest
|
||||
8, // 16: v1.Backrest.ListSnapshotFiles:input_type -> v1.ListSnapshotFilesRequest
|
||||
21, // 17: v1.Backrest.Backup:input_type -> types.StringValue
|
||||
2, // 18: v1.Backrest.DoRepoTask:input_type -> v1.DoRepoTaskRequest
|
||||
4, // 19: v1.Backrest.Forget:input_type -> v1.ForgetRequest
|
||||
7, // 20: v1.Backrest.Restore:input_type -> v1.RestoreSnapshotRequest
|
||||
22, // 21: v1.Backrest.Cancel:input_type -> types.Int64Value
|
||||
10, // 22: v1.Backrest.GetLogs:input_type -> v1.LogDataRequest
|
||||
13, // 23: v1.Backrest.RunCommand:input_type -> v1.RunCommandRequest
|
||||
11, // 24: v1.Backrest.GetDownloadURL:input_type -> v1.GetDownloadURLRequest
|
||||
3, // 25: v1.Backrest.ClearHistory:input_type -> v1.ClearHistoryRequest
|
||||
21, // 26: v1.Backrest.PathAutocomplete:input_type -> types.StringValue
|
||||
18, // 27: v1.Backrest.GetSummaryDashboard:input_type -> google.protobuf.Empty
|
||||
19, // 28: v1.Backrest.GetConfig:output_type -> v1.Config
|
||||
19, // 29: v1.Backrest.SetConfig:output_type -> v1.Config
|
||||
23, // 30: v1.Backrest.CheckRepoExists:output_type -> types.BoolValue
|
||||
19, // 31: v1.Backrest.AddRepo:output_type -> v1.Config
|
||||
19, // 32: v1.Backrest.RemoveRepo:output_type -> v1.Config
|
||||
24, // 33: v1.Backrest.GetOperationEvents:output_type -> v1.OperationEvent
|
||||
25, // 34: v1.Backrest.GetOperations:output_type -> v1.OperationList
|
||||
26, // 35: v1.Backrest.ListSnapshots:output_type -> v1.ResticSnapshotList
|
||||
9, // 36: v1.Backrest.ListSnapshotFiles:output_type -> v1.ListSnapshotFilesResponse
|
||||
18, // 37: v1.Backrest.Backup:output_type -> google.protobuf.Empty
|
||||
18, // 38: v1.Backrest.DoRepoTask:output_type -> google.protobuf.Empty
|
||||
18, // 39: v1.Backrest.Forget:output_type -> google.protobuf.Empty
|
||||
18, // 40: v1.Backrest.Restore:output_type -> google.protobuf.Empty
|
||||
18, // 41: v1.Backrest.Cancel:output_type -> google.protobuf.Empty
|
||||
27, // 42: v1.Backrest.GetLogs:output_type -> types.BytesValue
|
||||
22, // 43: v1.Backrest.RunCommand:output_type -> types.Int64Value
|
||||
21, // 44: v1.Backrest.GetDownloadURL:output_type -> types.StringValue
|
||||
18, // 45: v1.Backrest.ClearHistory:output_type -> google.protobuf.Empty
|
||||
28, // 46: v1.Backrest.PathAutocomplete:output_type -> types.StringList
|
||||
14, // 47: v1.Backrest.GetSummaryDashboard:output_type -> v1.SummaryDashboardResponse
|
||||
28, // [28:48] is the sub-list for method output_type
|
||||
8, // [8:28] is the sub-list for method input_type
|
||||
8, // [8:8] is the sub-list for extension type_name
|
||||
8, // [8:8] is the sub-list for extension extendee
|
||||
0, // [0:8] is the sub-list for field type_name
|
||||
22, // 0: v1.CheckRepoExistsRequest.repo:type_name -> v1.Repo
|
||||
22, // 1: v1.AddRepoRequest.repo:type_name -> v1.Repo
|
||||
0, // 2: v1.DoRepoTaskRequest.task:type_name -> v1.DoRepoTaskRequest.Task
|
||||
1, // 3: v1.ClearHistoryRequest.selector:type_name -> v1.OpSelector
|
||||
1, // 4: v1.GetOperationsRequest.selector:type_name -> v1.OpSelector
|
||||
17, // 5: v1.ListSnapshotFilesResponse.entries:type_name -> v1.LsEntry
|
||||
20, // 6: v1.SummaryDashboardResponse.repo_summaries:type_name -> v1.SummaryDashboardResponse.Summary
|
||||
20, // 7: v1.SummaryDashboardResponse.plan_summaries:type_name -> v1.SummaryDashboardResponse.Summary
|
||||
21, // 8: v1.SummaryDashboardResponse.Summary.recent_backups:type_name -> v1.SummaryDashboardResponse.BackupChart
|
||||
23, // 9: v1.SummaryDashboardResponse.BackupChart.status:type_name -> v1.OperationStatus
|
||||
24, // 10: v1.Backrest.GetConfig:input_type -> google.protobuf.Empty
|
||||
25, // 11: v1.Backrest.SetConfig:input_type -> v1.Config
|
||||
2, // 12: v1.Backrest.SetupSftp:input_type -> v1.SetupSftpRequest
|
||||
4, // 13: v1.Backrest.CheckRepoExists:input_type -> v1.CheckRepoExistsRequest
|
||||
6, // 14: v1.Backrest.AddRepo:input_type -> v1.AddRepoRequest
|
||||
26, // 15: v1.Backrest.RemoveRepo:input_type -> types.StringValue
|
||||
24, // 16: v1.Backrest.GetOperationEvents:input_type -> google.protobuf.Empty
|
||||
11, // 17: v1.Backrest.GetOperations:input_type -> v1.GetOperationsRequest
|
||||
10, // 18: v1.Backrest.ListSnapshots:input_type -> v1.ListSnapshotsRequest
|
||||
13, // 19: v1.Backrest.ListSnapshotFiles:input_type -> v1.ListSnapshotFilesRequest
|
||||
26, // 20: v1.Backrest.Backup:input_type -> types.StringValue
|
||||
7, // 21: v1.Backrest.DoRepoTask:input_type -> v1.DoRepoTaskRequest
|
||||
9, // 22: v1.Backrest.Forget:input_type -> v1.ForgetRequest
|
||||
12, // 23: v1.Backrest.Restore:input_type -> v1.RestoreSnapshotRequest
|
||||
27, // 24: v1.Backrest.Cancel:input_type -> types.Int64Value
|
||||
15, // 25: v1.Backrest.GetLogs:input_type -> v1.LogDataRequest
|
||||
18, // 26: v1.Backrest.RunCommand:input_type -> v1.RunCommandRequest
|
||||
16, // 27: v1.Backrest.GetDownloadURL:input_type -> v1.GetDownloadURLRequest
|
||||
8, // 28: v1.Backrest.ClearHistory:input_type -> v1.ClearHistoryRequest
|
||||
26, // 29: v1.Backrest.PathAutocomplete:input_type -> types.StringValue
|
||||
24, // 30: v1.Backrest.GetSummaryDashboard:input_type -> google.protobuf.Empty
|
||||
25, // 31: v1.Backrest.GetConfig:output_type -> v1.Config
|
||||
25, // 32: v1.Backrest.SetConfig:output_type -> v1.Config
|
||||
3, // 33: v1.Backrest.SetupSftp:output_type -> v1.SetupSftpResponse
|
||||
5, // 34: v1.Backrest.CheckRepoExists:output_type -> v1.CheckRepoExistsResponse
|
||||
25, // 35: v1.Backrest.AddRepo:output_type -> v1.Config
|
||||
25, // 36: v1.Backrest.RemoveRepo:output_type -> v1.Config
|
||||
28, // 37: v1.Backrest.GetOperationEvents:output_type -> v1.OperationEvent
|
||||
29, // 38: v1.Backrest.GetOperations:output_type -> v1.OperationList
|
||||
30, // 39: v1.Backrest.ListSnapshots:output_type -> v1.ResticSnapshotList
|
||||
14, // 40: v1.Backrest.ListSnapshotFiles:output_type -> v1.ListSnapshotFilesResponse
|
||||
24, // 41: v1.Backrest.Backup:output_type -> google.protobuf.Empty
|
||||
24, // 42: v1.Backrest.DoRepoTask:output_type -> google.protobuf.Empty
|
||||
24, // 43: v1.Backrest.Forget:output_type -> google.protobuf.Empty
|
||||
24, // 44: v1.Backrest.Restore:output_type -> google.protobuf.Empty
|
||||
24, // 45: v1.Backrest.Cancel:output_type -> google.protobuf.Empty
|
||||
31, // 46: v1.Backrest.GetLogs:output_type -> types.BytesValue
|
||||
27, // 47: v1.Backrest.RunCommand:output_type -> types.Int64Value
|
||||
26, // 48: v1.Backrest.GetDownloadURL:output_type -> types.StringValue
|
||||
24, // 49: v1.Backrest.ClearHistory:output_type -> google.protobuf.Empty
|
||||
32, // 50: v1.Backrest.PathAutocomplete:output_type -> types.StringList
|
||||
19, // 51: v1.Backrest.GetSummaryDashboard:output_type -> v1.SummaryDashboardResponse
|
||||
31, // [31:52] is the sub-list for method output_type
|
||||
10, // [10:31] is the sub-list for method input_type
|
||||
10, // [10:10] is the sub-list for extension type_name
|
||||
10, // [10:10] is the sub-list for extension extendee
|
||||
0, // [0:10] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_v1_service_proto_init() }
|
||||
@@ -1431,13 +1763,14 @@ func file_v1_service_proto_init() {
|
||||
file_v1_restic_proto_init()
|
||||
file_v1_operations_proto_init()
|
||||
file_v1_service_proto_msgTypes[0].OneofWrappers = []any{}
|
||||
file_v1_service_proto_msgTypes[1].OneofWrappers = []any{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_v1_service_proto_rawDesc), len(file_v1_service_proto_rawDesc)),
|
||||
NumEnums: 1,
|
||||
NumMessages: 16,
|
||||
NumMessages: 21,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@@ -23,6 +23,7 @@ const _ = grpc.SupportPackageIsVersion9
|
||||
const (
|
||||
Backrest_GetConfig_FullMethodName = "/v1.Backrest/GetConfig"
|
||||
Backrest_SetConfig_FullMethodName = "/v1.Backrest/SetConfig"
|
||||
Backrest_SetupSftp_FullMethodName = "/v1.Backrest/SetupSftp"
|
||||
Backrest_CheckRepoExists_FullMethodName = "/v1.Backrest/CheckRepoExists"
|
||||
Backrest_AddRepo_FullMethodName = "/v1.Backrest/AddRepo"
|
||||
Backrest_RemoveRepo_FullMethodName = "/v1.Backrest/RemoveRepo"
|
||||
@@ -49,8 +50,9 @@ const (
|
||||
type BackrestClient interface {
|
||||
GetConfig(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*Config, error)
|
||||
SetConfig(ctx context.Context, in *Config, opts ...grpc.CallOption) (*Config, error)
|
||||
CheckRepoExists(ctx context.Context, in *Repo, opts ...grpc.CallOption) (*types.BoolValue, error)
|
||||
AddRepo(ctx context.Context, in *Repo, opts ...grpc.CallOption) (*Config, error)
|
||||
SetupSftp(ctx context.Context, in *SetupSftpRequest, opts ...grpc.CallOption) (*SetupSftpResponse, error)
|
||||
CheckRepoExists(ctx context.Context, in *CheckRepoExistsRequest, opts ...grpc.CallOption) (*CheckRepoExistsResponse, error)
|
||||
AddRepo(ctx context.Context, in *AddRepoRequest, opts ...grpc.CallOption) (*Config, error)
|
||||
RemoveRepo(ctx context.Context, in *types.StringValue, opts ...grpc.CallOption) (*Config, error)
|
||||
GetOperationEvents(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (grpc.ServerStreamingClient[OperationEvent], error)
|
||||
GetOperations(ctx context.Context, in *GetOperationsRequest, opts ...grpc.CallOption) (*OperationList, error)
|
||||
@@ -108,9 +110,19 @@ func (c *backrestClient) SetConfig(ctx context.Context, in *Config, opts ...grpc
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *backrestClient) CheckRepoExists(ctx context.Context, in *Repo, opts ...grpc.CallOption) (*types.BoolValue, error) {
|
||||
func (c *backrestClient) SetupSftp(ctx context.Context, in *SetupSftpRequest, opts ...grpc.CallOption) (*SetupSftpResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(types.BoolValue)
|
||||
out := new(SetupSftpResponse)
|
||||
err := c.cc.Invoke(ctx, Backrest_SetupSftp_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *backrestClient) CheckRepoExists(ctx context.Context, in *CheckRepoExistsRequest, opts ...grpc.CallOption) (*CheckRepoExistsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CheckRepoExistsResponse)
|
||||
err := c.cc.Invoke(ctx, Backrest_CheckRepoExists_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -118,7 +130,7 @@ func (c *backrestClient) CheckRepoExists(ctx context.Context, in *Repo, opts ...
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *backrestClient) AddRepo(ctx context.Context, in *Repo, opts ...grpc.CallOption) (*Config, error) {
|
||||
func (c *backrestClient) AddRepo(ctx context.Context, in *AddRepoRequest, opts ...grpc.CallOption) (*Config, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(Config)
|
||||
err := c.cc.Invoke(ctx, Backrest_AddRepo_FullMethodName, in, out, cOpts...)
|
||||
@@ -312,8 +324,9 @@ func (c *backrestClient) GetSummaryDashboard(ctx context.Context, in *emptypb.Em
|
||||
type BackrestServer interface {
|
||||
GetConfig(context.Context, *emptypb.Empty) (*Config, error)
|
||||
SetConfig(context.Context, *Config) (*Config, error)
|
||||
CheckRepoExists(context.Context, *Repo) (*types.BoolValue, error)
|
||||
AddRepo(context.Context, *Repo) (*Config, error)
|
||||
SetupSftp(context.Context, *SetupSftpRequest) (*SetupSftpResponse, error)
|
||||
CheckRepoExists(context.Context, *CheckRepoExistsRequest) (*CheckRepoExistsResponse, error)
|
||||
AddRepo(context.Context, *AddRepoRequest) (*Config, error)
|
||||
RemoveRepo(context.Context, *types.StringValue) (*Config, error)
|
||||
GetOperationEvents(*emptypb.Empty, grpc.ServerStreamingServer[OperationEvent]) error
|
||||
GetOperations(context.Context, *GetOperationsRequest) (*OperationList, error)
|
||||
@@ -357,10 +370,13 @@ func (UnimplementedBackrestServer) GetConfig(context.Context, *emptypb.Empty) (*
|
||||
func (UnimplementedBackrestServer) SetConfig(context.Context, *Config) (*Config, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SetConfig not implemented")
|
||||
}
|
||||
func (UnimplementedBackrestServer) CheckRepoExists(context.Context, *Repo) (*types.BoolValue, error) {
|
||||
func (UnimplementedBackrestServer) SetupSftp(context.Context, *SetupSftpRequest) (*SetupSftpResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SetupSftp not implemented")
|
||||
}
|
||||
func (UnimplementedBackrestServer) CheckRepoExists(context.Context, *CheckRepoExistsRequest) (*CheckRepoExistsResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CheckRepoExists not implemented")
|
||||
}
|
||||
func (UnimplementedBackrestServer) AddRepo(context.Context, *Repo) (*Config, error) {
|
||||
func (UnimplementedBackrestServer) AddRepo(context.Context, *AddRepoRequest) (*Config, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method AddRepo not implemented")
|
||||
}
|
||||
func (UnimplementedBackrestServer) RemoveRepo(context.Context, *types.StringValue) (*Config, error) {
|
||||
@@ -468,8 +484,26 @@ func _Backrest_SetConfig_Handler(srv interface{}, ctx context.Context, dec func(
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Backrest_SetupSftp_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SetupSftpRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(BackrestServer).SetupSftp(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Backrest_SetupSftp_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BackrestServer).SetupSftp(ctx, req.(*SetupSftpRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Backrest_CheckRepoExists_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Repo)
|
||||
in := new(CheckRepoExistsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -481,13 +515,13 @@ func _Backrest_CheckRepoExists_Handler(srv interface{}, ctx context.Context, dec
|
||||
FullMethod: Backrest_CheckRepoExists_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BackrestServer).CheckRepoExists(ctx, req.(*Repo))
|
||||
return srv.(BackrestServer).CheckRepoExists(ctx, req.(*CheckRepoExistsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Backrest_AddRepo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Repo)
|
||||
in := new(AddRepoRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -499,7 +533,7 @@ func _Backrest_AddRepo_Handler(srv interface{}, ctx context.Context, dec func(in
|
||||
FullMethod: Backrest_AddRepo_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(BackrestServer).AddRepo(ctx, req.(*Repo))
|
||||
return srv.(BackrestServer).AddRepo(ctx, req.(*AddRepoRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
@@ -793,6 +827,10 @@ var Backrest_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "SetConfig",
|
||||
Handler: _Backrest_SetConfig_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SetupSftp",
|
||||
Handler: _Backrest_SetupSftp_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CheckRepoExists",
|
||||
Handler: _Backrest_CheckRepoExists_Handler,
|
||||
|
||||
@@ -39,6 +39,8 @@ const (
|
||||
BackrestGetConfigProcedure = "/v1.Backrest/GetConfig"
|
||||
// BackrestSetConfigProcedure is the fully-qualified name of the Backrest's SetConfig RPC.
|
||||
BackrestSetConfigProcedure = "/v1.Backrest/SetConfig"
|
||||
// BackrestSetupSftpProcedure is the fully-qualified name of the Backrest's SetupSftp RPC.
|
||||
BackrestSetupSftpProcedure = "/v1.Backrest/SetupSftp"
|
||||
// BackrestCheckRepoExistsProcedure is the fully-qualified name of the Backrest's CheckRepoExists
|
||||
// RPC.
|
||||
BackrestCheckRepoExistsProcedure = "/v1.Backrest/CheckRepoExists"
|
||||
@@ -86,8 +88,9 @@ const (
|
||||
type BackrestClient interface {
|
||||
GetConfig(context.Context, *connect.Request[emptypb.Empty]) (*connect.Response[v1.Config], error)
|
||||
SetConfig(context.Context, *connect.Request[v1.Config]) (*connect.Response[v1.Config], error)
|
||||
CheckRepoExists(context.Context, *connect.Request[v1.Repo]) (*connect.Response[types.BoolValue], error)
|
||||
AddRepo(context.Context, *connect.Request[v1.Repo]) (*connect.Response[v1.Config], error)
|
||||
SetupSftp(context.Context, *connect.Request[v1.SetupSftpRequest]) (*connect.Response[v1.SetupSftpResponse], error)
|
||||
CheckRepoExists(context.Context, *connect.Request[v1.CheckRepoExistsRequest]) (*connect.Response[v1.CheckRepoExistsResponse], error)
|
||||
AddRepo(context.Context, *connect.Request[v1.AddRepoRequest]) (*connect.Response[v1.Config], error)
|
||||
RemoveRepo(context.Context, *connect.Request[types.StringValue]) (*connect.Response[v1.Config], error)
|
||||
GetOperationEvents(context.Context, *connect.Request[emptypb.Empty]) (*connect.ServerStreamForClient[v1.OperationEvent], error)
|
||||
GetOperations(context.Context, *connect.Request[v1.GetOperationsRequest]) (*connect.Response[v1.OperationList], error)
|
||||
@@ -140,13 +143,19 @@ func NewBackrestClient(httpClient connect.HTTPClient, baseURL string, opts ...co
|
||||
connect.WithSchema(backrestMethods.ByName("SetConfig")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
checkRepoExists: connect.NewClient[v1.Repo, types.BoolValue](
|
||||
setupSftp: connect.NewClient[v1.SetupSftpRequest, v1.SetupSftpResponse](
|
||||
httpClient,
|
||||
baseURL+BackrestSetupSftpProcedure,
|
||||
connect.WithSchema(backrestMethods.ByName("SetupSftp")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
checkRepoExists: connect.NewClient[v1.CheckRepoExistsRequest, v1.CheckRepoExistsResponse](
|
||||
httpClient,
|
||||
baseURL+BackrestCheckRepoExistsProcedure,
|
||||
connect.WithSchema(backrestMethods.ByName("CheckRepoExists")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
addRepo: connect.NewClient[v1.Repo, v1.Config](
|
||||
addRepo: connect.NewClient[v1.AddRepoRequest, v1.Config](
|
||||
httpClient,
|
||||
baseURL+BackrestAddRepoProcedure,
|
||||
connect.WithSchema(backrestMethods.ByName("AddRepo")),
|
||||
@@ -255,8 +264,9 @@ func NewBackrestClient(httpClient connect.HTTPClient, baseURL string, opts ...co
|
||||
type backrestClient struct {
|
||||
getConfig *connect.Client[emptypb.Empty, v1.Config]
|
||||
setConfig *connect.Client[v1.Config, v1.Config]
|
||||
checkRepoExists *connect.Client[v1.Repo, types.BoolValue]
|
||||
addRepo *connect.Client[v1.Repo, v1.Config]
|
||||
setupSftp *connect.Client[v1.SetupSftpRequest, v1.SetupSftpResponse]
|
||||
checkRepoExists *connect.Client[v1.CheckRepoExistsRequest, v1.CheckRepoExistsResponse]
|
||||
addRepo *connect.Client[v1.AddRepoRequest, v1.Config]
|
||||
removeRepo *connect.Client[types.StringValue, v1.Config]
|
||||
getOperationEvents *connect.Client[emptypb.Empty, v1.OperationEvent]
|
||||
getOperations *connect.Client[v1.GetOperationsRequest, v1.OperationList]
|
||||
@@ -285,13 +295,18 @@ func (c *backrestClient) SetConfig(ctx context.Context, req *connect.Request[v1.
|
||||
return c.setConfig.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// SetupSftp calls v1.Backrest.SetupSftp.
|
||||
func (c *backrestClient) SetupSftp(ctx context.Context, req *connect.Request[v1.SetupSftpRequest]) (*connect.Response[v1.SetupSftpResponse], error) {
|
||||
return c.setupSftp.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// CheckRepoExists calls v1.Backrest.CheckRepoExists.
|
||||
func (c *backrestClient) CheckRepoExists(ctx context.Context, req *connect.Request[v1.Repo]) (*connect.Response[types.BoolValue], error) {
|
||||
func (c *backrestClient) CheckRepoExists(ctx context.Context, req *connect.Request[v1.CheckRepoExistsRequest]) (*connect.Response[v1.CheckRepoExistsResponse], error) {
|
||||
return c.checkRepoExists.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// AddRepo calls v1.Backrest.AddRepo.
|
||||
func (c *backrestClient) AddRepo(ctx context.Context, req *connect.Request[v1.Repo]) (*connect.Response[v1.Config], error) {
|
||||
func (c *backrestClient) AddRepo(ctx context.Context, req *connect.Request[v1.AddRepoRequest]) (*connect.Response[v1.Config], error) {
|
||||
return c.addRepo.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
@@ -379,8 +394,9 @@ func (c *backrestClient) GetSummaryDashboard(ctx context.Context, req *connect.R
|
||||
type BackrestHandler interface {
|
||||
GetConfig(context.Context, *connect.Request[emptypb.Empty]) (*connect.Response[v1.Config], error)
|
||||
SetConfig(context.Context, *connect.Request[v1.Config]) (*connect.Response[v1.Config], error)
|
||||
CheckRepoExists(context.Context, *connect.Request[v1.Repo]) (*connect.Response[types.BoolValue], error)
|
||||
AddRepo(context.Context, *connect.Request[v1.Repo]) (*connect.Response[v1.Config], error)
|
||||
SetupSftp(context.Context, *connect.Request[v1.SetupSftpRequest]) (*connect.Response[v1.SetupSftpResponse], error)
|
||||
CheckRepoExists(context.Context, *connect.Request[v1.CheckRepoExistsRequest]) (*connect.Response[v1.CheckRepoExistsResponse], error)
|
||||
AddRepo(context.Context, *connect.Request[v1.AddRepoRequest]) (*connect.Response[v1.Config], error)
|
||||
RemoveRepo(context.Context, *connect.Request[types.StringValue]) (*connect.Response[v1.Config], error)
|
||||
GetOperationEvents(context.Context, *connect.Request[emptypb.Empty], *connect.ServerStream[v1.OperationEvent]) error
|
||||
GetOperations(context.Context, *connect.Request[v1.GetOperationsRequest]) (*connect.Response[v1.OperationList], error)
|
||||
@@ -429,6 +445,12 @@ func NewBackrestHandler(svc BackrestHandler, opts ...connect.HandlerOption) (str
|
||||
connect.WithSchema(backrestMethods.ByName("SetConfig")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
backrestSetupSftpHandler := connect.NewUnaryHandler(
|
||||
BackrestSetupSftpProcedure,
|
||||
svc.SetupSftp,
|
||||
connect.WithSchema(backrestMethods.ByName("SetupSftp")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
backrestCheckRepoExistsHandler := connect.NewUnaryHandler(
|
||||
BackrestCheckRepoExistsProcedure,
|
||||
svc.CheckRepoExists,
|
||||
@@ -543,6 +565,8 @@ func NewBackrestHandler(svc BackrestHandler, opts ...connect.HandlerOption) (str
|
||||
backrestGetConfigHandler.ServeHTTP(w, r)
|
||||
case BackrestSetConfigProcedure:
|
||||
backrestSetConfigHandler.ServeHTTP(w, r)
|
||||
case BackrestSetupSftpProcedure:
|
||||
backrestSetupSftpHandler.ServeHTTP(w, r)
|
||||
case BackrestCheckRepoExistsProcedure:
|
||||
backrestCheckRepoExistsHandler.ServeHTTP(w, r)
|
||||
case BackrestAddRepoProcedure:
|
||||
@@ -596,11 +620,15 @@ func (UnimplementedBackrestHandler) SetConfig(context.Context, *connect.Request[
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1.Backrest.SetConfig is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedBackrestHandler) CheckRepoExists(context.Context, *connect.Request[v1.Repo]) (*connect.Response[types.BoolValue], error) {
|
||||
func (UnimplementedBackrestHandler) SetupSftp(context.Context, *connect.Request[v1.SetupSftpRequest]) (*connect.Response[v1.SetupSftpResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1.Backrest.SetupSftp is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedBackrestHandler) CheckRepoExists(context.Context, *connect.Request[v1.CheckRepoExistsRequest]) (*connect.Response[v1.CheckRepoExistsResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1.Backrest.CheckRepoExists is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedBackrestHandler) AddRepo(context.Context, *connect.Request[v1.Repo]) (*connect.Response[v1.Config], error) {
|
||||
func (UnimplementedBackrestHandler) AddRepo(context.Context, *connect.Request[v1.AddRepoRequest]) (*connect.Response[v1.Config], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("v1.Backrest.AddRepo is not implemented"))
|
||||
}
|
||||
|
||||
|
||||
@@ -54,10 +54,12 @@ require (
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/josephspurrier/goversioninfo v1.5.0 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/ncruces/julianday v1.0.0 // indirect
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect
|
||||
github.com/pkg/sftp v1.13.10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.2 // indirect
|
||||
|
||||
@@ -90,6 +90,8 @@ github.com/josephspurrier/goversioninfo v1.5.0 h1:9TJtORoyf4YMoWSOo/cXFN9A/lB3Pn
|
||||
github.com/josephspurrier/goversioninfo v1.5.0/go.mod h1:6MoTvFZ6GKJkzcdLnU5T/RGYUbHQbKpYeNP0AgQLd2o=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
@@ -124,6 +126,8 @@ github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzL
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
|
||||
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU=
|
||||
github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1HbeGA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
|
||||
+373
-13
@@ -3,11 +3,19 @@ package api
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -17,6 +25,7 @@ import (
|
||||
"github.com/garethgeorge/backrest/gen/go/types"
|
||||
v1 "github.com/garethgeorge/backrest/gen/go/v1"
|
||||
"github.com/garethgeorge/backrest/gen/go/v1/v1connect"
|
||||
"github.com/garethgeorge/backrest/internal/api/sftputil"
|
||||
syncapi "github.com/garethgeorge/backrest/internal/api/syncapi"
|
||||
"github.com/garethgeorge/backrest/internal/config"
|
||||
"github.com/garethgeorge/backrest/internal/cryptoutil"
|
||||
@@ -29,7 +38,9 @@ import (
|
||||
"github.com/garethgeorge/backrest/internal/protoutil"
|
||||
"github.com/garethgeorge/backrest/internal/resticinstaller"
|
||||
"github.com/garethgeorge/backrest/pkg/restic"
|
||||
"github.com/pkg/sftp"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
@@ -98,21 +109,33 @@ func (s *BackrestHandler) SetConfig(ctx context.Context, req *connect.Request[v1
|
||||
return connect.NewResponse(newConfig), nil
|
||||
}
|
||||
|
||||
func (s *BackrestHandler) CheckRepoExists(ctx context.Context, req *connect.Request[v1.Repo]) (*connect.Response[types.BoolValue], error) {
|
||||
func (s *BackrestHandler) CheckRepoExists(ctx context.Context, req *connect.Request[v1.CheckRepoExistsRequest]) (*connect.Response[v1.CheckRepoExistsResponse], error) {
|
||||
c, err := s.config.Get()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get config: %w", err)
|
||||
}
|
||||
|
||||
sanitizeRepoFlags(req.Msg.Repo)
|
||||
|
||||
c = proto.Clone(c).(*v1.Config)
|
||||
if idx := slices.IndexFunc(c.Repos, func(r *v1.Repo) bool { return r.Id == req.Msg.Id }); idx != -1 {
|
||||
c.Repos[idx] = req.Msg
|
||||
if idx := slices.IndexFunc(c.Repos, func(r *v1.Repo) bool { return r.Id == req.Msg.Repo.Id }); idx != -1 {
|
||||
c.Repos[idx] = req.Msg.Repo
|
||||
} else {
|
||||
c.Repos = append(c.Repos, req.Msg)
|
||||
c.Repos = append(c.Repos, req.Msg.Repo)
|
||||
}
|
||||
|
||||
if req.Msg.Guid == "" {
|
||||
req.Msg.Guid = cryptoutil.MustRandomID(cryptoutil.DefaultIDBits)
|
||||
if req.Msg.Repo.Guid == "" {
|
||||
req.Msg.Repo.Guid = cryptoutil.MustRandomID(cryptoutil.DefaultIDBits)
|
||||
}
|
||||
|
||||
if err := s.addSftpHostKey(req.Msg.Repo, req.Msg.GetTrustSftpHostKey()); err != nil {
|
||||
if strings.Contains(err.Error(), "host key verification failed") {
|
||||
return connect.NewResponse(&v1.CheckRepoExistsResponse{
|
||||
HostKeyUntrusted: true,
|
||||
Error: err.Error(),
|
||||
}), nil
|
||||
}
|
||||
return nil, fmt.Errorf("failed to add sftp host key: %w", err)
|
||||
}
|
||||
|
||||
if err := config.ValidateConfig(c); err != nil {
|
||||
@@ -124,7 +147,7 @@ func (s *BackrestHandler) CheckRepoExists(ctx context.Context, req *connect.Requ
|
||||
return nil, fmt.Errorf("failed to find or install restic binary: %w", err)
|
||||
}
|
||||
|
||||
r, err := repo.NewRepoOrchestrator(c, req.Msg, bin)
|
||||
r, err := repo.NewRepoOrchestrator(c, req.Msg.Repo, bin)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to configure repo: %w", err)
|
||||
}
|
||||
@@ -133,24 +156,25 @@ func (s *BackrestHandler) CheckRepoExists(ctx context.Context, req *connect.Requ
|
||||
defer cancel()
|
||||
|
||||
if err := r.Exists(ctx); err != nil {
|
||||
zap.S().Debugf("repo %q exists or not: %v", req.Msg.Id, err)
|
||||
zap.S().Debugf("repo %q exists or not: %v", req.Msg.Repo.Id, err)
|
||||
if errors.Is(err, restic.ErrRepoNotFound) {
|
||||
zap.S().Debugf("repo %q does not exist", req.Msg.Id)
|
||||
return connect.NewResponse(&types.BoolValue{Value: false}), nil
|
||||
zap.S().Debugf("repo %q does not exist", req.Msg.Repo.Id)
|
||||
return connect.NewResponse(&v1.CheckRepoExistsResponse{Exists: false}), nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return connect.NewResponse(&types.BoolValue{Value: true}), nil
|
||||
return connect.NewResponse(&v1.CheckRepoExistsResponse{Exists: true}), nil
|
||||
}
|
||||
|
||||
// AddRepo implements POST /v1/config/repo, it includes validation that the repo can be initialized.
|
||||
func (s *BackrestHandler) AddRepo(ctx context.Context, req *connect.Request[v1.Repo]) (*connect.Response[v1.Config], error) {
|
||||
func (s *BackrestHandler) AddRepo(ctx context.Context, req *connect.Request[v1.AddRepoRequest]) (*connect.Response[v1.Config], error) {
|
||||
c, err := s.config.Get()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get config: %w", err)
|
||||
}
|
||||
|
||||
newRepo := req.Msg
|
||||
newRepo := req.Msg.Repo
|
||||
sanitizeRepoFlags(newRepo)
|
||||
|
||||
// Deep copy the configuration
|
||||
c = proto.Clone(c).(*v1.Config)
|
||||
@@ -189,6 +213,10 @@ func (s *BackrestHandler) AddRepo(ctx context.Context, req *connect.Request[v1.R
|
||||
|
||||
newRepo.Guid = guid
|
||||
|
||||
if err := s.addSftpHostKey(newRepo, req.Msg.GetTrustSftpHostKey()); err != nil {
|
||||
return nil, fmt.Errorf("failed to add sftp host key: %w", err)
|
||||
}
|
||||
|
||||
if err := config.ValidateConfig(c); err != nil {
|
||||
return nil, fmt.Errorf("validation error: %w", err)
|
||||
}
|
||||
@@ -267,6 +295,158 @@ func (s *BackrestHandler) RemoveRepo(ctx context.Context, req *connect.Request[t
|
||||
return connect.NewResponse(cfg), nil
|
||||
}
|
||||
|
||||
// SetupSftp implements SetupSftp RPC
|
||||
func (s *BackrestHandler) SetupSftp(ctx context.Context, req *connect.Request[v1.SetupSftpRequest]) (*connect.Response[v1.SetupSftpResponse], error) {
|
||||
host := req.Msg.Host
|
||||
port := req.Msg.Port
|
||||
if port == "" {
|
||||
port = "22"
|
||||
}
|
||||
user := req.Msg.Username
|
||||
password := req.Msg.Password // Optional
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("automated SFTP setup is not supported on Windows"))
|
||||
}
|
||||
|
||||
// 1. Host Key Verification/Addition
|
||||
if err := sftputil.AddHostKey(host, port, env.SSHDir()); err != nil {
|
||||
return connect.NewResponse(&v1.SetupSftpResponse{
|
||||
Error: fmt.Sprintf("Failed to add host key: %v", err),
|
||||
}), nil
|
||||
}
|
||||
|
||||
// 2. Generate Key
|
||||
_, pubBytes, keyPath, err := sftputil.GenerateKey(host, env.SSHDir())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate key: %w", err)
|
||||
}
|
||||
|
||||
pubKeyStr := string(pubBytes)
|
||||
|
||||
// 3. Install if password provided
|
||||
if password != nil {
|
||||
if err := sftputil.InstallKey(host, port, user, *password, pubBytes); err != nil {
|
||||
return connect.NewResponse(&v1.SetupSftpResponse{
|
||||
Error: fmt.Sprintf("Failed to install key: %v", err),
|
||||
}), nil
|
||||
}
|
||||
|
||||
// Verify
|
||||
privPEM, err := os.ReadFile(keyPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read generated private key for verification: %w", err)
|
||||
}
|
||||
|
||||
if err := sftputil.VerifyConnection(host, port, user, privPEM); err != nil {
|
||||
return connect.NewResponse(&v1.SetupSftpResponse{
|
||||
Error: fmt.Sprintf("Key installed but verification failed: %v", err),
|
||||
}), nil
|
||||
}
|
||||
}
|
||||
|
||||
return connect.NewResponse(&v1.SetupSftpResponse{
|
||||
PublicKey: pubKeyStr,
|
||||
KeyPath: keyPath,
|
||||
KnownHostsPath: filepath.Join(env.SSHDir(), "known_hosts"),
|
||||
}), nil
|
||||
}
|
||||
|
||||
// This is equivalent to what `ssh-keyscan` does.
|
||||
func (s *BackrestHandler) addSftpHostKey(repo *v1.Repo, trust bool) error {
|
||||
uri := repo.GetUri()
|
||||
if !strings.HasPrefix(uri, "sftp:") {
|
||||
return nil
|
||||
}
|
||||
uri = strings.TrimPrefix(uri, "sftp:")
|
||||
|
||||
slashIdx := strings.Index(uri, "/")
|
||||
if slashIdx == -1 {
|
||||
slashIdx = len(uri)
|
||||
}
|
||||
|
||||
authority := uri[:slashIdx]
|
||||
hostPart := authority
|
||||
if atIdx := strings.LastIndex(authority, "@"); atIdx != -1 {
|
||||
hostPart = authority[atIdx+1:]
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(hostPart)
|
||||
if err != nil {
|
||||
host = hostPart
|
||||
}
|
||||
|
||||
if host == "" {
|
||||
return errors.New("could not parse host from sftp uri")
|
||||
}
|
||||
|
||||
// Extract port from flags if possible
|
||||
// Check for port in sftp.args
|
||||
// e.g. --option=sftp.args='-oBatchMode=yes -p 23'
|
||||
re := regexp.MustCompile(`(?:^|\s)-[pP]\s*(\d+)`)
|
||||
for _, flag := range repo.Flags {
|
||||
if strings.HasPrefix(flag, "--option=sftp.args=") {
|
||||
matches := re.FindStringSubmatch(flag)
|
||||
if len(matches) > 1 {
|
||||
port = matches[1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get home directory: %w", err)
|
||||
}
|
||||
knownHostsPath := path.Join(home, ".ssh", "known_hosts")
|
||||
|
||||
if err := os.MkdirAll(path.Dir(knownHostsPath), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Construct host spec for ssh-keygen and ssh-keyscan
|
||||
// If port is non-standard, host spec is usually [host]:port
|
||||
hostSpec := host
|
||||
if port != "" && port != "22" {
|
||||
hostSpec = fmt.Sprintf("[%s]:%s", host, port)
|
||||
}
|
||||
|
||||
checkCmd := exec.Command("ssh-keygen", "-F", hostSpec)
|
||||
if err := checkCmd.Run(); err == nil {
|
||||
zap.S().Debugf("SFTP host %s already in known_hosts", hostSpec)
|
||||
return nil
|
||||
}
|
||||
|
||||
if !trust {
|
||||
return fmt.Errorf("SFTP host key verification failed: key for host %s is unknown", hostSpec)
|
||||
}
|
||||
|
||||
keyscanArgs := []string{"-H"}
|
||||
if port != "" {
|
||||
keyscanArgs = append(keyscanArgs, "-p", port)
|
||||
}
|
||||
keyscanArgs = append(keyscanArgs, host)
|
||||
|
||||
keyscanCmd := exec.Command("ssh-keyscan", keyscanArgs...)
|
||||
keyOutput, err := keyscanCmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ssh-keyscan for host %s failed: %w", host, err)
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(knownHostsPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open known_hosts file: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := f.Write(keyOutput); err != nil {
|
||||
return fmt.Errorf("failed to write to known_hosts file: %w", err)
|
||||
}
|
||||
|
||||
zap.S().Infof("Added SFTP host %s to known_hosts file at %s", hostSpec, knownHostsPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListSnapshots implements POST /v1/snapshots
|
||||
func (s *BackrestHandler) ListSnapshots(ctx context.Context, req *connect.Request[v1.ListSnapshotsRequest]) (*connect.Response[v1.ResticSnapshotList], error) {
|
||||
query := req.Msg
|
||||
@@ -904,3 +1084,183 @@ func (s *BackrestHandler) GetSummaryDashboard(ctx context.Context, req *connect.
|
||||
|
||||
return connect.NewResponse(response), nil
|
||||
}
|
||||
|
||||
func parseSftpConfig(repo *v1.Repo) (user, host, port string, err error) {
|
||||
uri := repo.GetUri()
|
||||
if !strings.HasPrefix(uri, "sftp:") {
|
||||
return "", "", "", errors.New("not an sftp repo")
|
||||
}
|
||||
uri = strings.TrimPrefix(uri, "sftp:")
|
||||
|
||||
slashIdx := strings.Index(uri, "/")
|
||||
if slashIdx == -1 {
|
||||
slashIdx = len(uri)
|
||||
}
|
||||
|
||||
authority := uri[:slashIdx]
|
||||
hostPart := authority
|
||||
if atIdx := strings.LastIndex(authority, "@"); atIdx != -1 {
|
||||
user = authority[:atIdx]
|
||||
hostPart = authority[atIdx+1:]
|
||||
}
|
||||
|
||||
host, port, err = net.SplitHostPort(hostPart)
|
||||
if err != nil {
|
||||
host = hostPart
|
||||
}
|
||||
|
||||
// Parse port from flags
|
||||
re := regexp.MustCompile(`(?:^|\s)-[pP]\s*(\d+)`)
|
||||
for _, flag := range repo.Flags {
|
||||
if strings.HasPrefix(flag, "--option=sftp.args=") {
|
||||
matches := re.FindStringSubmatch(flag)
|
||||
if len(matches) > 1 {
|
||||
port = matches[1]
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return user, host, port, nil
|
||||
}
|
||||
|
||||
func sanitizeRepoFlags(repo *v1.Repo) {
|
||||
for i, flag := range repo.Flags {
|
||||
if strings.HasPrefix(flag, "--option=sftp.args=") {
|
||||
repo.Flags[i] = strings.ReplaceAll(flag, "-i @", "-i ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *BackrestHandler) generateAndInstallSSHKey(host, port, user, password string) (string, error) {
|
||||
zap.S().Debugf("Generating ED25519 key for %s@%s:%s", user, host, port)
|
||||
// Generate Key
|
||||
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
privBlock, err := ssh.MarshalPrivateKey(priv, "")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal private key: %w", err)
|
||||
}
|
||||
privPEM := pem.EncodeToMemory(privBlock)
|
||||
|
||||
sshPub, err := ssh.NewPublicKey(pub)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
pubBytes := ssh.MarshalAuthorizedKey(sshPub)
|
||||
|
||||
// Save to file
|
||||
keyDir := env.SSHDir()
|
||||
if err := os.MkdirAll(keyDir, 0700); err != nil {
|
||||
return "", err
|
||||
}
|
||||
keyPath := path.Join(keyDir, fmt.Sprintf("id_ed25519_%s_%d", host, time.Now().Unix()))
|
||||
if err := os.WriteFile(keyPath, privPEM, 0600); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := os.WriteFile(keyPath+".pub", pubBytes, 0644); err != nil {
|
||||
zap.S().Warnf("failed to write public key: %v", err)
|
||||
}
|
||||
zap.S().Debugf("Saved private key to %s", keyPath)
|
||||
|
||||
// Verify key file is readable and valid
|
||||
if diskBytes, err := os.ReadFile(keyPath); err != nil {
|
||||
return "", fmt.Errorf("failed to read back key file: %w", err)
|
||||
} else if _, err := ssh.ParsePrivateKey(diskBytes); err != nil {
|
||||
return "", fmt.Errorf("generated key file is invalid or unparseable: %w", err)
|
||||
}
|
||||
|
||||
// Install on server
|
||||
sshConfig := &ssh.ClientConfig{
|
||||
User: user,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.Password(password),
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
zap.S().Debugf("Dialing SSH to %s:%s to install key", host, port)
|
||||
conn, err := ssh.Dial("tcp", net.JoinHostPort(host, port), sshConfig)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to dial ssh: %w", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
// Use SFTP to install key (handles restricted shells better)
|
||||
sftpClient, err := sftp.NewClient(conn)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create sftp client: %w", err)
|
||||
}
|
||||
defer sftpClient.Close()
|
||||
|
||||
// Ensure .ssh directory exists
|
||||
if _, err := sftpClient.Stat(".ssh"); errors.Is(err, os.ErrNotExist) {
|
||||
if err := sftpClient.Mkdir(".ssh"); err != nil {
|
||||
return "", fmt.Errorf("failed to create .ssh directory: %w", err)
|
||||
}
|
||||
if err := sftpClient.Chmod(".ssh", 0700); err != nil {
|
||||
zap.S().Warnf("failed to chmod .ssh: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Open authorized_keys for appending
|
||||
f, err := sftpClient.OpenFile(".ssh/authorized_keys", os.O_APPEND|os.O_CREATE|os.O_WRONLY)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to open .ssh/authorized_keys: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := f.Chmod(0600); err != nil {
|
||||
zap.S().Warnf("failed to chmod authorized_keys: %v", err)
|
||||
}
|
||||
|
||||
// Append key with leading newline to be safe
|
||||
if _, err := f.Write([]byte("\n")); err != nil {
|
||||
return "", fmt.Errorf("failed to write newline to authorized_keys: %w", err)
|
||||
}
|
||||
if _, err := f.Write(pubBytes); err != nil {
|
||||
return "", fmt.Errorf("failed to write key to authorized_keys: %w", err)
|
||||
}
|
||||
if _, err := f.Write([]byte("\n")); err != nil {
|
||||
return "", fmt.Errorf("failed to write newline to authorized_keys: %w", err)
|
||||
}
|
||||
|
||||
zap.S().Debug("Key installed successfully via SFTP")
|
||||
|
||||
// Verify key works
|
||||
keySigner, err := ssh.NewSignerFromKey(priv)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create signer: %w", err)
|
||||
}
|
||||
|
||||
testConfig := &ssh.ClientConfig{
|
||||
User: user,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.PublicKeys(keySigner),
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
zap.S().Debugf("Verifying connection with new key...")
|
||||
testConn, err := ssh.Dial("tcp", net.JoinHostPort(host, port), testConfig)
|
||||
if err != nil {
|
||||
zap.S().Warnf("Verification failed: %v", err)
|
||||
return "", fmt.Errorf("generated key was installed but failed to authenticate: %w", err)
|
||||
}
|
||||
defer testConn.Close()
|
||||
|
||||
// Verify SFTP subsystem
|
||||
verifySftpClient, err := sftp.NewClient(testConn)
|
||||
if err != nil {
|
||||
zap.S().Warnf("SFTP verification failed: %v", err)
|
||||
return "", fmt.Errorf("generated key authenticated but SFTP subsystem failed: %w", err)
|
||||
}
|
||||
verifySftpClient.Close()
|
||||
|
||||
zap.S().Debug("Verification successful")
|
||||
|
||||
return keyPath, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
package sftputil
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/sftp"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// AddHostKey adds the SFTP host key to the known_hosts file.
|
||||
// It uses ssh-keyscan to fetch the key.
|
||||
func AddHostKey(host, port string, sshDir string) error {
|
||||
hostSpec := host
|
||||
if port != "" && port != "22" {
|
||||
hostSpec = fmt.Sprintf("[%s]:%s", host, port)
|
||||
}
|
||||
|
||||
knownHostsPath := filepath.Join(sshDir, "known_hosts")
|
||||
if err := os.MkdirAll(path.Dir(knownHostsPath), 0700); err != nil {
|
||||
return fmt.Errorf("failed to create ssh dir: %w", err)
|
||||
}
|
||||
|
||||
// Check if already known
|
||||
checkCmd := exec.Command("ssh-keygen", "-F", hostSpec, "-f", knownHostsPath)
|
||||
if checkCmd.Run() == nil {
|
||||
zap.S().Debugf("SFTP host %s already in known_hosts", hostSpec)
|
||||
return nil
|
||||
}
|
||||
if err := os.MkdirAll(path.Dir(knownHostsPath), 0700); err != nil {
|
||||
return fmt.Errorf("failed to create ssh dir: %w", err)
|
||||
}
|
||||
|
||||
keyscanArgs := []string{"-H"}
|
||||
if port != "" {
|
||||
keyscanArgs = append(keyscanArgs, "-p", port)
|
||||
}
|
||||
keyscanArgs = append(keyscanArgs, host)
|
||||
|
||||
keyscanCmd := exec.Command("ssh-keyscan", keyscanArgs...)
|
||||
keyOutput, err := keyscanCmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ssh-keyscan for host %s failed: %w", host, err)
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(knownHostsPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open known_hosts file: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if _, err := f.Write(keyOutput); err != nil {
|
||||
return fmt.Errorf("failed to write to known_hosts file: %w", err)
|
||||
}
|
||||
|
||||
zap.S().Infof("Added SFTP host %s to known_hosts file at %s", hostSpec, knownHostsPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenerateKey generates an Ed25519 key pair and saves it to the specified directory.
|
||||
// Returns the private key in OpenSSH PEM format, public key in SSH format, and the full path to the private key file.
|
||||
func GenerateKey(host string, sshDir string) ([]byte, []byte, string, error) {
|
||||
zap.S().Debugf("Generating ED25519 key for host %s", host)
|
||||
pub, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, nil, "", fmt.Errorf("failed to generate key: %w", err)
|
||||
}
|
||||
|
||||
// Marshal private key to OpenSSH PEM format (requires "golang.org/x/crypto/ssh")
|
||||
// Note: ssh.MarshalPrivateKey returns a PEM block since Go 1.16+ for Ed25519?
|
||||
// Actually ssh.MarshalPrivateKey returns an *pem.Block.
|
||||
privBlock, err := ssh.MarshalPrivateKey(priv, "")
|
||||
if err != nil {
|
||||
return nil, nil, "", fmt.Errorf("failed to marshal private key: %w", err)
|
||||
}
|
||||
privPEM := pem.EncodeToMemory(privBlock)
|
||||
|
||||
sshPub, err := ssh.NewPublicKey(pub)
|
||||
if err != nil {
|
||||
return nil, nil, "", fmt.Errorf("failed to create public key: %w", err)
|
||||
}
|
||||
pubBytes := ssh.MarshalAuthorizedKey(sshPub)
|
||||
|
||||
// Save to file
|
||||
if err := os.MkdirAll(sshDir, 0700); err != nil {
|
||||
return nil, nil, "", fmt.Errorf("failed to create ssh dir: %w", err)
|
||||
}
|
||||
sanitizedHost := strings.Map(func(r rune) rune {
|
||||
if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '.' || r == '-' {
|
||||
return r
|
||||
}
|
||||
return '_'
|
||||
}, host)
|
||||
|
||||
keyPath := filepath.Join(sshDir, "id_ed25519_"+string(sanitizedHost))
|
||||
// check if file exists
|
||||
if _, err := os.Stat(keyPath); err == nil {
|
||||
// read the key from disk instead
|
||||
privPEM, err = os.ReadFile(keyPath)
|
||||
if err != nil {
|
||||
return nil, nil, "", fmt.Errorf("failed to read private key: %w", err)
|
||||
}
|
||||
pubBytes, err = os.ReadFile(keyPath + ".pub")
|
||||
if err != nil {
|
||||
return nil, nil, "", fmt.Errorf("failed to read public key: %w", err)
|
||||
}
|
||||
return privPEM, pubBytes, keyPath, nil
|
||||
}
|
||||
|
||||
if err := os.WriteFile(keyPath, privPEM, 0600); err != nil {
|
||||
return nil, nil, "", fmt.Errorf("failed to write private key: %w", err)
|
||||
}
|
||||
if err := os.WriteFile(keyPath+".pub", pubBytes, 0644); err != nil {
|
||||
zap.S().Warnf("failed to write public key: %v", err)
|
||||
}
|
||||
|
||||
return privPEM, pubBytes, keyPath, nil
|
||||
}
|
||||
|
||||
// InstallKey connects to the SFTP server using a password and appends the public key to authorized_keys.
|
||||
func InstallKey(host, port, user, password string, pubBytes []byte) error {
|
||||
sshConfig := &ssh.ClientConfig{
|
||||
User: user,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.Password(password),
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // Verification assumed done via AddHostKey
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
conn, err := ssh.Dial("tcp", net.JoinHostPort(host, port), sshConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect with password: %w", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
sftpClient, err := sftp.NewClient(conn)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create sftp client: %w", err)
|
||||
}
|
||||
defer sftpClient.Close()
|
||||
|
||||
// Ensure .ssh directory exists
|
||||
if _, err := sftpClient.Stat(".ssh"); errors.Is(err, os.ErrNotExist) {
|
||||
if err := sftpClient.Mkdir(".ssh"); err != nil {
|
||||
return fmt.Errorf("failed to create .ssh directory: %w", err)
|
||||
}
|
||||
if err := sftpClient.Chmod(".ssh", 0700); err != nil {
|
||||
zap.S().Warnf("failed to chmod .ssh: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
f, err := sftpClient.OpenFile(".ssh/authorized_keys", os.O_APPEND|os.O_CREATE|os.O_WRONLY)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open authorized_keys: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := f.Chmod(0600); err != nil {
|
||||
zap.S().Warnf("failed to chmod authorized_keys: %v", err)
|
||||
}
|
||||
|
||||
if _, err := f.Write([]byte("\n")); err != nil {
|
||||
return fmt.Errorf("write error: %w", err)
|
||||
}
|
||||
if _, err := f.Write(pubBytes); err != nil {
|
||||
return fmt.Errorf("write error: %w", err)
|
||||
}
|
||||
if _, err := f.Write([]byte("\n")); err != nil {
|
||||
return fmt.Errorf("write error: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifyConnection attempts to connect using the provided private key.
|
||||
func VerifyConnection(host, port, user string, privPEM []byte) error {
|
||||
signer, err := ssh.ParsePrivateKey(privPEM)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse private key: %w", err)
|
||||
}
|
||||
|
||||
clientConfig := &ssh.ClientConfig{
|
||||
User: user,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.PublicKeys(signer),
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
Timeout: 10 * time.Second,
|
||||
}
|
||||
|
||||
conn, err := ssh.Dial("tcp", net.JoinHostPort(host, port), clientConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("verification connection failed: %w", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package sftputil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGenerateKey_ReuseAndSanitization(t *testing.T) {
|
||||
// Create a temporary directory for SSH keys
|
||||
tempDir, err := os.MkdirTemp("", "sftp_test")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
host := "example.com"
|
||||
|
||||
// First call: should generate new keys
|
||||
priv1, pub1, path1, err := GenerateKey(host, tempDir)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKey failed: %v", err)
|
||||
}
|
||||
if priv1 == nil || pub1 == nil {
|
||||
t.Fatal("GenerateKey returned nil keys")
|
||||
}
|
||||
|
||||
// Verify file existence
|
||||
expectedFilename := "id_ed25519_example.com"
|
||||
if filepath.Base(path1) != expectedFilename {
|
||||
t.Errorf("expected filename %s, got %s", expectedFilename, filepath.Base(path1))
|
||||
}
|
||||
if _, err := os.Stat(path1); os.IsNotExist(err) {
|
||||
t.Errorf("private key file does not exist at %s", path1)
|
||||
}
|
||||
if _, err := os.Stat(path1 + ".pub"); os.IsNotExist(err) {
|
||||
t.Errorf("public key file does not exist at %s.pub", path1)
|
||||
}
|
||||
|
||||
// Second call: should reuse existing keys
|
||||
priv2, pub2, path2, err := GenerateKey(host, tempDir)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKey (2nd call) failed: %v", err)
|
||||
}
|
||||
|
||||
// Verify keys are identical
|
||||
if !bytes.Equal(priv1, priv2) {
|
||||
t.Error("GenerateKey did not reuse the private key")
|
||||
}
|
||||
if !bytes.Equal(pub1, pub2) {
|
||||
t.Error("GenerateKey did not reuse the public key")
|
||||
}
|
||||
if path1 != path2 {
|
||||
t.Errorf("GenerateKey returned different paths: %s vs %s", path1, path2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerateKey_Sanitization(t *testing.T) {
|
||||
tempDir, err := os.MkdirTemp("", "sftp_retry_test")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
// Host with special characters
|
||||
unsafeHost := "bad/host:name!@#"
|
||||
_, _, keyPath, err := GenerateKey(unsafeHost, tempDir)
|
||||
if err != nil {
|
||||
t.Fatalf("GenerateKey failed for unsafe host: %v", err)
|
||||
}
|
||||
|
||||
// Expected sanitization: bad_host_name___
|
||||
// characters allowed: a-z, A-Z, 0-9, ., -
|
||||
// '/' -> '_'
|
||||
// ':' -> '_'
|
||||
// '!' -> '_'
|
||||
// '@' -> '_'
|
||||
// '#' -> '_'
|
||||
expectedFilename := "id_ed25519_bad_host_name___"
|
||||
if filepath.Base(keyPath) != expectedFilename {
|
||||
t.Errorf("Sanitization failed. Expected %s, got %s", expectedFilename, filepath.Base(keyPath))
|
||||
}
|
||||
}
|
||||
Vendored
+5
@@ -99,6 +99,11 @@ func LogsPath() string {
|
||||
return filepath.Join(dataDir, "processlogs")
|
||||
}
|
||||
|
||||
func SSHDir() string {
|
||||
// This is awkward, we don't have a flag that specifies a "config" directory persay, so we default to the directory containing the config file for our SSH keys/known_hosts file.
|
||||
return filepath.Join(filepath.Dir(ConfigFilePath()), ".backrest-ssh")
|
||||
}
|
||||
|
||||
func getHomeDir() string {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
|
||||
+34
-2
@@ -16,9 +16,11 @@ service Backrest {
|
||||
|
||||
rpc SetConfig (Config) returns (Config) {}
|
||||
|
||||
rpc CheckRepoExists (Repo) returns (types.BoolValue) {} // returns an error if the repo does not exist
|
||||
rpc SetupSftp (SetupSftpRequest) returns (SetupSftpResponse) {}
|
||||
|
||||
rpc AddRepo (Repo) returns (Config) {}
|
||||
rpc CheckRepoExists (CheckRepoExistsRequest) returns (CheckRepoExistsResponse) {} // returns an error if the repo does not exist
|
||||
|
||||
rpc AddRepo (AddRepoRequest) returns (Config) {}
|
||||
|
||||
rpc RemoveRepo (types.StringValue) returns (Config) {} // remvoes a repo from the config and deletes its history, does not delete the repo on disk
|
||||
|
||||
@@ -76,6 +78,36 @@ message OpSelector {
|
||||
optional int64 modno_gte = 9;
|
||||
}
|
||||
|
||||
message SetupSftpRequest {
|
||||
string host = 1;
|
||||
string port = 2;
|
||||
string username = 3;
|
||||
optional string password = 4; // If not provided, we only generate the key and add host to known_hosts
|
||||
}
|
||||
|
||||
message SetupSftpResponse {
|
||||
string public_key = 1;
|
||||
string key_path = 2;
|
||||
string known_hosts_path = 3;
|
||||
string error = 4;
|
||||
}
|
||||
|
||||
message CheckRepoExistsRequest {
|
||||
Repo repo = 1;
|
||||
bool trust_sftp_host_key = 2;
|
||||
}
|
||||
|
||||
message CheckRepoExistsResponse {
|
||||
bool exists = 1;
|
||||
string error = 2;
|
||||
bool host_key_untrusted = 5;
|
||||
}
|
||||
|
||||
message AddRepoRequest {
|
||||
Repo repo = 1;
|
||||
bool trust_sftp_host_key = 2;
|
||||
}
|
||||
|
||||
message DoRepoTaskRequest {
|
||||
string repo_id = 1;
|
||||
enum Task {
|
||||
|
||||
@@ -115,10 +115,12 @@ func TestFirstRun(t *testing.T) {
|
||||
fmt.Sprintf("http://%s", addr),
|
||||
)
|
||||
|
||||
req := connect.NewRequest(&v1.Repo{
|
||||
Id: "test-repo",
|
||||
Uri: filepath.Join(tmpDir, "test-repo"),
|
||||
Password: "1234",
|
||||
req := connect.NewRequest(&v1.AddRepoRequest{
|
||||
Repo: &v1.Repo{
|
||||
Id: "test-repo",
|
||||
Uri: filepath.Join(tmpDir, "test-repo"),
|
||||
Password: "1234",
|
||||
},
|
||||
})
|
||||
_, err := client.AddRepo(context.Background(), req)
|
||||
if err != nil {
|
||||
|
||||
+167
-22
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user