Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
36e5c81
Add --profile flag to cleanup command
jcechace Nov 12, 2025
dfddd80
Add --profile flag to delete-backup command
jcechace Nov 13, 2025
acf607f
Add profile validation to PBM when executing delete-backup or cleanup
jcechace Nov 13, 2025
5f60fea
Profile support for delete and cleanup in PBM CLI
jcechace Nov 13, 2025
90d3623
Profile support for delete and cleanup in PBM agent
jcechace Nov 13, 2025
3483320
Don't require storage sync when cleaning up backups in profile
jcechace Nov 18, 2025
f168e05
Use an appropriate variable name when constructing the cleanup cli co…
jcechace Nov 20, 2025
8677ace
Log profile name when running delete-backup
jcechace Nov 20, 2025
a2c65c5
Don't continue when running cleanup with missing config
jcechace Nov 20, 2025
d44abd3
Include chunks in CleanupInfo only for the default storage profile
jcechace Nov 20, 2025
4cd4f7c
Disallow --profile together with name when running delete-backup
jcechace Nov 20, 2025
1b04ae4
Fixed error messages when running backup-delete command with invalid …
jcechace Nov 20, 2025
d90fb0e
DeleteBackupsBefore now takes storage as a parameter
jcechace Nov 24, 2025
e140609
Actual backup deletion extracted into DeleteBackupUnsafe to avoid dup…
jcechace Nov 24, 2025
91dc2f3
Improved code documentation related to deletes/cleanups
jcechace Nov 24, 2025
68c0775
Tests for cleanup and delete-backup components
jcechace Nov 18, 2025
ed3582d
Added testify as a direct dependency
jcechace Nov 20, 2025
dc3d250
Fixed a storage error when not using profile
jcechace Nov 27, 2025
b6fdb4b
Fixed a case when backups in profile were not deleted due to being co…
jcechace Nov 27, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions cmd/pbm-agent/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,13 @@ func (a *Agent) Delete(ctx context.Context, d *ctrl.DeleteBackupCmd, opid ctrl.O
return
}

l.Info("deleting backups older than %v", t)
err = backup.DeleteBackupBefore(ctx, a.leadConn, t, bcpType, nodeInfo.Me)
stg, err := util.GetProfiledStorage(ctx, a.leadConn, d.Profile, nodeInfo.Me, l)
if err != nil {
l.Error("get storage: %v", err)
return
}
l.Info("deleting backups (profile: %q) older than %v", d.Profile, t)
err = backup.DeleteBackupBefore(ctx, a.leadConn, stg, d.Profile, bcpType, t)
if err != nil {
l.Error("deleting: %v", err)
return
Expand Down Expand Up @@ -254,20 +259,22 @@ func (a *Agent) Cleanup(ctx context.Context, d *ctrl.CleanupCmd, opid ctrl.OPID,
return
}

cfg, err := config.GetConfig(ctx, a.leadConn)
cfg, err := config.GetProfiledConfig(ctx, a.leadConn, d.Profile)
if err != nil {
l.Error("get config: %v", err)
return
}

stg, err := util.StorageFromConfig(&cfg.Storage, a.brief.Me, l)
if err != nil {
l.Error("get storage: " + err.Error())
return
}

eg := errgroup.Group{}
eg.SetLimit(runtime.NumCPU())

cr, err := backup.MakeCleanupInfo(ctx, a.leadConn, d.OlderThan)
cr, err := backup.MakeCleanupInfo(ctx, a.leadConn, d.OlderThan, d.Profile)
if err != nil {
l.Error("make cleanup report: " + err.Error())
return
Expand All @@ -289,15 +296,18 @@ func (a *Agent) Cleanup(ctx context.Context, d *ctrl.CleanupCmd, opid ctrl.OPID,
bcp := &cr.Backups[i]

eg.Go(func() error {
err := backup.DeleteBackupFiles(stg, bcp.Name)
return errors.Wrapf(err, "delete backup files %q", bcp.Name)
err := backup.DeleteBackupData(ctx, a.leadConn, stg, bcp.Name)
return errors.Wrapf(err, "delete backup %q", bcp.Name)
})
}
if err := eg.Wait(); err != nil {
l.Error(err.Error())
}

err = resync.Resync(ctx, a.leadConn, &cfg.Storage, a.brief.Me, false)
if d.Profile == "" {
err = resync.Resync(ctx, a.leadConn, &cfg.Storage, a.brief.Me, false)
}

if err != nil {
l.Error("storage resync: " + err.Error())
}
Expand All @@ -306,7 +316,7 @@ func (a *Agent) Cleanup(ctx context.Context, d *ctrl.CleanupCmd, opid ctrl.OPID,
func (a *Agent) deletePITRImpl(ctx context.Context, ts primitive.Timestamp) error {
l := log.LogEventFromContext(ctx)

r, err := backup.MakeCleanupInfo(ctx, a.leadConn, ts)
r, err := backup.MakeCleanupInfo(ctx, a.leadConn, ts, "")
if err != nil {
return errors.Wrap(err, "get pitr chunks")
}
Expand Down
36 changes: 30 additions & 6 deletions cmd/pbm/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"go.mongodb.org/mongo-driver/bson/primitive"

"github.com/percona/percona-backup-mongodb/pbm/backup"
"github.com/percona/percona-backup-mongodb/pbm/config"
"github.com/percona/percona-backup-mongodb/pbm/connect"
"github.com/percona/percona-backup-mongodb/pbm/ctrl"
"github.com/percona/percona-backup-mongodb/pbm/defs"
Expand All @@ -25,6 +26,7 @@ type deleteBcpOpts struct {
name string
olderThan string
bcpType string
profile string
dryRun bool
yes bool
}
Expand All @@ -36,14 +38,26 @@ func deleteBackup(
d *deleteBcpOpts,
) (fmt.Stringer, error) {
if d.name == "" && d.olderThan == "" {
return nil, errors.New("either --name or --older-than should be set")
return nil, errors.New("either [name] or --older-than should be set")
}
if d.name != "" && d.olderThan != "" {
return nil, errors.New("cannot use --name and --older-than at the same command")
return nil, errors.New("cannot use [name] and --older-than at the same command")
}
if d.name != "" && d.profile != "" {
return nil, errors.New("cannot use [name] with --profile")
}
if d.bcpType != "" && d.olderThan == "" {
return nil, errors.New("cannot use --type without --older-than")
}
if d.profile != "" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably we should forbidden delete by name in combination with --profile:

pbm delete <backup-name> --profile p1

In case when backup-name is on default storage or on e.g. p2, above command will still delete backup, so that's kind of misleading.

Alternative is to not delete that backup in case when that backup-name is not present within the profile storage, but I'd recommend the first solution.

_, err := config.GetProfile(ctx, conn, d.profile)
if err != nil {
if errors.Is(err, config.ErrMissedConfigProfile) {
return nil, errors.Errorf("profile %q is not found", d.profile)
}
return nil, errors.Wrap(err, "get config")
}
}
if !d.dryRun {
err := checkForAnotherOperation(ctx, pbm)
if err != nil {
Expand Down Expand Up @@ -135,7 +149,7 @@ func deleteManyBackup(ctx context.Context, pbm *sdk.Client, d *deleteBcpOpts) (s
if err != nil {
return sdk.NoOpID, errors.Wrap(err, "parse --type")
}
backups, err := sdk.ListDeleteBackupBefore(ctx, pbm, ts, bcpType)
backups, err := sdk.ListDeleteBackupBefore(ctx, pbm, ts, bcpType, d.profile)
if err != nil {
return sdk.NoOpID, errors.Wrap(err, "fetch backup list")
}
Expand All @@ -151,7 +165,7 @@ func deleteManyBackup(ctx context.Context, pbm *sdk.Client, d *deleteBcpOpts) (s
}
}

cid, err := pbm.DeleteBackupBefore(ctx, ts, sdk.DeleteBackupBeforeOptions{Type: bcpType})
cid, err := pbm.DeleteBackupBefore(ctx, ts, sdk.DeleteBackupBeforeOptions{Type: bcpType, Profile: d.profile})
return cid, errors.Wrap(err, "schedule delete")
}

Expand Down Expand Up @@ -255,6 +269,7 @@ type cleanupOptions struct {
wait bool
waitTime time.Duration
dryRun bool
profile string
}

func doCleanup(ctx context.Context, conn connect.Client, pbm *sdk.Client, d *cleanupOptions) (fmt.Stringer, error) {
Expand All @@ -267,14 +282,23 @@ func doCleanup(ctx context.Context, conn connect.Client, pbm *sdk.Client, d *cle
realTime := n.Format(time.RFC3339)
return nil, errors.Errorf("--older-than %q is after now %q", providedTime, realTime)
}
if d.profile != "" {
_, err := config.GetProfile(ctx, conn, d.profile)
if err != nil {
if errors.Is(err, config.ErrMissedConfigProfile) {
return nil, errors.Errorf("profile %q is not found", d.profile)
}
return nil, errors.Wrap(err, "get config")
}
}
if !d.dryRun {
err := checkForAnotherOperation(ctx, pbm)
if err != nil {
return nil, err
}
}

info, err := pbm.CleanupReport(ctx, ts)
info, err := pbm.CleanupReport(ctx, ts, d.profile)
if err != nil {
return nil, errors.Wrap(err, "make cleanup report")
}
Expand All @@ -296,7 +320,7 @@ func doCleanup(ctx context.Context, conn connect.Client, pbm *sdk.Client, d *cle
}
}

cid, err := pbm.RunCleanup(ctx, ts)
cid, err := pbm.RunCleanup(ctx, ts, d.profile)
if err != nil {
return nil, errors.Wrap(err, "send command")
}
Expand Down
22 changes: 15 additions & 7 deletions cmd/pbm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,32 +335,36 @@ func (app *pbmApp) buildCancelBackupCmd() *cobra.Command {
func (app *pbmApp) buildCleanupCmd() *cobra.Command {
cleanupOpts := cleanupOptions{}

deletePitrCmd := &cobra.Command{
cleanupCmd := &cobra.Command{
Use: "cleanup",
Short: "Delete Backups and PITR chunks",
RunE: app.wrapRunE(func(cmd *cobra.Command, args []string) (fmt.Stringer, error) {
return doCleanup(app.ctx, app.conn, app.pbm, &cleanupOpts)
}),
}

deletePitrCmd.Flags().StringVar(
cleanupCmd.Flags().StringVar(
&cleanupOpts.olderThan, "older-than", "",
fmt.Sprintf("Delete backups older than date/time in format %s or %s", datetimeFormat, dateFormat),
)
deletePitrCmd.Flags().BoolVarP(
cleanupCmd.Flags().BoolVarP(
&cleanupOpts.yes, "yes", "y", false, "Don't ask for confirmation",
)
deletePitrCmd.Flags().BoolVarP(
cleanupCmd.Flags().BoolVarP(
&cleanupOpts.wait, "wait", "w", false, "Wait for deletion done",
)
deletePitrCmd.Flags().DurationVar(
cleanupCmd.Flags().DurationVar(
&cleanupOpts.waitTime, "wait-time", 0, "Maximum wait time",
)
deletePitrCmd.Flags().BoolVar(
cleanupCmd.Flags().BoolVar(
&cleanupOpts.dryRun, "dry-run", false, "Report but do not delete",
)
cleanupCmd.Flags().StringVar(
&cleanupOpts.profile, "profile", "",
"Name of the PBM profile to use for cleanup. Uses the default profile if omitted.",
)

return deletePitrCmd
return cleanupCmd
}

func (app *pbmApp) buildConfigCmd() *cobra.Command {
Expand Down Expand Up @@ -560,6 +564,10 @@ func (app *pbmApp) buildDeleteBackupCmd() *cobra.Command {
deleteBcpCmd.Flags().BoolVar(
&deleteBcpOptions.dryRun, "dry-run", false, "Report but do not delete",
)
deleteBcpCmd.Flags().StringVar(
&deleteBcpOptions.profile, "profile", "",
"Name of the PBM profile to use for delete. Uses the default profile if omitted.",
)

return deleteBcpCmd
}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/spf13/cobra v1.10.1
github.com/spf13/viper v1.21.0
github.com/stretchr/testify v1.11.1
github.com/testcontainers/testcontainers-go v0.39.0
github.com/testcontainers/testcontainers-go/modules/minio v0.34.0
github.com/testcontainers/testcontainers-go/modules/mongodb v0.39.0
Expand Down Expand Up @@ -126,7 +127,6 @@ require (
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
github.com/stretchr/testify v1.11.1 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tinylib/msgp v1.4.0 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
Expand Down
Loading
Loading