diff --git a/Dockerfile b/Dockerfile index 85b327c7..c7bf7f47 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,8 @@ RUN CGO_ENABLED=0 GOOS=linux go build -v -o /fly/bin/failover_validation ./cmd/f RUN CGO_ENABLED=0 GOOS=linux go build -v -o /fly/bin/pg_unregister ./cmd/pg_unregister RUN CGO_ENABLED=0 GOOS=linux go build -v -o /fly/bin/start_monitor ./cmd/monitor RUN CGO_ENABLED=0 GOOS=linux go build -v -o /fly/bin/start_admin_server ./cmd/admin_server +RUN CGO_ENABLED=0 GOOS=linux go build -v -o /fly/bin/flexctl ./cmd/flexctl + RUN CGO_ENABLED=0 GOOS=linux go build -v -o /fly/bin/start ./cmd/start COPY ./bin/* /fly/bin/ @@ -20,6 +22,8 @@ FROM wrouesnel/postgres_exporter:latest AS postgres_exporter FROM postgres:${PG_VERSION} ENV PGDATA=/data/postgresql ENV PGPASSFILE=/data/.pgpass +ENV AWS_SHARED_CREDENTIALS_FILE=/data/.aws/credentials + ARG VERSION ARG PG_MAJOR_VERSION ARG POSTGIS_MAJOR=3 diff --git a/Dockerfile-timescaledb b/Dockerfile-timescaledb index 16e79b48..a9e3fa7a 100644 --- a/Dockerfile-timescaledb +++ b/Dockerfile-timescaledb @@ -12,6 +12,8 @@ RUN CGO_ENABLED=0 GOOS=linux go build -v -o /fly/bin/failover_validation ./cmd/f RUN CGO_ENABLED=0 GOOS=linux go build -v -o /fly/bin/pg_unregister ./cmd/pg_unregister RUN CGO_ENABLED=0 GOOS=linux go build -v -o /fly/bin/start_monitor ./cmd/monitor RUN CGO_ENABLED=0 GOOS=linux go build -v -o /fly/bin/start_admin_server ./cmd/admin_server +RUN CGO_ENABLED=0 GOOS=linux go build -v -o /fly/bin/flexctl ./cmd/flexctl + RUN CGO_ENABLED=0 GOOS=linux go build -v -o /fly/bin/start ./cmd/start COPY ./bin/* /fly/bin/ @@ -21,6 +23,8 @@ FROM wrouesnel/postgres_exporter:latest AS postgres_exporter FROM postgres:${PG_VERSION} ENV PGDATA=/data/postgresql ENV PGPASSFILE=/data/.pgpass +ENV AWS_SHARED_CREDENTIALS_FILE=/data/.aws/credentials + ARG VERSION ARG PG_MAJOR_VERSION ARG POSTGIS_MAJOR=3 diff --git a/cmd/flexctl/backups.go b/cmd/flexctl/backups.go new file mode 100644 index 00000000..e7724b51 --- /dev/null +++ b/cmd/flexctl/backups.go @@ -0,0 +1,226 @@ +package main + +import ( + "context" + "fmt" + "os" + "time" + + "github.com/fly-apps/postgres-flex/internal/flypg" + "github.com/fly-apps/postgres-flex/internal/flypg/state" + "github.com/olekukonko/tablewriter" + "github.com/spf13/cobra" +) + +var backupListCmd = &cobra.Command{ + Use: "list", + Short: "Lists all backups", + Long: `Lists all available backups created.`, + RunE: func(cmd *cobra.Command, args []string) error { + if !backupsEnabled() { + return fmt.Errorf("backups are not enabled") + } + + return listBackups(cmd) + }, + Args: cobra.NoArgs, +} + +var backupCreateCmd = &cobra.Command{ + Use: "create", + Short: "Creates a new backup", + Long: `Creates a new backup.`, + RunE: func(cmd *cobra.Command, args []string) error { + if !backupsEnabled() { + return fmt.Errorf("backups are not enabled") + } + + if err := createBackup(cmd); err != nil { + return err + } + + fmt.Println("Backup completed successfully!") + + return nil + }, + Args: cobra.NoArgs, +} + +var backupShowCmd = &cobra.Command{ + Use: "show", + Short: "Shows details about a specific backup", + Long: `Shows details about a specific backup.`, + RunE: func(cmd *cobra.Command, args []string) error { + if !backupsEnabled() { + return fmt.Errorf("backups are not enabled") + } + return showBackup(cmd, args) + }, + Args: cobra.ExactArgs(1), +} + +func showBackup(cmd *cobra.Command, args []string) error { + id := args[0] + + ctx, cancel := context.WithTimeout(cmd.Context(), 10*time.Second) + defer cancel() + + store, err := state.NewStore() + if err != nil { + return fmt.Errorf("failed to initialize store: %v", err) + } + + barman, err := flypg.NewBarman(store, os.Getenv("S3_ARCHIVE_CONFIG"), flypg.DefaultAuthProfile) + if err != nil { + return fmt.Errorf("failed to initialize barman: %v", err) + } + + backupDetails, err := barman.ShowBackup(ctx, id) + if err != nil { + return fmt.Errorf("failed to get backup details: %v", err) + } + + fmt.Println(string(backupDetails)) + + return nil +} + +func createBackup(cmd *cobra.Command) error { + ctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Minute) + defer cancel() + + n, err := flypg.NewNode() + if err != nil { + return fmt.Errorf("failed to initialize node: %v", err) + } + + conn, err := n.RepMgr.NewLocalConnection(ctx) + if err != nil { + return fmt.Errorf("failed to connect to local db: %v", err) + } + defer func() { _ = conn.Close(ctx) }() + + isPrimary, err := n.RepMgr.IsPrimary(ctx, conn) + if err != nil { + return fmt.Errorf("failed to determine if node is primary: %v", err) + } + + if !isPrimary { + return fmt.Errorf("backups can only be performed against the primary node") + } + + store, err := state.NewStore() + if err != nil { + return fmt.Errorf("failed to initialize store: %v", err) + } + + barman, err := flypg.NewBarman(store, os.Getenv("S3_ARCHIVE_CONFIG"), flypg.DefaultAuthProfile) + if err != nil { + return fmt.Errorf("failed to initialize barman: %v", err) + } + + name, err := cmd.Flags().GetString("name") + if err != nil { + return fmt.Errorf("failed to get name flag: %v", err) + } + + immediateCheckpoint, err := cmd.Flags().GetBool("immediate-checkpoint") + if err != nil { + return fmt.Errorf("failed to get immediate-checkpoint flag: %v", err) + } + + cfg := flypg.BackupConfig{ + ImmediateCheckpoint: immediateCheckpoint, + Name: name, + } + + fmt.Println("Performing backup...") + + if _, err := barman.Backup(ctx, cfg); err != nil { + return fmt.Errorf("failed to create backup: %v", err) + } + + return nil +} + +func listBackups(cmd *cobra.Command) error { + ctx, cancel := context.WithTimeout(cmd.Context(), 10*time.Second) + defer cancel() + + store, err := state.NewStore() + if err != nil { + return fmt.Errorf("failed to initialize store: %v", err) + } + + barman, err := flypg.NewBarman(store, os.Getenv("S3_ARCHIVE_CONFIG"), flypg.DefaultAuthProfile) + if err != nil { + return fmt.Errorf("failed to initialize barman: %v", err) + } + + isJSON, err := cmd.Flags().GetBool("json") + if err != nil { + return fmt.Errorf("failed to get json flag: %v", err) + } + + if isJSON { + jsonBytes, err := barman.ListRawBackups(cmd.Context()) + if err != nil { + return fmt.Errorf("failed to list backups: %v", err) + } + + fmt.Println(string(jsonBytes)) + return nil + } + + backupList, err := barman.ListBackups(ctx) + if err != nil { + return fmt.Errorf("failed to list backups: %v", err) + } + + if len(backupList.Backups) == 0 { + fmt.Println("No backups found") + return nil + } + + var filterStatus string + + filterStatus, err = cmd.Flags().GetString("status") + if err != nil { + return fmt.Errorf("failed to get status flag: %v", err) + } + + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"ID", "Name", "Status", "End time", "Begin WAL"}) + + // Set table alignment, borders, padding, etc. as needed + table.SetAlignment(tablewriter.ALIGN_LEFT) + table.SetBorder(true) // Set to false to hide borders + table.SetCenterSeparator("|") + table.SetColumnSeparator("|") + table.SetRowSeparator("-") + table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) + table.SetHeaderLine(true) // Enable header line + table.SetAutoWrapText(false) + + for _, b := range backupList.Backups { + if filterStatus != "" && b.Status != filterStatus { + continue + } + + table.Append([]string{ + b.ID, + b.Name, + b.Status, + b.EndTime, + b.BeginWal, + }) + } + + table.Render() + + return nil +} + +func backupsEnabled() bool { + return os.Getenv("S3_ARCHIVE_CONFIG") != "" +} diff --git a/cmd/flexctl/main.go b/cmd/flexctl/main.go new file mode 100644 index 00000000..262d4c1e --- /dev/null +++ b/cmd/flexctl/main.go @@ -0,0 +1,36 @@ +package main + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" +) + +func main() { + var rootCmd = &cobra.Command{Use: "flexctl"} + + // Backup commands + var backupCmd = &cobra.Command{Use: "backup"} + + rootCmd.AddCommand(backupCmd) + backupCmd.AddCommand(backupListCmd) + backupCmd.AddCommand(backupCreateCmd) + backupCmd.AddCommand(backupShowCmd) + + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func init() { + // Backup commands + backupListCmd.Flags().StringP("status", "s", "", "Filter backups by status (Not applicable for JSON output)") + backupListCmd.Flags().BoolP("json", "", false, "Output in JSON format") + + backupShowCmd.Flags().BoolP("json", "", false, "Output in JSON format") + + backupCreateCmd.Flags().StringP("name", "n", "", "Name of the backup") + backupCreateCmd.Flags().BoolP("immediate-checkpoint", "", false, "Forces Postgres to perform an immediate checkpoint") +} diff --git a/cmd/monitor/monitor_backup_schedule.go b/cmd/monitor/monitor_backup_schedule.go index e75df4d2..fe9d1262 100644 --- a/cmd/monitor/monitor_backup_schedule.go +++ b/cmd/monitor/monitor_backup_schedule.go @@ -149,7 +149,8 @@ func performBaseBackup(ctx context.Context, barman *flypg.Barman, immediateCheck case <-ctx.Done(): return ctx.Err() default: - if _, err := barman.Backup(ctx, immediateCheckpoint); err != nil { + cfg := flypg.BackupConfig{ImmediateCheckpoint: immediateCheckpoint} + if _, err := barman.Backup(ctx, cfg); err != nil { log.Printf("[WARN] Failed to perform full backup: %s. Retrying in 30 seconds.", err) // If we've exceeded the maximum number of retries, we should return an error. diff --git a/cmd/start/main.go b/cmd/start/main.go index e9b7005f..b3cb3fbf 100644 --- a/cmd/start/main.go +++ b/cmd/start/main.go @@ -55,14 +55,6 @@ func main() { return } - // TODO - Find a better way to handle this - if os.Getenv("S3_ARCHIVE_CONFIG") != "" || os.Getenv("S3_ARCHIVE_REMOTE_RESTORE_CONFIG") != "" { - if err := os.Setenv("AWS_SHARED_CREDENTIALS_FILE", "/data/.aws/credentials"); err != nil { - panicHandler(err) - return - } - } - node, err := flypg.NewNode() if err != nil { panicHandler(err) diff --git a/go.mod b/go.mod index 188240e6..b1b1e9fb 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/serf v0.10.1 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect @@ -32,8 +33,12 @@ require ( github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/mattn/go-colorable v0.1.6 // indirect github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/olekukonko/tablewriter v0.0.5 // indirect + github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.0 // indirect golang.org/x/crypto v0.20.0 // indirect golang.org/x/sys v0.17.0 // indirect diff --git a/go.sum b/go.sum index 612cc9ba..a095412d 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,7 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -58,6 +59,8 @@ github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= @@ -90,6 +93,8 @@ github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcME github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= @@ -101,6 +106,8 @@ github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUb github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -112,10 +119,15 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= diff --git a/internal/flypg/barman.go b/internal/flypg/barman.go index 176a3959..6d2766be 100644 --- a/internal/flypg/barman.go +++ b/internal/flypg/barman.go @@ -34,8 +34,9 @@ type Barman struct { } type Backup struct { + ID string `json:"backup_id"` + Name string `json:"backup_name"` Status string `json:"status"` - BackupID string `json:"backup_id"` StartTime string `json:"begin_time"` EndTime string `json:"end_time"` BeginWal string `json:"begin_wal"` @@ -105,9 +106,12 @@ func (b *Barman) BucketURL() string { return fmt.Sprintf("s3://%s", b.bucket) } -// Backup performs a base backup of the database. -// immediateCheckpoint - forces the initial checkpoint to be done as quickly as possible. -func (b *Barman) Backup(ctx context.Context, immediateCheckpoint bool) ([]byte, error) { +type BackupConfig struct { + Name string // A customized name for the backup. + ImmediateCheckpoint bool // Force an immediate checkpoint. +} + +func (b *Barman) Backup(ctx context.Context, cfg BackupConfig) ([]byte, error) { args := []string{ "--cloud-provider", providerDefault, "--endpoint-url", b.endpoint, @@ -118,10 +122,14 @@ func (b *Barman) Backup(ctx context.Context, immediateCheckpoint bool) ([]byte, b.bucketDirectory, } - if immediateCheckpoint { + if cfg.ImmediateCheckpoint { args = append(args, "--immediate-checkpoint") } + if cfg.Name != "" { + args = append(args, "-n", cfg.Name) + } + return utils.RunCmd(ctx, "postgres", "barman-cloud-backup", args...) } @@ -158,6 +166,33 @@ func (b *Barman) ListBackups(ctx context.Context) (BackupList, error) { return b.parseBackups(backupsBytes) } +// ListRawBackups returns the raw output of the backups list command. +func (b *Barman) ListRawBackups(ctx context.Context) ([]byte, error) { + args := []string{ + "--cloud-provider", providerDefault, + "--endpoint-url", b.endpoint, + "--profile", b.authProfile, + "--format", "json", + b.BucketURL(), + b.bucketDirectory, + } + + return utils.RunCmd(ctx, "postgres", "barman-cloud-backup-list", args...) +} + +func (b *Barman) ShowBackup(ctx context.Context, id string) ([]byte, error) { + args := []string{ + "--cloud-provider", providerDefault, + "--endpoint-url", b.endpoint, + "--profile", b.authProfile, + b.BucketURL(), + b.bucketDirectory, + id, + } + + return utils.RunCmd(ctx, "postgres", "barman-cloud-backup-show", args...) +} + func (b *Barman) WALArchiveDelete(ctx context.Context) ([]byte, error) { args := []string{ "--cloud-provider", providerDefault, diff --git a/internal/flypg/barman_restore.go b/internal/flypg/barman_restore.go index 0bd05cce..dec237a8 100644 --- a/internal/flypg/barman_restore.go +++ b/internal/flypg/barman_restore.go @@ -198,8 +198,8 @@ func (*BarmanRestore) resolveBackupFromID(backupList BackupList, id string) (str } for _, backup := range backupList.Backups { - if backup.BackupID == id { - return backup.BackupID, nil + if backup.ID == id { + return backup.ID, nil } } @@ -237,7 +237,7 @@ func (*BarmanRestore) resolveBackupFromTime(backupList BackupList, restoreStr st // If the last backup ID is empty or the restore time is after the last backup time, update the last backup ID. if lastBackupID == "" || restoreTime.After(endTime) { - lastBackupID = backup.BackupID + lastBackupID = backup.ID lastBackupTime = endTime } diff --git a/internal/flypg/barman_restore_test.go b/internal/flypg/barman_restore_test.go index 45978565..f5629911 100644 --- a/internal/flypg/barman_restore_test.go +++ b/internal/flypg/barman_restore_test.go @@ -278,8 +278,8 @@ func TestParseBackups(t *testing.T) { } firstBackup := list.Backups[0] - if firstBackup.BackupID != "20240702T210544" { - t.Fatalf("expected backup ID to be 20240625T194412, got %s", firstBackup.BackupID) + if firstBackup.ID != "20240702T210544" { + t.Fatalf("expected backup ID to be 20240625T194412, got %s", firstBackup.ID) } if firstBackup.StartTime != "Tue Jun 24 19:44:20 2024" { @@ -296,8 +296,8 @@ func TestParseBackups(t *testing.T) { secondBackup := list.Backups[2] - if secondBackup.BackupID != "20240626T172443" { - t.Fatalf("expected backup ID to be 20240626T172443, got %s", secondBackup.BackupID) + if secondBackup.ID != "20240626T172443" { + t.Fatalf("expected backup ID to be 20240626T172443, got %s", secondBackup.ID) } if secondBackup.StartTime != "Wed Jun 26 17:24:43 2024" {