From 3510a2d65292d9727d797e8ed36dbb24f009f833 Mon Sep 17 00:00:00 2001 From: Nathan ter Bogt Date: Thu, 28 Aug 2025 18:53:49 +1200 Subject: [PATCH] Improve backup and restore functionality for failed and restoring backups across environments --- internal/model/backup.go | 6 ++- internal/model/model.go | 11 ++++ internal/model/restore.go | 6 ++- internal/server/mock/backup/server.go | 47 +++++++++-------- internal/server/mock/project/server.go | 6 +-- internal/server/mock/restore/server.go | 71 ++++++++++++++------------ 6 files changed, 84 insertions(+), 63 deletions(-) diff --git a/internal/model/backup.go b/internal/model/backup.go index b958913..77c381b 100644 --- a/internal/model/backup.go +++ b/internal/model/backup.go @@ -12,6 +12,7 @@ type Backup struct { Id string StartTime time.Time Duration time.Duration + Failed bool } func NewBackup(environment string) *Backup { @@ -19,12 +20,15 @@ func NewBackup(environment string) *Backup { Id: environment + "-" + gofakeit.UUID(), StartTime: time.Now().Round(time.Second), Duration: 120 * time.Second, + Failed: false, } } func (b *Backup) Status() pb.BackupStatus_Phase { status := pb.BackupStatus_Completed - if b.StartTime.Add(b.Duration).After(time.Now()) { + if b.Failed { + status = pb.BackupStatus_Failed + } else if b.StartTime.Add(b.Duration).After(time.Now()) { status = pb.BackupStatus_InProgress } return status diff --git a/internal/model/model.go b/internal/model/model.go index 20b3e8a..6ed24ed 100644 --- a/internal/model/model.go +++ b/internal/model/model.go @@ -127,16 +127,19 @@ func (s *Model) CreateEnvironment(name string, size int) { Id: name + "-b84dd996-8113-4cd3-8dfe-018c990f5f1a", StartTime: time.Now().Add(-12 * time.Hour).Round(time.Second), Duration: 115 * time.Second, + Failed: false, }, name + "-aabb7bee-ab04-4ae1-ba3f-1142aae1353f": { Id: name + "-aabb7bee-ab04-4ae1-ba3f-1142aae1353f", StartTime: time.Now().Add(-24 * time.Hour).Round(time.Second), Duration: 134 * time.Second, + Failed: true, }, name + "-7d419959-a3b7-483b-8fa7-769a0977e46b": { Id: name + "-7d419959-a3b7-483b-8fa7-769a0977e46b", StartTime: time.Now().Add(-36 * time.Hour).Round(time.Second), Duration: 123 * time.Second, + Failed: false, }, } @@ -146,6 +149,14 @@ func (s *Model) CreateEnvironment(name string, size int) { BackupId: name + "-7d419959-a3b7-483b-8fa7-769a0977e46b", StartTime: time.Now().Add(-12 * time.Hour).Round(time.Second), Duration: 145 * time.Second, + Failed: false, + }, + name + "-39b11746-eca6-40ac-9aed-094d38c1e444": { + Id: name + "-39b11746-eca6-40ac-9aed-094d38c1e444", + BackupId: name + "-7d419959-a3b7-483b-8fa7-769a0977e46b", + StartTime: time.Now().Add(-12 * time.Hour).Round(time.Second), + Duration: 145 * time.Second, + Failed: true, }, } diff --git a/internal/model/restore.go b/internal/model/restore.go index dd008b9..709945d 100644 --- a/internal/model/restore.go +++ b/internal/model/restore.go @@ -13,6 +13,7 @@ type Restore struct { BackupId string StartTime time.Time Duration time.Duration + Failed bool } func NewRestore(environment string, backupId string) *Restore { @@ -21,12 +22,15 @@ func NewRestore(environment string, backupId string) *Restore { BackupId: backupId, StartTime: time.Now().Round(time.Second), Duration: 150 * time.Second, + Failed: false, } } func (b *Restore) Status() pb.RestoreStatus_Phase { status := pb.RestoreStatus_Completed - if b.StartTime.Add(b.Duration).After(time.Now()) { + if b.Failed { + status = pb.RestoreStatus_Failed + } else if b.StartTime.Add(b.Duration).After(time.Now()) { status = pb.RestoreStatus_InProgress } return status diff --git a/internal/server/mock/backup/server.go b/internal/server/mock/backup/server.go index 1cad988..62c68ac 100644 --- a/internal/server/mock/backup/server.go +++ b/internal/server/mock/backup/server.go @@ -35,23 +35,11 @@ func (s *Server) Get(ctx context.Context, req *pb.BackupGetRequest) (*pb.BackupG return nil, fmt.Errorf("id not provided") } - backup, err := s.Model.GetBackup(req.ID) + backupResponse, err := buildBackup(s.Model, req.ID) if err != nil { return nil, err } - backupResponse := &pb.BackupStatus{ - Name: backup.Id, - Phase: backup.Status(), - StartTime: backup.StartTime.Format(time.RFC3339), - Duration: backup.Duration.String(), - Databases: []string{"default"}, - Volumes: []string{"public", "private"}, - } - if backup.Status() == pb.BackupStatus_Completed { - backupResponse.CompletionTime = backup.StartTime.Add(backup.Duration).Format(time.RFC3339) - } - resp := &pb.BackupGetResponse{ Backup: backupResponse, } @@ -67,17 +55,7 @@ func (s *Server) List(ctx context.Context, req *pb.BackupListRequest) (*pb.Backu resp := &pb.BackupListResponse{} for _, value := range environment.Backup { - summary := &pb.BackupStatus{ - Name: value.Id, - Phase: value.Status(), - StartTime: value.StartTime.Format(time.RFC3339), - Duration: value.Duration.String(), - Databases: []string{"default"}, - Volumes: []string{"public", "private"}, - } - if value.Status() == pb.BackupStatus_Completed { - summary.CompletionTime = value.StartTime.Add(value.Duration).Format(time.RFC3339) - } + summary, _ := buildBackup(s.Model, value.Id) resp.List = append(resp.List, summary) } @@ -87,3 +65,24 @@ func (s *Server) List(ctx context.Context, req *pb.BackupListRequest) (*pb.Backu return resp, nil } + +func buildBackup(model *model.Model, id string) (*pb.BackupStatus, error) { + backup, err := model.GetBackup(id) + if err != nil { + return nil, err + } + + backupResponse := &pb.BackupStatus{ + Name: backup.Id, + Phase: backup.Status(), + StartTime: backup.StartTime.Format(time.RFC3339), + Duration: backup.Duration.String(), + Databases: []string{"default"}, + Volumes: []string{"public", "private"}, + } + if backup.Status() != pb.BackupStatus_InProgress { + backupResponse.CompletionTime = backup.StartTime.Add(backup.Duration).Format(time.RFC3339) + } + + return backupResponse, nil +} diff --git a/internal/server/mock/project/server.go b/internal/server/mock/project/server.go index 57536d9..9ec93b8 100644 --- a/internal/server/mock/project/server.go +++ b/internal/server/mock/project/server.go @@ -20,10 +20,8 @@ func (s *Server) List(ctx context.Context, req *pb.ProjectListRequest) (*pb.Proj resp := &pb.ProjectListResponse{} for _, project := range s.Model.GetProjects() { - respProject, err := buildProject(s.Model, project.Id) - if err == nil { - resp.Projects = append(resp.Projects, respProject) - } + respProject, _ := buildProject(s.Model, project.Id) + resp.Projects = append(resp.Projects, respProject) } return resp, nil diff --git a/internal/server/mock/restore/server.go b/internal/server/mock/restore/server.go index 9087e44..ed18292 100644 --- a/internal/server/mock/restore/server.go +++ b/internal/server/mock/restore/server.go @@ -21,17 +21,24 @@ func (s *Server) Create(ctx context.Context, req *pb.RestoreCreateRequest) (*pb. return nil, fmt.Errorf("backup not provided") } - environment, err := s.Model.GetEnvironment(req.Environment) - if err != nil { - return nil, err + var backup *model.Backup + exists := false + for _, environment := range s.Model.GetEnvironments() { + backup, exists = environment.Backup[req.Backup] + if exists { + break + } } - - backup, exists := environment.Backup[req.Backup] if !exists { return nil, fmt.Errorf("backup does not exist") } - if backup.Status() == pb.BackupStatus_InProgress { - return nil, fmt.Errorf("backup is not yet complete") + if backup.Status() != pb.BackupStatus_Completed { + return nil, fmt.Errorf("backup is not available for restore") + } + + environment, err := s.Model.GetEnvironment(req.Environment) + if err != nil { + return nil, err } restore := model.NewRestore(environment.Environment.Name, req.Backup) @@ -47,24 +54,11 @@ func (s *Server) Get(ctx context.Context, req *pb.RestoreGetRequest) (*pb.Restor return nil, fmt.Errorf("id not provided") } - restore, err := s.Model.GetRestore(req.ID) + restoreResponse, err := buildRestore(s.Model, req.ID) if err != nil { return nil, err } - restoreResponse := &pb.RestoreStatus{ - Name: restore.Id, - Backup: restore.BackupId, - Phase: restore.Status(), - StartTime: restore.StartTime.Format(time.RFC3339), - Duration: restore.Duration.String(), - Databases: []string{"default"}, - Volumes: []string{"public", "private"}, - } - if restore.Status() == pb.RestoreStatus_Completed { - restoreResponse.CompletionTime = restore.StartTime.Add(restore.Duration).Format(time.RFC3339) - } - resp := &pb.RestoreGetResponse{ Restore: restoreResponse, } @@ -80,18 +74,7 @@ func (s *Server) List(ctx context.Context, req *pb.RestoreListRequest) (*pb.Rest resp := &pb.RestoreListResponse{} for _, value := range environment.Restore { - summary := &pb.RestoreStatus{ - Name: value.Id, - Backup: value.BackupId, - Phase: value.Status(), - StartTime: value.StartTime.Format(time.RFC3339), - Duration: value.Duration.String(), - Databases: []string{"default"}, - Volumes: []string{"public", "private"}, - } - if value.Status() == pb.RestoreStatus_Completed { - summary.CompletionTime = value.StartTime.Add(value.Duration).Format(time.RFC3339) - } + summary, _ := buildRestore(s.Model, value.Id) resp.List = append(resp.List, summary) } @@ -101,3 +84,25 @@ func (s *Server) List(ctx context.Context, req *pb.RestoreListRequest) (*pb.Rest return resp, nil } + +func buildRestore(model *model.Model, id string) (*pb.RestoreStatus, error) { + restore, err := model.GetRestore(id) + if err != nil { + return nil, err + } + + restoreResponse := &pb.RestoreStatus{ + Name: restore.Id, + Backup: restore.BackupId, + Phase: restore.Status(), + StartTime: restore.StartTime.Format(time.RFC3339), + Duration: restore.Duration.String(), + Databases: []string{"default"}, + Volumes: []string{"public", "private"}, + } + if restore.Status() != pb.RestoreStatus_InProgress { + restoreResponse.CompletionTime = restore.StartTime.Add(restore.Duration).Format(time.RFC3339) + } + + return restoreResponse, nil +}