-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Adding flexctl * Cleanup * Backup name is now listed correctly * cleanup * whoops
- Loading branch information
Showing
11 changed files
with
336 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") != "" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.