Skip to content

fix: reject an empty --plugin-config and stage plugin restore directories - #48

Open
Bonartze wants to merge 3 commits into
mainfrom
fix/gprestore-empty-plugin-config
Open

fix: reject an empty --plugin-config and stage plugin restore directories#48
Bonartze wants to merge 3 commits into
mainfrom
fix/gprestore-empty-plugin-config

Conversation

@Bonartze

@Bonartze Bonartze commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Fixes the reported DD Boost restore failure, where gprestore stopped with

open .../gpbackup_<ts>_config.yaml: no such file or directory
Backup directories missing or inaccessible on 1 segment

after the simulation host loss by deleting $COORDINATOR_DATA_DIRECTORY/backups/YYYYMMDD
(and, separately, a segment's backups/<date>/<ts>). Both errors point at the local filesystem
rather than at the real problem, and the documented workaround was to NFS-mount the Data Domain
storage unit and copy the files back by hand.

Root cause

--plugin-config "$CFG" was passed with $CFG empty. utils.ValidateFullPath short-circuits on
len(path) > 0 (utils/util.go:129), so the empty value passes validation, and restore/restore.go:95
then reads "empty plugin config" as "no plugin at all" and restores from local disk with no warning.
The reporter has since confirmed this on the ticket. The same guard is used by gpbackup, where the
consequence is worse: the backup silently lands on segment local disk and reports success, which can
fill the segment hosts.

What changed

  • An explicitly-empty --plugin-config is now a config error, on every command that accepts the
    flag: gpbackup, gprestore, gpbackup delete-backup and gpbackup delete-backups-before. The
    two delete commands never run DoFlagValidation, so they call the shared
    ValidatePluginConfigFlag themselves after UseCmdFlags. An empty value there would have deleted
    only the local half of a backup set.
  • The plugin-combination check runs before any local-filesystem check.
    ValidateBackupFlagPluginCombinations (renamed from the unexported form) now runs at the top of
    BackupConfigurationValidation, ahead of the directory check. A plugin backup restored without
    --plugin-config reports "Backup was taken with plugin ... The --plugin-config flag must be used
    to restore"
    instead of a missing local directory. That function had one production caller, so this
    is a reordering, not new logic.
  • The restore logs which mode it is in — the plugin config path in use, or that no plugin is
    configured and files are expected on local disk under a named directory.
  • Local backup directories are created, not required, when a plugin is in use.
    VerifyBackupDirectoriesExistOnAllHosts becomes EnsureBackupDirectoriesExistOnAllHosts: with a
    plugin it mkdir -ps the coordinator and segment directories, since those only stage files
    gprestore downloads from the plugin's storage; without a plugin it keeps asserting them with
    test -d, because there a missing directory means the data really is gone. Segments are now covered
    for every plugin restore rather than only single-data-file ones — that combination previously
    skipped them entirely, which is harmless for a plain restore but leaves the staging directory
    missing for a --resize-cluster restore, whose helper writes its pipes, oid files and segment TOCs
    there.

Verification

Unit tests for each guard, each negative-checked (disabling the guard fails exactly the matching
spec), plus EnsureBackupDirectoriesExistOnAllHosts specs asserting the generated commands via the
cluster test executor.

Also run end to end on a live WHPG cluster with gpbackup's example plugin, which stores the backup
set outside the cluster in the same flat, host-independent layout the DD Boost plugin produces —
1.34.0-WHPG binaries against this branch:

Scenario 1.34.0-WHPG This branch
gprestore --plugin-config "", coordinator directory deleted open ..._config.yaml: no such file or directory — the reported error, verbatim The --plugin-config flag was specified with an empty value
gprestore --plugin-config "", segment directory deleted Backup directories missing or inaccessible on 1 segment — the second reported error, verbatim same clear message
gprestore with a valid config, segment directory deleted succeeds succeeds; 500/500 rows restored, staging directory recreated
gpbackup --plugin-config "" --leaf-partition-data Backup completed successfully, files written to segment local disk, nothing on plugin storage refused before anything is written

Worth stating plainly for reviewers: with a valid --plugin-config, 1.34.0 already recovered from
both deleted directories — the plugin recreates the coordinator files via MustRestoreFile, and the
segment check is skipped for non-single-data-file backups. So the reported ticket is fixed by the
validation and ordering changes; the directory-creation change is robustness — it closes the
--resize-cluster hole above and removes the test -d precondition for a rebuilt host.

…ly restoring from local disk

gprestore accepted `--plugin-config ""` and fell back to a local-filesystem
restore of a backup set that only exists on the plugin's storage.
utils.ValidateFullPath short-circuits on len(path) > 0, so the empty value passed
validation, and DoSetup treats an empty plugin config as "no plugin at all"
(restore/restore.go:95). The run then failed on the first backup file that is not
on local disk. Reported against DD Boost as

    open .../gpbackup_<ts>_config.yaml: no such file or directory
    Backup directories missing or inaccessible on 1 segment

both of which point at the local filesystem rather than at the dropped plugin,
and neither of which mentions that no plugin was in effect.

- Reject an explicitly-set-but-empty --plugin-config in gprestore
  (ValidatePluginConfigFlag, called from DoValidation) and in gpbackup
  (validateFlagValues), so the same mistake at backup time cannot silently
  produce a backup on local disk instead of on the plugin's storage.
- Run the plugin-combination check before any check that assumes the backup
  files are on local disk: ValidateBackupFlagPluginCombinations (renamed from
  the unexported validateBackupFlagPluginCombinations) now runs at the top of
  BackupConfigurationValidation, ahead of VerifyBackupDirectoriesExistOnAllHosts.
  A plugin backup restored without --plugin-config now reports "Backup was taken
  with plugin ... The --plugin-config flag must be used to restore." instead of a
  missing local directory. BackupConfigurationValidation was the only production
  caller, so this is a reordering, not a behavior change.
- Log at INFO whether a plugin is in effect and which config path is used.
…g them when a plugin is in use

A restore through a plugin failed if the local backup directory was missing on
the coordinator or on any segment, even though the backup itself lives on the
plugin's storage and those directories only ever hold files gprestore downloads
into them. A host rebuilt or replaced since the backup was taken therefore could
not be restored onto without first recreating the old directory tree by hand
(the reported workaround was to NFS-mount the Data Domain storage unit and copy
the files back).

VerifyBackupDirectoriesExistOnAllHosts is renamed to
EnsureBackupDirectoriesExistOnAllHosts and now branches on whether a plugin is in
effect:

- with a plugin, "mkdir -p" the coordinator and segment directories;
- without one, keep asserting them with "test -d", because there a missing
  directory means the backup data really is gone.

The segment directories are now handled for every plugin restore rather than only
for single-data-file ones. That combination previously skipped the segments
altogether, which is harmless for a plain restore (restoreDataFromTimestamp only
starts gpbackup_helper when SingleDataFile || resizeCluster, and otherwise the
segments read straight from the plugin via COPY FROM PROGRAM) but leaves the
staging directory missing for a resize restore, whose helper writes its pipes,
oid files and segment TOCs there.

Adds unit coverage for all three cases via the cluster test executor.
…s too

--plugin-config is registered on four flag sets: gpbackup, gprestore,
delete-backup and delete-backups-before. gpbackup and gprestore now reject an
explicitly-empty value, but the two delete commands never call DoFlagValidation,
so an empty value still reached setupDeletionTargets as "" and meant "no plugin".

Lifts the guard out of validateFlagValues into an exported
ValidatePluginConfigFlag and calls it from DoDeleteBackup and
DoDeleteBackupsBefore, after UseCmdFlags has pointed cmdFlags at the
subcommand's own flag set.

Reported on the ticket: a silent fallback to local storage is dangerous in both
directions -- a "backup" that is not on the appliance can fill the segment
hosts, and a delete that ignores the plugin removes only the local half of a
backup set.
@Bonartze Bonartze changed the title fix(restore): fail fast on an empty --plugin-config instead of silently restoring from local disk fix: reject an empty --plugin-config and stage plugin restore directories Sep 4, 2026
@Bonartze
Bonartze requested a review from a team September 4, 2026 12:24
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.

1 participant