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
8 changes: 0 additions & 8 deletions internal/flypg/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ package flypg

import (
"context"
"encoding/binary"
"errors"
"fmt"
"math/rand"
"net"
"os"
"os/exec"
Expand Down Expand Up @@ -86,13 +84,7 @@ func NewNode() (*Node, error) {
Password: os.Getenv("REPL_PASSWORD"),
}

// Generate a random, reconstructable signed int32
machineID := os.Getenv("FLY_ALLOC_ID")
seed := binary.LittleEndian.Uint64([]byte(machineID))
rand.Seed(int64(seed))

node.RepMgr = RepMgr{
ID: rand.Int31(), // skipcq: GSC-G404
AppName: node.AppName,
PrimaryRegion: node.PrimaryRegion,
Region: os.Getenv("FLY_REGION"),
Expand Down
46 changes: 43 additions & 3 deletions internal/flypg/repmgr.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ package flypg

import (
"context"
"crypto/rand"
"fmt"
"math"
"math/big"
"net"
"os"
"strconv"
Expand Down Expand Up @@ -95,7 +98,9 @@ func (r *RepMgr) NewRemoteConnection(ctx context.Context, hostname string) (*pgx
}

func (r *RepMgr) initialize() error {
r.setDefaults()
if err := r.setDefaults(); err != nil {
return fmt.Errorf("failed to set repmgr defaults: %s", err)
}

file, err := os.Create(r.ConfigPath)
if err != nil {
Expand Down Expand Up @@ -139,9 +144,14 @@ func (r *RepMgr) setup(ctx context.Context, conn *pgx.Conn) error {
return nil
}

func (r *RepMgr) setDefaults() {
func (r *RepMgr) setDefaults() error {
nodeID, err := r.resolveNodeID()
if err != nil {
return err
}

conf := ConfigMap{
"node_id": fmt.Sprint(r.ID),
"node_id": nodeID,
"node_name": fmt.Sprintf("'%s'", r.PrivateIP),
"conninfo": fmt.Sprintf("'host=%s port=%d user=%s dbname=%s connect_timeout=10'", r.PrivateIP, r.Port, r.Credentials.Username, r.DatabaseName),
"data_directory": fmt.Sprintf("'%s'", r.DataDir),
Expand All @@ -163,6 +173,36 @@ func (r *RepMgr) setDefaults() {
}

r.internalConfig = conf

return nil
}

func (r *RepMgr) resolveNodeID() (string, error) {
var nodeID string
if utils.FileExists(r.InternalConfigFile()) {
// Pull existing id from configuraiton file
config, err := r.CurrentConfig()
if err != nil {
return "", fmt.Errorf("failed to resolve current repmgr config: %s", err)
}

if val, ok := config["node_id"]; ok {
nodeID = fmt.Sprint(val)
}

if nodeID == "" {
return "", fmt.Errorf("failed to resolve existing node_id: %s", err)
}
} else {
// Generate a new random id
id, err := rand.Int(rand.Reader, big.NewInt(math.MaxInt32))
if err != nil {
return "", fmt.Errorf("failed to generate node id: %s", err)
}
nodeID = id.String()
}

return nodeID, nil
}

func (r *RepMgr) registerPrimary() error {
Expand Down
84 changes: 84 additions & 0 deletions internal/flypg/repmgr_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package flypg

import (
"testing"
)

const (
repmgrTestDirectory = "./test_results"
repmgrConfigFilePath = "./test_results/repmgr.conf"
repgmrInternalConfigFilePath = "./test_results/repmgr.internal.conf"
repgmrUserConfigFilePath = "./test_results/repmgr.internal.conf"
)

func TestRepmgrConfigDefaults(t *testing.T) {
if err := setup(t); err != nil {
t.Fatal(err)
}
defer cleanup()

conf := &RepMgr{
AppName: "test-app",
PrimaryRegion: "dev",
Region: "dev",
ConfigPath: repmgrConfigFilePath,
InternalConfigPath: repgmrInternalConfigFilePath,
UserConfigPath: repgmrUserConfigFilePath,
DataDir: repmgrTestDirectory,
PrivateIP: "127.0.0.1",
Port: 5433,
DatabaseName: "repmgr",
}

if err := conf.setDefaults(); err != nil {
t.Error(err)
}

if conf.internalConfig["node_name"] != "'127.0.0.1'" {
t.Fatalf("expected node_name to be '127.0.0.1', got %v", conf.internalConfig["node_name"])
}

if conf.internalConfig["node_id"] == "" {
t.Fatalf("expected node_id to not be empty, got %q", conf.internalConfig["node_id"])
}

}

func TestRepmgrNodeIDGeneration(t *testing.T) {
if err := setup(t); err != nil {
t.Fatal(err)
}
defer cleanup()

conf := &RepMgr{
AppName: "test-app",
PrimaryRegion: "dev",
Region: "dev",
ConfigPath: repmgrConfigFilePath,
InternalConfigPath: repgmrInternalConfigFilePath,
UserConfigPath: repgmrUserConfigFilePath,
DataDir: repmgrTestDirectory,
PrivateIP: "127.0.0.1",
Port: 5433,
DatabaseName: "repmgr",
}

if err := conf.setDefaults(); err != nil {
t.Error(err)
}

if err := writeInternalConfigFile(conf); err != nil {
t.Error(err)
}

nodeID := conf.internalConfig["node_id"]

resolvedNodeID, err := conf.resolveNodeID()
if err != nil {
t.Error(err)
}

if nodeID != resolvedNodeID {
t.Fatalf("expected node_id to be %s, got %q", nodeID, resolvedNodeID)
}
}
7 changes: 7 additions & 0 deletions internal/utils/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ func SetFileOwnership(pathToFile, owner string) error {
return nil
}

func FileExists(path string) bool {
if _, err := os.Stat(path); err != nil {
return false
}
return true
}

func SystemUserIDs(usr string) (int, int, error) {
pgUser, err := user.Lookup(usr)
if err != nil {
Expand Down