corso/src/cmd/longevity_test/longevity.go
Keepers 3c96eb6437
quick cleanup before next step (#4347)
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
2023-09-27 17:17:31 +00:00

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...)
}