Skip to content

Commit bb5ce6c

Browse files
authored
Merge pull request zalando#195 from zalando-incubator/databases-rest-endpoint
Add a REST endpoint to list databases in all clusters
2 parents 8e99518 + 5c8bd04 commit bb5ce6c

File tree

5 files changed

+70
-4
lines changed

5 files changed

+70
-4
lines changed

pkg/apiserver/apiserver.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type controllerInformer interface {
3333
ClusterStatus(team, cluster string) (*spec.ClusterStatus, error)
3434
ClusterLogs(team, cluster string) ([]*spec.LogEntry, error)
3535
ClusterHistory(team, cluster string) ([]*spec.Diff, error)
36+
ClusterDatabasesMap() map[string][]string
3637
WorkerLogs(workerID uint32) ([]*spec.LogEntry, error)
3738
ListQueue(workerID uint32) (*spec.QueueDump, error)
3839
GetWorkersCnt() uint32
@@ -78,6 +79,7 @@ func New(controller controllerInformer, port int, logger *logrus.Logger) *Server
7879

7980
mux.HandleFunc("/clusters/", s.clusters)
8081
mux.HandleFunc("/workers/", s.workers)
82+
mux.HandleFunc("/databases/", s.databases)
8183

8284
s.http = http.Server{
8385
Addr: fmt.Sprintf(":%d", port),
@@ -222,6 +224,14 @@ func (s *Server) workers(w http.ResponseWriter, req *http.Request) {
222224
s.respond(resp, err, w)
223225
}
224226

227+
func (s *Server) databases(w http.ResponseWriter, req *http.Request) {
228+
229+
databaseNamesPerCluster := s.controller.ClusterDatabasesMap()
230+
s.respond(databaseNamesPerCluster, nil, w)
231+
return
232+
233+
}
234+
225235
func (s *Server) allQueues(w http.ResponseWriter, r *http.Request) {
226236
workersCnt := s.controller.GetWorkersCnt()
227237
resp := make(map[uint32]*spec.QueueDump, workersCnt)

pkg/cluster/cluster.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ type Cluster struct {
7575
oauthTokenGetter OAuthTokenGetter
7676
KubeClient k8sutil.KubernetesClient //TODO: move clients to the better place?
7777
currentProcess spec.Process
78-
processMu sync.RWMutex
78+
processMu sync.RWMutex // protects the current operation for reporting, no need to hold the master mutex
79+
specMu sync.RWMutex // protects the spec for reporting, no need to hold the master mutex
7980
}
8081

8182
type compareStatefulsetResult struct {
@@ -437,7 +438,7 @@ func (c *Cluster) Update(oldSpec, newSpec *spec.Postgresql) error {
437438
defer c.mu.Unlock()
438439

439440
c.setStatus(spec.ClusterStatusUpdating)
440-
c.Postgresql = *newSpec
441+
c.setSpec(newSpec)
441442

442443
defer func() {
443444
if updateFailed {

pkg/cluster/sync.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ func (c *Cluster) Sync(newSpec *spec.Postgresql) (err error) {
2020
c.mu.Lock()
2121
defer c.mu.Unlock()
2222

23-
c.Postgresql = *newSpec
23+
c.setSpec(newSpec)
2424

2525
defer func() {
2626
if err != nil {

pkg/cluster/util.go

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package cluster
22

33
import (
4+
"bytes"
5+
"encoding/gob"
46
"encoding/json"
57
"fmt"
68
"math/rand"
9+
"sort"
710
"strings"
811
"time"
912

@@ -18,7 +21,6 @@ import (
1821
"github.com/zalando-incubator/postgres-operator/pkg/util/constants"
1922
"github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil"
2023
"github.com/zalando-incubator/postgres-operator/pkg/util/retryutil"
21-
"sort"
2224
)
2325

2426
// OAuthTokenGetter provides the method for fetching OAuth tokens
@@ -386,3 +388,32 @@ func (c *Cluster) credentialSecretNameForCluster(username string, clusterName st
386388
func masterCandidate(replicas []spec.NamespacedName) spec.NamespacedName {
387389
return replicas[rand.Intn(len(replicas))]
388390
}
391+
392+
func cloneSpec(from *spec.Postgresql) (*spec.Postgresql, error) {
393+
var (
394+
buf bytes.Buffer
395+
result *spec.Postgresql
396+
err error
397+
)
398+
enc := gob.NewEncoder(&buf)
399+
if err = enc.Encode(*from); err != nil {
400+
return nil, fmt.Errorf("could not encode the spec: %v", err)
401+
}
402+
dec := gob.NewDecoder(&buf)
403+
if err = dec.Decode(&result); err != nil {
404+
return nil, fmt.Errorf("could not decode the spec: %v", err)
405+
}
406+
return result, nil
407+
}
408+
409+
func (c *Cluster) setSpec(newSpec *spec.Postgresql) {
410+
c.specMu.Lock()
411+
c.Postgresql = *newSpec
412+
c.specMu.Unlock()
413+
}
414+
415+
func (c *Cluster) GetSpec() (*spec.Postgresql, error) {
416+
c.specMu.RLock()
417+
defer c.specMu.RUnlock()
418+
return cloneSpec(&c.Postgresql)
419+
}

pkg/controller/status.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package controller
22

33
import (
44
"fmt"
5+
"sort"
56
"sync/atomic"
67

78
"github.com/Sirupsen/logrus"
@@ -32,6 +33,29 @@ func (c *Controller) ClusterStatus(team, cluster string) (*spec.ClusterStatus, e
3233
return status, nil
3334
}
3435

36+
// ClusterDatabasesMap returns for each cluster the list of databases running there
37+
func (c *Controller) ClusterDatabasesMap() map[string][]string {
38+
39+
m := make(map[string][]string)
40+
41+
// avoid modifying the cluster list while we are fetching each one of them.
42+
c.clustersMu.RLock()
43+
defer c.clustersMu.RUnlock()
44+
for _, cluster := range c.clusters {
45+
// GetSpec holds the specMu lock of a cluster
46+
if spec, err := cluster.GetSpec(); err == nil {
47+
for database := range spec.Spec.Databases {
48+
m[cluster.Name] = append(m[cluster.Name], database)
49+
}
50+
sort.Strings(m[cluster.Name])
51+
} else {
52+
c.logger.Warningf("could not get the list of databases for cluster %q: %v", cluster.Name, err)
53+
}
54+
}
55+
56+
return m
57+
}
58+
3559
// TeamClusterList returns team-clusters map
3660
func (c *Controller) TeamClusterList() map[string][]spec.NamespacedName {
3761
return c.teamClusters

0 commit comments

Comments
 (0)