Skip to content

Commit

Permalink
[!] add Patroni REST API support (#263)
Browse files Browse the repository at this point in the history
* [!] add Patroni REST API support
* [-] fix nil pointer dereference
  • Loading branch information
pashagolub authored Oct 28, 2024
1 parent 459ef03 commit 8aef662
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 9 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

# vip-manager

Manages a virtual IP based on state kept in `etcd` or `Consul`
Manages a virtual IP based on state kept in `etcd`, `Consul` or using `Patroni` REST API

## Table of Contents
- [Prerequisites](#prerequisites)
Expand Down Expand Up @@ -159,4 +159,4 @@ Either:
## Author

Cybertec Schönig & Schönig GmbH, https://www.cybertec-postgresql.com
CYBERTEC PostgreSQL International GmbH, https://www.cybertec-postgresql.com
2 changes: 2 additions & 0 deletions checker/leader_checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ func NewLeaderChecker(con *vipconfig.Config) (LeaderChecker, error) {
lc, err = NewConsulLeaderChecker(con)
case "etcd", "etcd3":
lc, err = NewEtcdLeaderChecker(con)
case "patroni":
lc, err = NewPatroniLeaderChecker(con)
default:
err = ErrUnsupportedEndpointType
}
Expand Down
41 changes: 41 additions & 0 deletions checker/patroni_leader_checker.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package checker

import (
"context"
"log"
"strconv"
"time"

"net/http"

"github.com/cybertec-postgresql/vip-manager/vipconfig"
)

// PatroniLeaderChecker will use Patroni REST API to check the leader.
// --trigger-key is used to specify the endpoint to check, e.g. /leader.
// --trigger-value is used to specify the HTTP code to expect, e.g. 200.
type PatroniLeaderChecker struct {
*vipconfig.Config
}

// NewPatroniLeaderChecker returns a new instance
func NewPatroniLeaderChecker(conf *vipconfig.Config) (*PatroniLeaderChecker, error) {
return &PatroniLeaderChecker{conf}, nil
}

// GetChangeNotificationStream checks the status in the loop
func (c *PatroniLeaderChecker) GetChangeNotificationStream(ctx context.Context, out chan<- bool) error {
for {
select {
case <-ctx.Done():
return nil
case <-time.After(time.Duration(c.Interval) * time.Millisecond):
r, err := http.Get(c.Endpoints[0] + c.Key)
if err != nil {
log.Printf("patroni REST API error: %s", err)
continue
}
out <- strconv.Itoa(r.StatusCode) == c.Nodename
}
}
}
16 changes: 11 additions & 5 deletions vipconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ func defineFlags() {
pflag.String("trigger-key", "", "Key in the DCS to monitor, e.g. \"/service/batman/leader\".")
pflag.String("trigger-value", "", "Value to monitor for.")

pflag.String("dcs-type", "etcd", "Type of endpoint used for key storage. Supported values: etcd, consul.")
pflag.String("dcs-type", "etcd", "Type of endpoint used for key storage. Supported values: etcd, consul, patroni.")
// note: can't put a default value into dcs-endpoints as that would mess with applying default localhost when using consul
pflag.String("dcs-endpoints", "", "DCS endpoint(s), separate multiple endpoints using commas. (default \"http://127.0.0.1:2379\" or \"http://127.0.0.1:8500\" depending on dcs-type.)")
pflag.String("dcs-endpoints", "", "DCS endpoint(s), separate multiple endpoints using commas. (default \"http://127.0.0.1:2379\", \"http://127.0.0.1:8500\" or \"http://127.0.0.1:8008/\" depending on dcs-type.)")
pflag.String("etcd-user", "", "Username for etcd DCS endpoints.")
pflag.String("etcd-password", "", "Password for etcd DCS endpoints.")
pflag.String("etcd-ca-file", "", "Trusted CA certificate for the etcd server.")
Expand Down Expand Up @@ -302,12 +302,18 @@ func NewConfig() (*Config, error) {
viper.Set("dcs-endpoints", []string{"http://127.0.0.1:8500"})
case "etcd", "etcd3":
viper.Set("dcs-endpoints", []string{"http://127.0.0.1:2379"})
case "patroni":
viper.Set("dcs-endpoints", []string{"http://127.0.0.1:8008/"})
}
}

// set trigger-value to hostname if nothing is specified
if len(viper.GetString("trigger-value")) == 0 {
triggerValue, err := os.Hostname()
// set trigger-value to default value if nothing is specified
if triggerValue := viper.GetString("trigger-value"); len(triggerValue) == 0 {
if viper.GetString("dcs-type") == "patroni" {
triggerValue = "200"
} else {
triggerValue, err = os.Hostname()
}
if err != nil {
log.Printf("No trigger-value specified, hostname could not be retrieved: %s", err)
} else {
Expand Down
4 changes: 2 additions & 2 deletions vipconfig/vip-manager.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ interface: enp0s3 #interface to which the virtual ip will be added
# how the virtual ip should be managed. we currently support "ip addr add/remove" through shell commands or the Hetzner api
hosting-type: basic # possible values: basic, or hetzner.

dcs-type: etcd # etcd or consul
dcs-type: etcd # etcd, consul or patroni
# a list that contains all DCS endpoints to which vip-manager could talk.
dcs-endpoints:
- http://127.0.0.1:2379
- https://192.168.0.42:2379
# A single list-item is also fine.
# consul will always only use the first entry from this list.
# consul and patroni will always only use the first entry from this list.
# For consul, you'll obviously need to change the port to 8500. Unless you're using a different one. Maybe you're a rebel and are running consul on port 2379? Just to confuse people? Why would you do that? Oh, I get it.

etcd-user: "patroni"
Expand Down

0 comments on commit 8aef662

Please sign in to comment.