Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add backup config management to flexctl #244

Merged
merged 16 commits into from
Jul 18, 2024
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
185 changes: 185 additions & 0 deletions cmd/flexctl/backups.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package main

import (
"bytes"
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"time"

Expand Down Expand Up @@ -224,3 +227,185 @@ func listBackups(cmd *cobra.Command) error {
func backupsEnabled() bool {
return os.Getenv("S3_ARCHIVE_CONFIG") != ""
}

func newBackupConfig() *cobra.Command {
var cmd = &cobra.Command{
Use: "config",
Short: "Manage backup configuration",
}

cmd.AddCommand(newConfigShow(), newConfigUpdate())

return cmd
}

type configShowResult struct {
Result flypg.BarmanSettings `json:"result"`
}

func getAppName() (string, error) {
name := os.Getenv("FLY_APP_NAME")
if name == "" {
return "", fmt.Errorf("FLY_APP_NAME is not set")
}
return name, nil
}

func getApiUrl() (string, error) {
hostname, err := getAppName()
if err != nil {
return "", err
}
url := fmt.Sprintf("http://%s.internal:5500", hostname)
return url, nil
}

func newConfigShow() *cobra.Command {
var cmd = &cobra.Command{
Use: "show",
Short: "Show current configuration",
RunE: func(cmd *cobra.Command, args []string) error {
if !backupsEnabled() {
return fmt.Errorf("backups are not enabled")
}

url, err := getApiUrl()
if err != nil {
return err
}

url = fmt.Sprintf("%s/commands/admin/settings/view/barman", url)
resp, err := http.Get(url)
if err != nil {
return err
}

var rv configShowResult
if err := json.NewDecoder(resp.Body).Decode(&rv); err != nil {
return err
}

fmt.Printf(" ArchiveTimeout = %s\n", rv.Result.ArchiveTimeout)
Copy link
Contributor

Choose a reason for hiding this comment

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

To future proof this, we could consider just pretty printing the json output. However, I think this is totally fine for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I went back and forth on this but came to the same conclusion. If we make it more unwieldy, I'm happy to switch it later, or if you think we should do it now then I can.

fmt.Printf(" RecoveryWindow = %s\n", rv.Result.RecoveryWindow)
fmt.Printf(" FullBackupFrequency = %s\n", rv.Result.FullBackupFrequency)
fmt.Printf(" MinimumRedundancy = %s\n", rv.Result.MinimumRedundancy)

return nil
},
}

return cmd
}

type successfulUpdateResult struct {
Message string `json:"message,omitempty"`
RestartRequired bool `json:"restart_required"`
}

type configUpdateResult struct {
Result successfulUpdateResult `json:"result,omitempty"`
Error string `json:"error,omitempty"`
}

func newConfigUpdate() *cobra.Command {
var cmd = &cobra.Command{
Use: "update",
Short: "Update configuration",
}

cmd.RunE = func(cmd *cobra.Command, args []string) error {
if !backupsEnabled() {
return fmt.Errorf("backups are not enabled")
}

archiveTimeout, err := cmd.Flags().GetString("archive-timeout")
if err != nil {
return err
}

recoveryWindow, err := cmd.Flags().GetString("recovery-window")
if err != nil {
return err
}

fullBackupFrequency, err := cmd.Flags().GetString("full-backup-frequency")
if err != nil {
return err
}

minimumRedundancy, err := cmd.Flags().GetString("minimum-redundancy")
if err != nil {
return err
}

update := flypg.BarmanSettings{
ArchiveTimeout: archiveTimeout,
RecoveryWindow: recoveryWindow,
FullBackupFrequency: fullBackupFrequency,
MinimumRedundancy: minimumRedundancy,
}

jsonBody, err := json.Marshal(update)
if err != nil {
return err
}

url, err := getApiUrl()
if err != nil {
return err
}

url = fmt.Sprintf("%s/commands/admin/settings/update/barman", url)
resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonBody))
if err != nil {
return err
}

var rv configUpdateResult
if err := json.NewDecoder(resp.Body).Decode(&rv); err != nil {
return err
}

if rv.Error != "" {
return fmt.Errorf("error updating configuration: %s", rv.Error)
}

if rv.Result.Message != "" {
fmt.Println(rv.Result.Message)
}

if rv.Result.RestartRequired {
appName, err := getAppName()
if err != nil {
return err
}
fmt.Printf("A restart is required for these changes to take effect. Run `fly pg restart -a %s` to restart.)\n", appName)
}

return nil
}

cmd.Flags().StringP("archive-timeout", "", "", "Archive timeout")
cmd.Flags().StringP("recovery-window", "", "", "Recovery window")
cmd.Flags().StringP("full-backup-frequency", "", "", "Full backup frequency")
cmd.Flags().StringP("minimum-redundancy", "", "", "Minimum redundancy")

cmd.PreRunE = func(cmd *cobra.Command, args []string) error {
requiredFlags := []string{"archive-timeout", "recovery-window", "full-backup-frequency", "minimum-redundancy"}
providedFlags := 0

for _, flag := range requiredFlags {
if cmd.Flag(flag).Changed {
providedFlags++
}
}

if providedFlags < 1 {
return fmt.Errorf("at least one flag must be specified")
}

return nil
}

return cmd
}
1 change: 1 addition & 0 deletions cmd/flexctl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func main() {
backupCmd.AddCommand(backupListCmd)
backupCmd.AddCommand(backupShowCmd)
backupCmd.AddCommand(backupCreateCmd)
backupCmd.AddCommand(newBackupConfig())

if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
Expand Down
8 changes: 4 additions & 4 deletions internal/flypg/barman_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import (
)

type BarmanSettings struct {
ArchiveTimeout string
RecoveryWindow string
FullBackupFrequency string
MinimumRedundancy string
ArchiveTimeout string `json:"archive_timeout,omitempty"`
RecoveryWindow string `json:"recovery_window,omitempty"`
FullBackupFrequency string `json:"full_backup_frequency,omitempty"`
MinimumRedundancy string `json:"minimum_redundancy,omitempty"`
}

type BarmanConfig struct {
Expand Down
1 change: 1 addition & 0 deletions internal/supervisor/ensure_kill_linux.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:build linux
ndarilek marked this conversation as resolved.
Show resolved Hide resolved
// +build linux

package supervisor
Expand Down
Loading