Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 6 additions & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ type options struct {
Decrypt bool
WithRolePasswords bool
DumpOnly bool
UniformTimestamp bool

Upload string // values are none, b2, s3, sftp, gcs
UploadPrefix string
Expand Down Expand Up @@ -300,6 +301,7 @@ func parseCli(args []string) (options, []string, error) {
pflag.StringVarP(&purgeKeep, "purge-min-keep", "K", "0", "minimum number of dumps to keep when purging or 'all' to keep\neverything")
pflag.StringVar(&opts.PreHook, "pre-backup-hook", "", "command to run before taking dumps")
pflag.StringVar(&opts.PostHook, "post-backup-hook", "", "command to run after taking dumps\n")
pflag.BoolVar(&opts.UniformTimestamp, "uniform-timestamp", false, "Use the same timestamp for all pg_back files instead of individual creation times")

pflag.BoolVar(&opts.Encrypt, "encrypt", false, "encrypt the dumps")
NoEncrypt := pflag.Bool("no-encrypt", false, "do not encrypt the dumps")
Expand Down Expand Up @@ -559,7 +561,7 @@ func validateConfigurationFile(cfg *ini.File) error {
"sftp_port", "sftp_user", "sftp_password", "sftp_directory", "sftp_identity",
"sftp_ignore_hostkey", "gcs_bucket", "gcs_endpoint", "gcs_keyfile",
"azure_container", "azure_account", "azure_key", "azure_endpoint", "pg_dump_options",
"dump_role_passwords", "dump_only", "upload_prefix", "delete_uploaded",
"dump_role_passwords", "dump_only", "upload_prefix", "delete_uploaded", "uniform_timestamp",
}

gkLoop:
Expand Down Expand Up @@ -653,6 +655,7 @@ func loadConfigurationFile(path string) (options, error) {
opts.CipherPublicKey = s.Key("cipher_public_key").MustString("")
opts.CipherPrivateKey = s.Key("cipher_private_key").MustString("")
opts.EncryptKeepSrc = s.Key("encrypt_keep_source").MustBool(false)
opts.UniformTimestamp = s.Key("uniform_timestamp").MustBool(false)

opts.Upload = s.Key("upload").MustString("none")
opts.UploadPrefix = s.Key("upload_prefix").MustString("")
Expand Down Expand Up @@ -988,6 +991,8 @@ func mergeCliAndConfigOptions(cliOpts options, configOpts options, onCli []strin
opts.Username = cliOpts.Username
case "dbname":
opts.ConnDb = cliOpts.ConnDb
case "uniform-timestamp":
opts.UniformTimestamp = cliOpts.UniformTimestamp
}
}

Expand Down
39 changes: 27 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,12 @@ func run() (retVal error) {
}
defer db.Close()

// Generate a single datetime that will be used in all files generated by pg_back
var uniformTimestamp time.Time
if opts.UniformTimestamp {
uniformTimestamp = time.Now()
}

if !opts.DumpOnly {
if !db.superuser {
l.Infoln("connection user is not superuser, some information will not be dumped")
Expand All @@ -316,7 +322,7 @@ func run() (retVal error) {
} else {
l.Infoln("dumping globals without role passwords")
}
if err := dumpGlobals(opts.Directory, opts.Mode, opts.TimeFormat, dumpRolePasswords, conninfo, producedFiles); err != nil {
if err := dumpGlobals(opts.Directory, opts.Mode, opts.TimeFormat, dumpRolePasswords, conninfo, producedFiles, uniformTimestamp); err != nil {
return fmt.Errorf("pg_dumpall of globals failed: %w", err)
}

Expand All @@ -326,15 +332,15 @@ func run() (retVal error) {
perr *pgPrivError
)

if err := dumpSettings(opts.Directory, opts.Mode, opts.TimeFormat, db, producedFiles); err != nil {
if err := dumpSettings(opts.Directory, opts.Mode, opts.TimeFormat, db, producedFiles, uniformTimestamp); err != nil {
if errors.As(err, &verr) || errors.As(err, &perr) {
l.Warnln(err)
} else {
return fmt.Errorf("could not dump configuration parameters: %w", err)
}
}

if err := dumpConfigFiles(opts.Directory, opts.Mode, opts.TimeFormat, db, producedFiles); err != nil {
if err := dumpConfigFiles(opts.Directory, opts.Mode, opts.TimeFormat, db, producedFiles, uniformTimestamp); err != nil {
return fmt.Errorf("could not dump configuration files: %w", err)
}
}
Expand Down Expand Up @@ -386,6 +392,7 @@ func run() (retVal error) {
CipherPassphrase: passphrase,
CipherPublicKey: publicKey,
EncryptKeepSrc: opts.EncryptKeepSrc,
When: uniformTimestamp,
ExitCode: -1,
PgDumpVersion: pgDumpVersion,
}
Expand Down Expand Up @@ -613,8 +620,6 @@ func (d *dump) dump(fc chan<- sumFileJob) error {
return fmt.Errorf("could not acquire lock for %s", dbname)
}

d.When = time.Now()

var fileEnd string
switch d.Options.Format {
case 'p':
Expand All @@ -631,6 +636,10 @@ func (d *dump) dump(fc chan<- sumFileJob) error {
fileEnd = "d"
}

if d.When.IsZero() {
d.When = time.Now()
}

file := formatDumpPath(d.Directory, d.TimeFormat, fileEnd, dbname, d.When, d.Options.CompressLevel)
formatOpt := fmt.Sprintf("-F%c", d.Options.Format)

Expand Down Expand Up @@ -935,7 +944,7 @@ func pgToolVersion(tool string) int {
return numver
}

func dumpGlobals(dir string, mode int, timeFormat string, withRolePasswords bool, conninfo *ConnInfo, fc chan<- sumFileJob) error {
func dumpGlobals(dir string, mode int, timeFormat string, withRolePasswords bool, conninfo *ConnInfo, fc chan<- sumFileJob, uniformTimestamp time.Time) error {
command := execPath("pg_dumpall")
args := []string{"-g", "-w"}

Expand Down Expand Up @@ -967,7 +976,11 @@ func dumpGlobals(dir string, mode int, timeFormat string, withRolePasswords bool
args = append(args, "--no-role-passwords")
}

file := formatDumpPath(dir, timeFormat, "sql", "pg_globals", time.Now(), 0)
if uniformTimestamp.IsZero() {
uniformTimestamp = time.Now()
}

file := formatDumpPath(dir, timeFormat, "sql", "pg_globals", uniformTimestamp, 0)
args = append(args, "-f", file)

if err := os.MkdirAll(filepath.Dir(file), 0700); err != nil {
Expand Down Expand Up @@ -1008,9 +1021,8 @@ func dumpGlobals(dir string, mode int, timeFormat string, withRolePasswords bool
return nil
}

func dumpSettings(dir string, mode int, timeFormat string, db *pg, fc chan<- sumFileJob) error {

file := formatDumpPath(dir, timeFormat, "out", "pg_settings", time.Now(), 0)
func dumpSettings(dir string, mode int, timeFormat string, db *pg, fc chan<- sumFileJob, uniformTimestamp time.Time) error {
file := formatDumpPath(dir, timeFormat, "out", "pg_settings", uniformTimestamp, 0)

if err := os.MkdirAll(filepath.Dir(file), 0o700); err != nil {
return err
Expand Down Expand Up @@ -1044,9 +1056,12 @@ func dumpSettings(dir string, mode int, timeFormat string, db *pg, fc chan<- sum
return nil
}

func dumpConfigFiles(dir string, mode int, timeFormat string, db *pg, fc chan<- sumFileJob) error {
func dumpConfigFiles(dir string, mode int, timeFormat string, db *pg, fc chan<- sumFileJob, uniformTimestamp time.Time) error {
for _, param := range []string{"hba_file", "ident_file"} {
file := formatDumpPath(dir, timeFormat, "out", param, time.Now(), 0)
if uniformTimestamp.IsZero() {
uniformTimestamp = time.Now()
}
file := formatDumpPath(dir, timeFormat, "out", param, uniformTimestamp, 0)

if err := os.MkdirAll(filepath.Dir(file), 0700); err != nil {
return err
Expand Down
4 changes: 4 additions & 0 deletions pg_back.conf
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ with_templates = false
# Dump only databases, excluding configuration and globals
dump_only = false

# Apply the same consistent timestamp to all filenames generated
# by pg_back instead of using individual file creation times
uniform_timestamp = false

# Format of the dump, understood by pg_dump. Possible values are
# plain, custom, tar or directory.
format = custom
Expand Down