adds a container-of-things to reduce mostly-unused return value bloat, and updates some func names to be more appropriate to their behavior. No logical changes, just renaming/movement. --- #### Does this PR need a docs update or release note? - [x] ⛔ No #### Type of change - [x] 🧹 Tech Debt/Cleanup
199 lines
4.5 KiB
Go
199 lines
4.5 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/alcionai/clues"
|
|
"github.com/spf13/cobra"
|
|
"golang.org/x/exp/maps"
|
|
|
|
"github.com/alcionai/corso/src/cli/config"
|
|
"github.com/alcionai/corso/src/cli/utils"
|
|
"github.com/alcionai/corso/src/pkg/logger"
|
|
"github.com/alcionai/corso/src/pkg/path"
|
|
"github.com/alcionai/corso/src/pkg/repository"
|
|
"github.com/alcionai/corso/src/pkg/storage"
|
|
"github.com/alcionai/corso/src/pkg/store"
|
|
)
|
|
|
|
// deleteBackups connects to the repository and deletes all backups for
|
|
// service that are at least deletionDays old. Returns the IDs of all backups
|
|
// that were deleted.
|
|
// Only supported for S3 repos currently.
|
|
func deleteBackups(
|
|
ctx context.Context,
|
|
service path.ServiceType,
|
|
deletionDays int,
|
|
) ([]string, error) {
|
|
ctx = clues.Add(ctx, "cutoff_days", deletionDays)
|
|
|
|
r, _, err := utils.GetAccountAndConnectWithOverrides(ctx, service, storage.ProviderS3, nil)
|
|
if err != nil {
|
|
return nil, clues.Wrap(err, "connecting to account").WithClues(ctx)
|
|
}
|
|
|
|
defer r.Close(ctx)
|
|
|
|
backups, err := r.BackupsByTag(ctx, store.Service(service))
|
|
if err != nil {
|
|
return nil, clues.Wrap(err, "listing backups").WithClues(ctx)
|
|
}
|
|
|
|
var (
|
|
deleted []string
|
|
cutoff = time.Now().Add(-time.Hour * 24 * time.Duration(deletionDays))
|
|
)
|
|
|
|
for _, backup := range backups {
|
|
if backup.StartAndEndTime.CompletedAt.Before(cutoff) {
|
|
if err := r.DeleteBackups(ctx, true, backup.ID.String()); err != nil {
|
|
return nil, clues.Wrap(
|
|
err,
|
|
"deleting backup").
|
|
With("backup_id", backup.ID).
|
|
WithClues(ctx)
|
|
}
|
|
|
|
deleted = append(deleted, backup.ID.String())
|
|
logAndPrint(ctx, "Deleted backup %s", backup.ID.String())
|
|
}
|
|
}
|
|
|
|
return deleted, nil
|
|
}
|
|
|
|
// pitrListBackups connects to the repository at the given point in time and
|
|
// lists the backups for service. It then checks the list of backups contains
|
|
// the backups in backupIDs.
|
|
// Only supported for S3 repos currently.
|
|
func pitrListBackups(
|
|
ctx context.Context,
|
|
service path.ServiceType,
|
|
pitr time.Time,
|
|
backupIDs []string,
|
|
) error {
|
|
logAndPrint(
|
|
ctx,
|
|
"looking for %d backup(s) using timestamp %v\n",
|
|
len(backupIDs),
|
|
pitr)
|
|
|
|
if len(backupIDs) == 0 {
|
|
return nil
|
|
}
|
|
|
|
ctx = clues.Add(ctx, "pitr_time", pitr, "search_backups", backupIDs)
|
|
|
|
// TODO(ashmrtn): This may be moved into CLI layer at some point when we add
|
|
// flags for opening a repo at a point in time.
|
|
cfg, err := config.GetConfigRepoDetails(
|
|
ctx,
|
|
storage.ProviderS3,
|
|
true,
|
|
true,
|
|
nil)
|
|
if err != nil {
|
|
return clues.Wrap(err, "getting config info")
|
|
}
|
|
|
|
opts := utils.ControlWithConfig(cfg)
|
|
opts.Repo.ViewTimestamp = &pitr
|
|
|
|
r, err := repository.New(
|
|
ctx,
|
|
cfg.Account,
|
|
cfg.Storage,
|
|
opts,
|
|
cfg.RepoID)
|
|
if err != nil {
|
|
return clues.Wrap(err, "creating a repo")
|
|
}
|
|
|
|
err = r.Connect(ctx)
|
|
if err != nil {
|
|
return clues.Wrap(err, "connecting to the repository")
|
|
}
|
|
|
|
defer r.Close(ctx)
|
|
|
|
backups, err := r.BackupsByTag(ctx, store.Service(service))
|
|
if err != nil {
|
|
return clues.Wrap(err, "listing backups").WithClues(ctx)
|
|
}
|
|
|
|
bups := map[string]struct{}{}
|
|
|
|
for _, backup := range backups {
|
|
bups[backup.ID.String()] = struct{}{}
|
|
}
|
|
|
|
ctx = clues.Add(ctx, "found_backups", maps.Keys(bups))
|
|
|
|
for _, backupID := range backupIDs {
|
|
if _, ok := bups[backupID]; !ok {
|
|
return clues.New("looking for backup").
|
|
With("search_backup_id", backupID).
|
|
WithClues(ctx)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func main() {
|
|
var (
|
|
service path.ServiceType
|
|
cc = cobra.Command{}
|
|
)
|
|
|
|
cc.SetContext(context.Background())
|
|
|
|
if err := config.InitFunc(&cc, []string{}); err != nil {
|
|
return
|
|
}
|
|
|
|
switch serviceName := os.Getenv("SERVICE"); serviceName {
|
|
case "exchange":
|
|
service = path.ExchangeService
|
|
case "onedrive":
|
|
service = path.OneDriveService
|
|
case "sharepoint":
|
|
service = path.SharePointService
|
|
default:
|
|
fatal(cc.Context(), "unknown service", nil)
|
|
}
|
|
|
|
ctx := clues.Add(cc.Context(), "service", service)
|
|
|
|
days, err := strconv.Atoi(os.Getenv("DELETION_DAYS"))
|
|
if err != nil {
|
|
fatal(ctx, "invalid number of days provided", nil)
|
|
}
|
|
|
|
beforeDel := time.Now()
|
|
|
|
backups, err := deleteBackups(ctx, service, days)
|
|
if err != nil {
|
|
fatal(ctx, "deleting backups", clues.Stack(err))
|
|
}
|
|
|
|
if err := pitrListBackups(ctx, service, beforeDel, backups); err != nil {
|
|
fatal(ctx, "listing backups from point in time", clues.Stack(err))
|
|
}
|
|
}
|
|
|
|
func fatal(ctx context.Context, msg string, err error) {
|
|
logger.CtxErr(ctx, err).Error("test failure: " + msg)
|
|
fmt.Println(msg+": ", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
func logAndPrint(ctx context.Context, tmpl string, vs ...any) {
|
|
logger.Ctx(ctx).Infof(tmpl, vs...)
|
|
fmt.Printf(tmpl+"\n", vs...)
|
|
}
|