Skip to content

Commit 8aa11ec

Browse files
authored
Add patroni api client
1 parent 899c0be commit 8aa11ec

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed

pkg/cluster/cluster.go

+3
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/zalando-incubator/postgres-operator/pkg/util/config"
2424
"github.com/zalando-incubator/postgres-operator/pkg/util/constants"
2525
"github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil"
26+
"github.com/zalando-incubator/postgres-operator/pkg/util/patroni"
2627
"github.com/zalando-incubator/postgres-operator/pkg/util/teams"
2728
"github.com/zalando-incubator/postgres-operator/pkg/util/users"
2829
"github.com/zalando-incubator/postgres-operator/pkg/util/volumes"
@@ -55,6 +56,7 @@ type Cluster struct {
5556
spec.Postgresql
5657
Config
5758
logger *logrus.Entry
59+
patroni patroni.Interface
5860
pgUsers map[string]spec.PgUser
5961
systemUsers map[string]spec.PgUser
6062
podSubscribers map[spec.NamespacedName]chan spec.PodEvent
@@ -105,6 +107,7 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec spec.Postgresql
105107
teamsAPIClient: teams.NewTeamsAPI(cfg.OpConfig.TeamsAPIUrl, logger),
106108
}
107109
cluster.logger = logger.WithField("pkg", "cluster").WithField("cluster-name", cluster.clusterName())
110+
cluster.patroni = patroni.New(cluster.logger)
108111

109112
return cluster
110113
}

pkg/util/patroni/patroni.go

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package patroni
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"fmt"
7+
"io/ioutil"
8+
"net/http"
9+
"time"
10+
11+
"github.com/Sirupsen/logrus"
12+
"k8s.io/client-go/pkg/api/v1"
13+
)
14+
15+
const (
16+
failoverPath = "/failover"
17+
apiPort = 8008
18+
timeout = 30 * time.Second
19+
)
20+
21+
// Interface describe patroni methods
22+
type Interface interface {
23+
Failover(master *v1.Pod, candidate string) error
24+
}
25+
26+
// Patroni API client
27+
type Patroni struct {
28+
httpClient *http.Client
29+
logger *logrus.Entry
30+
}
31+
32+
// New create patroni
33+
func New(logger *logrus.Entry) *Patroni {
34+
cl := http.Client{
35+
Timeout: timeout,
36+
}
37+
38+
return &Patroni{
39+
logger: logger,
40+
httpClient: &cl,
41+
}
42+
}
43+
44+
func (p *Patroni) apiURL(masterPod *v1.Pod) string {
45+
return fmt.Sprintf("http://%s:%d", masterPod.Status.PodIP, apiPort)
46+
}
47+
48+
// Failover does manual failover via patroni api
49+
func (p *Patroni) Failover(master *v1.Pod, candidate string) error {
50+
buf := &bytes.Buffer{}
51+
52+
err := json.NewEncoder(buf).Encode(map[string]string{"leader": master.Name, "member": candidate})
53+
if err != nil {
54+
return fmt.Errorf("could not encode json: %v", err)
55+
}
56+
57+
request, err := http.NewRequest(http.MethodPost, p.apiURL(master)+failoverPath, buf)
58+
if err != nil {
59+
return fmt.Errorf("could not create request: %v", err)
60+
}
61+
62+
p.logger.Debugf("making http request: %s", request.URL.String())
63+
64+
resp, err := p.httpClient.Do(request)
65+
if err != nil {
66+
return fmt.Errorf("could not make request: %v", err)
67+
}
68+
defer resp.Body.Close()
69+
70+
if resp.StatusCode != http.StatusOK {
71+
bodyBytes, err := ioutil.ReadAll(resp.Body)
72+
if err != nil {
73+
return fmt.Errorf("could not read response: %v", err)
74+
}
75+
76+
return fmt.Errorf("patroni returned '%s'", string(bodyBytes))
77+
}
78+
79+
return nil
80+
}

0 commit comments

Comments
 (0)