Skip to content

Commit 8aef662

Browse files
authored
[!] add Patroni REST API support (#263)
* [!] add Patroni REST API support * [-] fix nil pointer dereference
1 parent 459ef03 commit 8aef662

File tree

5 files changed

+58
-9
lines changed

5 files changed

+58
-9
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
# vip-manager
88

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

1111
## Table of Contents
1212
- [Prerequisites](#prerequisites)
@@ -159,4 +159,4 @@ Either:
159159
160160
## Author
161161

162-
Cybertec Schönig & Schönig GmbH, https://www.cybertec-postgresql.com
162+
CYBERTEC PostgreSQL International GmbH, https://www.cybertec-postgresql.com

checker/leader_checker.go

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ func NewLeaderChecker(con *vipconfig.Config) (LeaderChecker, error) {
2525
lc, err = NewConsulLeaderChecker(con)
2626
case "etcd", "etcd3":
2727
lc, err = NewEtcdLeaderChecker(con)
28+
case "patroni":
29+
lc, err = NewPatroniLeaderChecker(con)
2830
default:
2931
err = ErrUnsupportedEndpointType
3032
}

checker/patroni_leader_checker.go

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package checker
2+
3+
import (
4+
"context"
5+
"log"
6+
"strconv"
7+
"time"
8+
9+
"net/http"
10+
11+
"github.com/cybertec-postgresql/vip-manager/vipconfig"
12+
)
13+
14+
// PatroniLeaderChecker will use Patroni REST API to check the leader.
15+
// --trigger-key is used to specify the endpoint to check, e.g. /leader.
16+
// --trigger-value is used to specify the HTTP code to expect, e.g. 200.
17+
type PatroniLeaderChecker struct {
18+
*vipconfig.Config
19+
}
20+
21+
// NewPatroniLeaderChecker returns a new instance
22+
func NewPatroniLeaderChecker(conf *vipconfig.Config) (*PatroniLeaderChecker, error) {
23+
return &PatroniLeaderChecker{conf}, nil
24+
}
25+
26+
// GetChangeNotificationStream checks the status in the loop
27+
func (c *PatroniLeaderChecker) GetChangeNotificationStream(ctx context.Context, out chan<- bool) error {
28+
for {
29+
select {
30+
case <-ctx.Done():
31+
return nil
32+
case <-time.After(time.Duration(c.Interval) * time.Millisecond):
33+
r, err := http.Get(c.Endpoints[0] + c.Key)
34+
if err != nil {
35+
log.Printf("patroni REST API error: %s", err)
36+
continue
37+
}
38+
out <- strconv.Itoa(r.StatusCode) == c.Nodename
39+
}
40+
}
41+
}

vipconfig/config.go

+11-5
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,9 @@ func defineFlags() {
5555
pflag.String("trigger-key", "", "Key in the DCS to monitor, e.g. \"/service/batman/leader\".")
5656
pflag.String("trigger-value", "", "Value to monitor for.")
5757

58-
pflag.String("dcs-type", "etcd", "Type of endpoint used for key storage. Supported values: etcd, consul.")
58+
pflag.String("dcs-type", "etcd", "Type of endpoint used for key storage. Supported values: etcd, consul, patroni.")
5959
// note: can't put a default value into dcs-endpoints as that would mess with applying default localhost when using consul
60-
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.)")
60+
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.)")
6161
pflag.String("etcd-user", "", "Username for etcd DCS endpoints.")
6262
pflag.String("etcd-password", "", "Password for etcd DCS endpoints.")
6363
pflag.String("etcd-ca-file", "", "Trusted CA certificate for the etcd server.")
@@ -302,12 +302,18 @@ func NewConfig() (*Config, error) {
302302
viper.Set("dcs-endpoints", []string{"http://127.0.0.1:8500"})
303303
case "etcd", "etcd3":
304304
viper.Set("dcs-endpoints", []string{"http://127.0.0.1:2379"})
305+
case "patroni":
306+
viper.Set("dcs-endpoints", []string{"http://127.0.0.1:8008/"})
305307
}
306308
}
307309

308-
// set trigger-value to hostname if nothing is specified
309-
if len(viper.GetString("trigger-value")) == 0 {
310-
triggerValue, err := os.Hostname()
310+
// set trigger-value to default value if nothing is specified
311+
if triggerValue := viper.GetString("trigger-value"); len(triggerValue) == 0 {
312+
if viper.GetString("dcs-type") == "patroni" {
313+
triggerValue = "200"
314+
} else {
315+
triggerValue, err = os.Hostname()
316+
}
311317
if err != nil {
312318
log.Printf("No trigger-value specified, hostname could not be retrieved: %s", err)
313319
} else {

vipconfig/vip-manager.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ interface: enp0s3 #interface to which the virtual ip will be added
1515
# how the virtual ip should be managed. we currently support "ip addr add/remove" through shell commands or the Hetzner api
1616
hosting-type: basic # possible values: basic, or hetzner.
1717

18-
dcs-type: etcd # etcd or consul
18+
dcs-type: etcd # etcd, consul or patroni
1919
# a list that contains all DCS endpoints to which vip-manager could talk.
2020
dcs-endpoints:
2121
- http://127.0.0.1:2379
2222
- https://192.168.0.42:2379
2323
# A single list-item is also fine.
24-
# consul will always only use the first entry from this list.
24+
# consul and patroni will always only use the first entry from this list.
2525
# 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.
2626

2727
etcd-user: "patroni"

0 commit comments

Comments
 (0)