mirror of
https://github.com/eduardolat/pgbackweb.git
synced 2026-05-12 22:48:27 -05:00
Add restorations service and related functions
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package restorations
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
)
|
||||
|
||||
func (s *Service) CreateRestoration(
|
||||
ctx context.Context, params dbgen.RestorationsServiceCreateRestorationParams,
|
||||
) (dbgen.Restoration, error) {
|
||||
return s.dbgen.RestorationsServiceCreateRestoration(ctx, params)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
-- name: RestorationsServiceCreateRestoration :one
|
||||
INSERT INTO restorations (execution_id, database_id, status, message)
|
||||
VALUES (@execution_id, @database_id, @status, @message)
|
||||
RETURNING *;
|
||||
@@ -0,0 +1,7 @@
|
||||
package restorations
|
||||
|
||||
import "context"
|
||||
|
||||
func (s *Service) GetRestorationsQty(ctx context.Context) (int64, error) {
|
||||
return s.dbgen.RestorationsServiceGetRestorationsQty(ctx)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
-- name: RestorationsServiceGetRestorationsQty :one
|
||||
SELECT COUNT(*) FROM restorations;
|
||||
@@ -0,0 +1,54 @@
|
||||
package restorations
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/util/paginateutil"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type PaginateRestorationsParams struct {
|
||||
Page int
|
||||
Limit int
|
||||
ExecutionFilter uuid.NullUUID
|
||||
DatabaseFilter uuid.NullUUID
|
||||
}
|
||||
|
||||
func (s *Service) PaginateRestorations(
|
||||
ctx context.Context, params PaginateRestorationsParams,
|
||||
) (paginateutil.PaginateResponse, []dbgen.RestorationsServicePaginateRestorationsRow, error) {
|
||||
page := max(params.Page, 1)
|
||||
limit := min(max(params.Limit, 1), 100)
|
||||
|
||||
count, err := s.dbgen.RestorationsServicePaginateRestorationsCount(
|
||||
ctx, dbgen.RestorationsServicePaginateRestorationsCountParams{
|
||||
ExecutionID: params.ExecutionFilter,
|
||||
DatabaseID: params.DatabaseFilter,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return paginateutil.PaginateResponse{}, nil, err
|
||||
}
|
||||
|
||||
paginateParams := paginateutil.PaginateParams{
|
||||
Page: page,
|
||||
Limit: limit,
|
||||
}
|
||||
offset := paginateutil.CreateOffsetFromParams(paginateParams)
|
||||
paginateResponse := paginateutil.CreatePaginateResponse(paginateParams, int(count))
|
||||
|
||||
restorations, err := s.dbgen.RestorationsServicePaginateRestorations(
|
||||
ctx, dbgen.RestorationsServicePaginateRestorationsParams{
|
||||
ExecutionID: params.ExecutionFilter,
|
||||
DatabaseID: params.DatabaseFilter,
|
||||
Limit: int32(params.Limit),
|
||||
Offset: int32(offset),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return paginateutil.PaginateResponse{}, nil, err
|
||||
}
|
||||
|
||||
return paginateResponse, restorations, nil
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
-- name: RestorationsServicePaginateRestorationsCount :one
|
||||
SELECT COUNT(restorations.*)
|
||||
FROM restorations
|
||||
INNER JOIN executions ON executions.id = restorations.execution_id
|
||||
LEFT JOIN databases ON databases.id = restorations.database_id
|
||||
WHERE
|
||||
(
|
||||
sqlc.narg('execution_id')::UUID IS NULL
|
||||
OR
|
||||
restorations.execution_id = sqlc.narg('execution_id')::UUID
|
||||
)
|
||||
AND
|
||||
(
|
||||
sqlc.narg('database_id')::UUID IS NULL
|
||||
OR
|
||||
restorations.database_id = sqlc.narg('database_id')::UUID
|
||||
);
|
||||
|
||||
-- name: RestorationsServicePaginateRestorations :many
|
||||
SELECT
|
||||
restorations.*,
|
||||
databases.name AS database_name
|
||||
FROM restorations
|
||||
INNER JOIN executions ON executions.id = restorations.execution_id
|
||||
LEFT JOIN databases ON databases.id = restorations.database_id
|
||||
WHERE
|
||||
(
|
||||
sqlc.narg('execution_id')::UUID IS NULL
|
||||
OR
|
||||
restorations.execution_id = sqlc.narg('execution_id')::UUID
|
||||
)
|
||||
AND
|
||||
(
|
||||
sqlc.narg('database_id')::UUID IS NULL
|
||||
OR
|
||||
restorations.database_id = sqlc.narg('database_id')::UUID
|
||||
)
|
||||
ORDER BY restorations.started_at DESC
|
||||
LIMIT sqlc.arg('limit') OFFSET sqlc.arg('offset');
|
||||
@@ -0,0 +1,31 @@
|
||||
package restorations
|
||||
|
||||
import (
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/integration"
|
||||
"github.com/eduardolat/pgbackweb/internal/service/databases"
|
||||
"github.com/eduardolat/pgbackweb/internal/service/destinations"
|
||||
"github.com/eduardolat/pgbackweb/internal/service/executions"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
dbgen *dbgen.Queries
|
||||
ints *integration.Integration
|
||||
executionsService *executions.Service
|
||||
databasesService *databases.Service
|
||||
destinationsService *destinations.Service
|
||||
}
|
||||
|
||||
func New(
|
||||
dbgen *dbgen.Queries, ints *integration.Integration,
|
||||
executionsService *executions.Service, databasesService *databases.Service,
|
||||
destinationsService *destinations.Service,
|
||||
) *Service {
|
||||
return &Service{
|
||||
dbgen: dbgen,
|
||||
ints: ints,
|
||||
executionsService: executionsService,
|
||||
databasesService: databasesService,
|
||||
destinationsService: destinationsService,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package restorations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
"github.com/eduardolat/pgbackweb/internal/logger"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// RunRestoration runs a backup restoration
|
||||
func (s *Service) RunRestoration(
|
||||
ctx context.Context,
|
||||
executionID uuid.UUID,
|
||||
databaseID uuid.NullUUID,
|
||||
connString string,
|
||||
) error {
|
||||
updateRes := func(params dbgen.RestorationsServiceUpdateRestorationParams) error {
|
||||
_, err := s.dbgen.RestorationsServiceUpdateRestoration(
|
||||
ctx, params,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
logError := func(err error) {
|
||||
dbID := "empty"
|
||||
if databaseID.Valid {
|
||||
dbID = databaseID.UUID.String()
|
||||
}
|
||||
logger.Error("error running restoration", logger.KV{
|
||||
"execution_id": executionID.String(),
|
||||
"database_id": dbID,
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
res, err := s.CreateRestoration(ctx, dbgen.RestorationsServiceCreateRestorationParams{
|
||||
ExecutionID: executionID,
|
||||
DatabaseID: databaseID,
|
||||
Status: "running",
|
||||
})
|
||||
if err != nil {
|
||||
logError(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if !databaseID.Valid && connString == "" {
|
||||
err := fmt.Errorf("database_id or connection_string must be provided")
|
||||
logError(err)
|
||||
return updateRes(dbgen.RestorationsServiceUpdateRestorationParams{
|
||||
ID: res.ID,
|
||||
Status: sql.NullString{Valid: true, String: "failed"},
|
||||
Message: sql.NullString{Valid: true, String: err.Error()},
|
||||
FinishedAt: sql.NullTime{Valid: true, Time: time.Now()},
|
||||
})
|
||||
}
|
||||
|
||||
execution, err := s.executionsService.GetExecution(ctx, executionID)
|
||||
if err != nil {
|
||||
logError(err)
|
||||
return updateRes(dbgen.RestorationsServiceUpdateRestorationParams{
|
||||
ID: res.ID,
|
||||
Status: sql.NullString{Valid: true, String: "failed"},
|
||||
Message: sql.NullString{Valid: true, String: err.Error()},
|
||||
FinishedAt: sql.NullTime{Valid: true, Time: time.Now()},
|
||||
})
|
||||
}
|
||||
|
||||
if execution.Status != "success" || !execution.Path.Valid {
|
||||
err := fmt.Errorf("backup execution must be successful")
|
||||
logError(err)
|
||||
return updateRes(dbgen.RestorationsServiceUpdateRestorationParams{
|
||||
ID: res.ID,
|
||||
Status: sql.NullString{Valid: true, String: "failed"},
|
||||
Message: sql.NullString{Valid: true, String: err.Error()},
|
||||
FinishedAt: sql.NullTime{Valid: true, Time: time.Now()},
|
||||
})
|
||||
}
|
||||
|
||||
if databaseID.Valid {
|
||||
db, err := s.databasesService.GetDatabase(ctx, databaseID.UUID)
|
||||
if err != nil {
|
||||
logError(err)
|
||||
return updateRes(dbgen.RestorationsServiceUpdateRestorationParams{
|
||||
ID: res.ID,
|
||||
Status: sql.NullString{Valid: true, String: "failed"},
|
||||
Message: sql.NullString{Valid: true, String: err.Error()},
|
||||
FinishedAt: sql.NullTime{Valid: true, Time: time.Now()},
|
||||
})
|
||||
}
|
||||
connString = db.DecryptedConnectionString
|
||||
}
|
||||
|
||||
pgVersion, err := s.ints.PGClient.ParseVersion(execution.DatabasePgVersion)
|
||||
if err != nil {
|
||||
logError(err)
|
||||
return updateRes(dbgen.RestorationsServiceUpdateRestorationParams{
|
||||
ID: res.ID,
|
||||
Status: sql.NullString{Valid: true, String: "failed"},
|
||||
Message: sql.NullString{Valid: true, String: err.Error()},
|
||||
FinishedAt: sql.NullTime{Valid: true, Time: time.Now()},
|
||||
})
|
||||
}
|
||||
|
||||
err = s.ints.PGClient.Ping(pgVersion, connString)
|
||||
if err != nil {
|
||||
logError(err)
|
||||
return updateRes(dbgen.RestorationsServiceUpdateRestorationParams{
|
||||
ID: res.ID,
|
||||
Status: sql.NullString{Valid: true, String: "failed"},
|
||||
Message: sql.NullString{Valid: true, String: err.Error()},
|
||||
FinishedAt: sql.NullTime{Valid: true, Time: time.Now()},
|
||||
})
|
||||
}
|
||||
|
||||
isLocal, zipURLOrPath, err := s.executionsService.GetExecutionDownloadLinkOrPath(
|
||||
ctx, executionID,
|
||||
)
|
||||
if err != nil {
|
||||
logError(err)
|
||||
return updateRes(dbgen.RestorationsServiceUpdateRestorationParams{
|
||||
ID: res.ID,
|
||||
Status: sql.NullString{Valid: true, String: "failed"},
|
||||
Message: sql.NullString{Valid: true, String: err.Error()},
|
||||
FinishedAt: sql.NullTime{Valid: true, Time: time.Now()},
|
||||
})
|
||||
}
|
||||
|
||||
err = s.ints.PGClient.RestoreZip(
|
||||
pgVersion, connString, isLocal, zipURLOrPath,
|
||||
)
|
||||
if err != nil {
|
||||
logError(err)
|
||||
return updateRes(dbgen.RestorationsServiceUpdateRestorationParams{
|
||||
ID: res.ID,
|
||||
Status: sql.NullString{Valid: true, String: "failed"},
|
||||
Message: sql.NullString{Valid: true, String: err.Error()},
|
||||
FinishedAt: sql.NullTime{Valid: true, Time: time.Now()},
|
||||
})
|
||||
}
|
||||
|
||||
logger.Info("backup restored successfully", logger.KV{
|
||||
"restoration_id": res.ID.String(),
|
||||
"execution_id": executionID.String(),
|
||||
})
|
||||
return updateRes(dbgen.RestorationsServiceUpdateRestorationParams{
|
||||
ID: res.ID,
|
||||
Status: sql.NullString{Valid: true, String: "success"},
|
||||
Message: sql.NullString{Valid: true, String: "Backup restored successfully"},
|
||||
FinishedAt: sql.NullTime{Valid: true, Time: time.Now()},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package restorations
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/eduardolat/pgbackweb/internal/database/dbgen"
|
||||
)
|
||||
|
||||
func (s *Service) UpdateRestoration(
|
||||
ctx context.Context, params dbgen.RestorationsServiceUpdateRestorationParams,
|
||||
) (dbgen.Restoration, error) {
|
||||
return s.dbgen.RestorationsServiceUpdateRestoration(ctx, params)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
-- name: RestorationsServiceUpdateRestoration :one
|
||||
UPDATE restorations
|
||||
SET
|
||||
status = COALESCE(sqlc.narg('status'), status),
|
||||
message = COALESCE(sqlc.narg('message'), message),
|
||||
finished_at = COALESCE(sqlc.narg('finished_at'), finished_at)
|
||||
WHERE id = @id
|
||||
RETURNING *;
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/eduardolat/pgbackweb/internal/service/databases"
|
||||
"github.com/eduardolat/pgbackweb/internal/service/destinations"
|
||||
"github.com/eduardolat/pgbackweb/internal/service/executions"
|
||||
"github.com/eduardolat/pgbackweb/internal/service/restorations"
|
||||
"github.com/eduardolat/pgbackweb/internal/service/users"
|
||||
)
|
||||
|
||||
@@ -20,6 +21,7 @@ type Service struct {
|
||||
DestinationsService *destinations.Service
|
||||
ExecutionsService *executions.Service
|
||||
UsersService *users.Service
|
||||
RestorationsService *restorations.Service
|
||||
}
|
||||
|
||||
func New(
|
||||
@@ -32,6 +34,9 @@ func New(
|
||||
executionsService := executions.New(env, dbgen, ints)
|
||||
usersService := users.New(dbgen)
|
||||
backupsService := backups.New(dbgen, cr, executionsService)
|
||||
restorationsService := restorations.New(
|
||||
dbgen, ints, executionsService, databasesService, destinationsService,
|
||||
)
|
||||
|
||||
return &Service{
|
||||
AuthService: authService,
|
||||
@@ -40,5 +45,6 @@ func New(
|
||||
DestinationsService: destinationsService,
|
||||
ExecutionsService: executionsService,
|
||||
UsersService: usersService,
|
||||
RestorationsService: restorationsService,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user