-
Notifications
You must be signed in to change notification settings - Fork 76
Start supporting barman #202
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
Merged
Changes from all commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
60ac465
Start supporting barman
lubien 51d390f
Remove deprecated fns
lubien 250a911
Delete unused
lubien debbc9e
Fix home
lubien b6cdc1c
Fix pgpass issues
lubien 5f247fe
Try trigger switch-wal and cron on startup
lubien 594dd4a
Make ssh just work with barman
lubien 2331a59
Use base image instead of barman
lubien 3e9cb28
Add health checks to barman
lubien 7c95cc7
Missing var oopsie
lubien 07d3a15
Oopsie command
lubien 9501aea
We need to start admin to get checks
lubien cb799a3
barman repmgr settings
lubien b507732
Ignore repmgr for now
lubien 67e5614
Pg should also be able to root ssh when needed
lubien 6f77df6
No early return
lubien 803e573
Consistency
lubien 33bbff5
Comment
lubien 21c32be
Mininmal pg_hba needed
lubien dfad0bd
Lint
lubien 03ee142
Fix defer
lubien 441c2ae
No capital error
lubien 7f883f2
Ignore ctx
lubien 8ffdf26
Simplify settings
lubien 1245efa
Simplify role check
lubien 6e95709
Add some tests
lubien ae3b856
Fix second .pgpass
lubien ae2bd87
Timescaledb too
lubien File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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,212 @@ | ||
| package flybarman | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "log" | ||
| "os" | ||
| "os/exec" | ||
|
|
||
| "github.com/fly-apps/postgres-flex/internal/flypg" | ||
| "github.com/fly-apps/postgres-flex/internal/flypg/admin" | ||
| ) | ||
|
|
||
| var ( | ||
| dataDir = "/data" | ||
| barmanConfigFile = dataDir + "/barman.conf" | ||
| barmanCronFile = dataDir + "/barman.cron" | ||
| globalBarmanConfigFile = "/etc/barman.conf" | ||
| barmanHome = dataDir + "/barman.d" | ||
| logFile = dataDir + "/barman.log" | ||
| passwordConfigPath = "/root/.pgpass" | ||
| rootPasswordConfigPath = "/.pgpass" | ||
| ) | ||
|
|
||
| type Node struct { | ||
| AppName string | ||
| PrivateIP string | ||
| PrimaryRegion string | ||
| DataDir string | ||
| Port int | ||
|
|
||
| BarmanConfigFile string | ||
| BarmanCronFile string | ||
| GlobalBarmanConfigFile string | ||
| BarmanHome string | ||
| LogFile string | ||
| PasswordConfigPath string | ||
| RootPasswordConfigPath string | ||
|
|
||
| SUCredentials admin.Credential | ||
| OperatorCredentials admin.Credential | ||
| ReplCredentials admin.Credential | ||
| } | ||
|
|
||
| func NewNode() (*Node, error) { | ||
| node := &Node{ | ||
| AppName: "local", | ||
| BarmanConfigFile: barmanConfigFile, | ||
| BarmanCronFile: barmanCronFile, | ||
| GlobalBarmanConfigFile: globalBarmanConfigFile, | ||
| BarmanHome: barmanHome, | ||
| LogFile: logFile, | ||
| PasswordConfigPath: passwordConfigPath, | ||
| RootPasswordConfigPath: rootPasswordConfigPath, | ||
| } | ||
|
|
||
| if appName := os.Getenv("FLY_APP_NAME"); appName != "" { | ||
| node.AppName = appName | ||
| } | ||
|
|
||
| // Internal user | ||
| node.SUCredentials = admin.Credential{ | ||
| Username: "flypgadmin", | ||
| Password: os.Getenv("SU_PASSWORD"), | ||
| } | ||
|
|
||
| // Postgres user | ||
| node.OperatorCredentials = admin.Credential{ | ||
| Username: "postgres", | ||
| Password: os.Getenv("OPERATOR_PASSWORD"), | ||
| } | ||
|
|
||
| // Repmgr user | ||
| node.ReplCredentials = admin.Credential{ | ||
| Username: "repmgr", | ||
| Password: os.Getenv("REPL_PASSWORD"), | ||
| } | ||
|
|
||
| return node, nil | ||
| } | ||
|
|
||
| func (n *Node) Init(_ context.Context) error { | ||
| if os.Getenv("UNIT_TESTING") == "" { | ||
| err := flypg.WriteSSHKey() | ||
| if err != nil { | ||
| return fmt.Errorf("failed write ssh keys: %s", err) | ||
| } | ||
| } | ||
|
|
||
| if _, err := os.Stat(n.BarmanConfigFile); os.IsNotExist(err) { | ||
| barmanConfigFileContent := fmt.Sprintf(`[barman] | ||
| barman_user = root | ||
| barman_home = /data/barman.d | ||
| log_level = info | ||
| log_file = /data/barman.log | ||
| [pg] | ||
| description = "Fly.io Postgres Cluster" | ||
| conninfo = host=%s.internal user=repmgr dbname=postgres | ||
| streaming_conninfo = host=%s.internal user=repmgr dbname=postgres | ||
| backup_method = postgres | ||
| streaming_archiver = on | ||
| slot_name = barman | ||
| create_slot = auto | ||
| retention_policy_mode = auto | ||
| retention_policy = RECOVERY WINDOW OF 7 days | ||
| wal_retention_policy = main | ||
| `, n.AppName, n.AppName) | ||
|
|
||
| if err := os.WriteFile(n.BarmanConfigFile, []byte(barmanConfigFileContent), 0644); err != nil { | ||
| return fmt.Errorf("failed write %s: %s", n.BarmanConfigFile, err) | ||
| } | ||
|
|
||
| log.Println(n.BarmanConfigFile + " created successfully.") | ||
| } | ||
|
|
||
| if err := n.deleteGlobalBarmanFile(); err != nil { | ||
| return fmt.Errorf("failed delete /etc/barman.conf: %s", err) | ||
| } | ||
|
|
||
| if err := os.Symlink(n.BarmanConfigFile, n.GlobalBarmanConfigFile); err != nil { | ||
| return fmt.Errorf("failed symlink %s to %s: %s", n.BarmanConfigFile, n.GlobalBarmanConfigFile, err) | ||
| } | ||
|
|
||
| log.Println("Symbolic link to barman config created successfully.") | ||
|
|
||
| if err := os.MkdirAll(n.BarmanHome, os.ModePerm); err != nil { | ||
| return fmt.Errorf("failed to mkdir %s: %s", n.BarmanHome, err) | ||
| } | ||
|
|
||
| log.Println("Barman home directory successfully.") | ||
|
|
||
| passStr := fmt.Sprintf("*:*:*:%s:%s", n.ReplCredentials.Username, n.ReplCredentials.Password) | ||
| if err := os.WriteFile(n.PasswordConfigPath, []byte(passStr), 0700); err != nil { | ||
| return fmt.Errorf("failed to write file %s: %s", n.PasswordConfigPath, err) | ||
| } | ||
| // We need this in case the user ssh to the vm as root | ||
| if err := os.WriteFile(n.RootPasswordConfigPath, []byte(passStr), 0700); err != nil { | ||
| return fmt.Errorf("failed to write file %s: %s", n.RootPasswordConfigPath, err) | ||
| } | ||
|
|
||
| if _, err := os.Stat(n.BarmanCronFile); os.IsNotExist(err) { | ||
| barmanCronFileContent := `* * * * * /usr/bin/barman cron | ||
| ` | ||
| if err := os.WriteFile(n.BarmanCronFile, []byte(barmanCronFileContent), 0644); err != nil { | ||
| return fmt.Errorf("failed write %s: %s", n.BarmanCronFile, err) | ||
| } | ||
|
|
||
| log.Println(n.BarmanCronFile + " created successfully.") | ||
| } | ||
|
|
||
| if _, err := os.Stat(n.LogFile); os.IsNotExist(err) { | ||
| file, err := os.Create(n.LogFile) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to touch %s: %s", n.LogFile, err) | ||
| } | ||
| defer func() { _ = file.Close() }() | ||
|
|
||
| log.Println(n.LogFile + " created successfully.") | ||
| } | ||
|
|
||
| if os.Getenv("UNIT_TESTING") == "" { | ||
| crontabCommand := exec.Command("/usr/bin/crontab", n.BarmanCronFile) | ||
| if _, err := crontabCommand.Output(); err != nil { | ||
| return fmt.Errorf("failed set crontab: %s", err) | ||
| } | ||
|
|
||
| log.Println("Crontab updated") | ||
|
|
||
| serviceCmd := exec.Command("/usr/sbin/service", "--version") | ||
| if err := serviceCmd.Run(); err != nil { | ||
| log.Println("service command not found, skipping initializing cron service") | ||
| } else { | ||
| serviceCronStartCommand := exec.Command("service", "cron", "start") | ||
| if _, err := serviceCronStartCommand.Output(); err != nil { | ||
| return fmt.Errorf("failed starting cron service: %s", err) | ||
| } | ||
| log.Println("Started cron service") | ||
| } | ||
|
|
||
| switchWalCommand := exec.Command("barman", "switch-wal", "--archive", "--force", "pg") | ||
| if _, err := switchWalCommand.Output(); err != nil { | ||
| log.Println(fmt.Errorf("failed switching WAL: %s", err)) | ||
| log.Println("try running `barman switch-wal --archive --force pg` or wait for the next WAL") | ||
| } else { | ||
| log.Println("successfully switched WAL files to start barman") | ||
| } | ||
|
|
||
| cronCommand := exec.Command("barman", "cron") | ||
| if _, err := cronCommand.Output(); err != nil { | ||
| log.Println(fmt.Errorf("failed running barman cron: %s", err)) | ||
| log.Println("try running `cronCommand` or wait for the next run") | ||
| } else { | ||
| log.Println("successfully ran `barman cron`") | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func (n *Node) deleteGlobalBarmanFile() error { | ||
| if _, err := os.Stat(n.GlobalBarmanConfigFile); os.IsNotExist(err) { | ||
| return nil | ||
| } | ||
|
|
||
| if err := os.Remove(n.GlobalBarmanConfigFile); err != nil { | ||
| return err | ||
| } | ||
|
|
||
| log.Println(n.GlobalBarmanConfigFile + " deleted successfully") | ||
| return nil | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.