Skip to content

feat(mpg): add --pitr-time to restore - #5051

Merged
tripledoublev merged 5 commits into
masterfrom
vincent/mpg-restore-pitr-flag
Aug 7, 2026
Merged

feat(mpg): add --pitr-time to restore#5051
tripledoublev merged 5 commits into
masterfrom
vincent/mpg-restore-pitr-flag

Conversation

@tripledoublev

@tripledoublev tripledoublev commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds --pitr-time <RFC3339_TIMESTAMP> to fly mpg restore. Exactly one of --backup-id or --pitr-time is required.
  • Rejects malformed timestamps or timestamps without an explicit offset before making a network request.
  • Forwards the original timestamp unchanged; ui-ex normalizes it to UTC.
  • Rejects --pitr-time for v1 clusters client-side.
  • Sends pitr_time only through the v2 restore request.
  • Uses neutral restore wording instead of assuming every restore uses a backup ID.

Test plan

  • go test ./internal/command/mpg/... ./internal/uiex/mpg/v1/... ./internal/uiex/mpg/v2/...
  • go vet ./...
  • gofmt -l .

Live-tested against fra-staging

Built this branch and pointed it at the v2 staging API, against an MPG v2 cluster on with timestamped test rows inserted directly via psql:

  • --pitr-time + --name together — succeeded, correct cluster name used
  • --pitr-time alone (no --name) — succeeded, default name uses the dash convention (cluster-restored-YYYYMMDD-HHMM)
  • --backup-id + --name together — succeeded (regression check: confirmed unaffected by the PITR changes)
  • --backup-id alone (no --name) — succeeded, same dash-convention default name
  • Malformed --pitr-time — rejected client-side with a clear RFC3339 error, no network call
  • --backup-id + --pitr-time together — rejected client-side as mutually exclusive
  • End-to-end correctness: restored to a specific minute-level timestamp and confirmed the restored cluster had exactly the rows written before that point and none after (done twice, different timestamps, both exact)

Dependencies

Stacked on #5044. The ui-ex v2 PITR API must be merged and deployed before this is released.

Base automatically changed from vincent/mpg-restore-name to master August 6, 2026 14:50
ui-ex just shipped point-in-time restore (pitr_time) as an alternative
to --backup-id across its v2/dashboard and public Flaps restore APIs.
Adds the matching client flag: exactly one of --backup-id/--pitr-time
is required, and v1 clusters -- which don't support PITR at all, a
deliberate product decision -- are rejected client-side with the same
error message the v1 API itself returns, instead of round-tripping to
get a 422.
An independent review of the --pitr-time flag flagged the new
one-of/mutually-exclusive/v1-rejection validation and the v2 JSON
serialization as untested. Adds:

- runRestore coverage for: neither flag, both flags, v1 rejecting
  pitr_time (asserting no restore call is issued), and v1/v2 dispatch
  for both backup_id and pitr_time.
- a serialization test proving RestoreClusterBackupInput's omitempty
  actually drops the unused field from the JSON body, not just that
  the Go struct field is zero-valued.
Follow-up from an independent review of the --pitr-time flag, refined
by a second independent pass:

- --pitr-time is now parsed as RFC3339 and rejected before any network
  call for malformed or timezone-less input. The parsed value is not
  used for anything else -- the original string is still forwarded to
  the backend unchanged (it already normalizes offsets to UTC
  server-side; validating-then-forwarding-raw avoids two independent
  transforms that would need to agree on fractional-second handling).
- one-of/mutually-exclusive/RFC3339-format checks now run before the
  cluster lookup, since none of them need cluster info. The
  v1-PITR-rejection check stays after the lookup -- it needs
  cluster.Version.
- restore's help text and generic restore-failure error messages no
  longer say "backup" unconditionally, now that a restore target can
  be a backup, a point in time, or (via #5044) neither with a bare
  name override.
- tests: malformed/timezone-less --pitr-time rejected before any
  client call, a non-UTC-offset value forwarded unchanged (regression
  guard against re-introducing client-side normalization later), and
  name+pitr_time proven to coexist correctly in the v2 wire format.
@tripledoublev
tripledoublev force-pushed the vincent/mpg-restore-pitr-flag branch from 5a98028 to a75c74d Compare August 7, 2026 14:09
Pre-existing lint violation on master, unrelated to this branch's own
changes, surfaced by CI's full-repo lint pass. One-line fix: blank line
before the trailing return.
@tripledoublev
tripledoublev marked this pull request as ready for review August 7, 2026 18:30
Comment on lines 41 to +69
Shorthand: "n",
Description: "The name of the restored cluster (defaults to a generated name)",
},
flag.String{
Name: "pitr-time",
Description: "Restore to a specific point in time (RFC3339, e.g. 2026-06-01T12:00:00Z). Requires the cluster's PITR recovery window to cover this time. Mutually exclusive with --backup-id.",
},
)

return cmd
}

func runRestore(ctx context.Context) error {
clusterID := flag.FirstArg(ctx)
backupID := flag.GetString(ctx, "backup-id")
pitrTime := flag.GetString(ctx, "pitr-time")
name := flag.GetString(ctx, "name")
if backupID == "" && pitrTime == "" {
return fmt.Errorf("one of --backup-id or --pitr-time is required")
}
if backupID != "" && pitrTime != "" {
return fmt.Errorf("--backup-id and --pitr-time are mutually exclusive")
}
if pitrTime != "" {
if _, err := time.Parse(time.RFC3339, pitrTime); err != nil {
return fmt.Errorf("--pitr-time must be an RFC3339 timestamp with an explicit offset (e.g. 2026-06-01T12:00:00Z): %w", err)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You can use Cobra to check for flags and make sure they're mutually exclusive / required.
Maybe something like...

cmd.MarkFlagRequired("name")
cmd.MarkFlagsOneRequired("pitr-time", "backup")
cmd.MarkFlagsMutuallyExclusive("pitr-time","backup")

(don't remember the syntax, bad memory)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks! Looked into Cobra's flag-grouping helpers and noticed they're not used anywhere in the codebase yet, so this'd be introducing a new pattern rather than following an existing one.

I think this could be a nice follow-up PR though, maybe worth applying more broadly across mpg commands, rather than as a one-off here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

maybe worth applying more broadly across mpg commands

yes!

@tripledoublev tripledoublev changed the title feat(mpg): add --pitr-time to restore feat(mpg): add --pitr-time to restore Aug 7, 2026
@tripledoublev
tripledoublev merged commit 180de61 into master Aug 7, 2026
30 of 31 checks passed
@tripledoublev
tripledoublev deleted the vincent/mpg-restore-pitr-flag branch August 7, 2026 19:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants