diff --git a/cmd/integration/make_backup/main.go b/cmd/integration/make_backup/main.go index fcc31f07..e8d71178 100644 --- a/cmd/integration/make_backup/main.go +++ b/cmd/integration/make_backup/main.go @@ -295,6 +295,39 @@ func main() { if len(schedules.Schedules[0].SourcePathsToExclude) != 0 { log.Panicln("unexpected number of source paths to exclude") } + + inactiveSchedule, err := scheduleClient.ToggleBackupSchedule( + context.Background(), &pb.ToggleBackupScheduleRequest{ + Id: newSchedule.Id, + ActiveState: false, + }, + ) + + if err != nil { + log.Panicf("failed to deactivate backup schedule: %v", err) + } + + if inactiveSchedule.Status != pb.BackupSchedule_INACTIVE { + log.Panicf("expected INACTIVE backup schedule status, but received: %s", inactiveSchedule.Status.String()) + } + + schedules, err = scheduleClient.ListBackupSchedules( + context.Background(), &pb.ListBackupSchedulesRequest{ + ContainerId: containerID, + DatabaseNameMask: "%", + }, + ) + if err != nil { + log.Panicf("failed to list backup schedules: %v", err) + } + if len(schedules.Schedules) != 1 { + log.Panicln("unexpected number of schedules") + } + + if schedules.Schedules[0].Status != pb.BackupSchedule_INACTIVE { + log.Panicf("expected INACTIVE backup schedule status, but received: %s", schedules.Schedules[0].Status.String()) + } + deletedSchedule, err := scheduleClient.DeleteBackupSchedule( context.Background(), &pb.DeleteBackupScheduleRequest{ Id: schedule.Id, @@ -322,4 +355,15 @@ func main() { if schedules.Schedules[0].Status != pb.BackupSchedule_DELETED { log.Panicf("expected DELETED backup schedule status, but received: %s", schedules.Schedules[0].Status.String()) } + + _, err = scheduleClient.ToggleBackupSchedule( + context.Background(), &pb.ToggleBackupScheduleRequest{ + Id: deletedSchedule.Id, + ActiveState: true, + }, + ) + + if err == nil { + log.Panicln("deleted schedule was successfully activated") + } } diff --git a/internal/server/services/backup_schedule/backup_schedule_service.go b/internal/server/services/backup_schedule/backup_schedule_service.go index ddb53506..e763f86e 100644 --- a/internal/server/services/backup_schedule/backup_schedule_service.go +++ b/internal/server/services/backup_schedule/backup_schedule_service.go @@ -180,6 +180,12 @@ func (s *BackupScheduleService) UpdateBackupSchedule( }); err != nil { return nil, err } + + if schedule.Status == types.BackupScheduleStateDeleted { + xlog.Error(ctx, "backup schedule was deleted") + return nil, status.Error(codes.FailedPrecondition, "backup schedule was deleted") + } + schedule.SourcePaths = request.SourcePaths schedule.SourcePathsToExclude = request.SourcePathsToExclude @@ -332,13 +338,76 @@ func (s *BackupScheduleService) ListBackupSchedules( } func (s *BackupScheduleService) ToggleBackupSchedule( - ctx context.Context, in *pb.ToggleBackupScheduleRequest, + ctx context.Context, request *pb.ToggleBackupScheduleRequest, ) (*pb.BackupSchedule, error) { ctx = grpcinfo.WithGRPCInfo(ctx) - ctx = xlog.With(ctx, zap.String("BackupScheduleID", in.GetId())) - xlog.Error(ctx, "ToggleBackupSchedule not implemented") - //TODO implement me - return nil, status.Error(codes.Internal, "not implemented") + + scheduleID := request.GetId() + ctx = xlog.With(ctx, zap.String("BackupScheduleID", scheduleID)) + + xlog.Debug(ctx, "ToggleBackupSchedule", zap.Stringer("request", request)) + + schedules, err := s.driver.SelectBackupSchedulesWithRPOInfo( + ctx, queries.NewReadTableQuery( + queries.WithRawQuery(GetScheduleQuery), + queries.WithParameters( + table.ValueParam("$schedule_id", table_types.StringValueFromString(scheduleID)), + ), + ), + ) + + if err != nil { + xlog.Error(ctx, "error getting backup schedule", zap.Error(err)) + return nil, status.Error(codes.Internal, "error getting backup schedule") + } + if len(schedules) == 0 { + xlog.Error(ctx, "backup schedule not found") + return nil, status.Error(codes.NotFound, "backup schedule not found") + } + + schedule := schedules[0] + ctx = xlog.With(ctx, zap.String("ContainerID", schedule.ContainerID)) + subject, err := auth.CheckAuth(ctx, s.auth, auth.PermissionBackupCreate, schedule.ContainerID, "") + if err != nil { + return nil, err + } + ctx = xlog.With(ctx, zap.String("SubjectID", subject)) + if err = s.CheckClientDbAccess(ctx, types.YdbConnectionParams{ + Endpoint: schedule.DatabaseEndpoint, + DatabaseName: schedule.DatabaseName, + }); err != nil { + return nil, err + } + + if schedule.Status == types.BackupScheduleStateDeleted { + xlog.Error(ctx, "backup schedule was deleted") + return nil, status.Error(codes.FailedPrecondition, "backup schedule was deleted") + } + + if request.GetActiveState() { + schedule.Status = types.BackupScheduleStateActive + } else { + schedule.Status = types.BackupScheduleStateInactive + } + + if schedule.ScheduleSettings != nil { + err = schedule.UpdateNextLaunch(s.clock.Now()) + if err != nil { + return nil, status.Error(codes.Internal, "failed to update next launch time") + } + } + + err = s.driver.ExecuteUpsert(ctx, queries.NewWriteTableQuery().WithUpdateBackupSchedule(*schedule)) + if err != nil { + xlog.Error( + ctx, "can't update backup schedule", zap.String("backup schedule", schedule.Proto(s.clock).String()), + zap.Error(err), + ) + return nil, status.Error(codes.Internal, "can't update backup schedule") + } + + xlog.Info(ctx, "ToggleBackupSchedule was completed successfully", zap.Stringer("schedule", schedule)) + return schedule.Proto(s.clock), nil } func (s *BackupScheduleService) DeleteBackupSchedule(