mirror of
https://github.com/zalando/postgres-operator.git
synced 2026-10-18 10:08:40 +02:00
api server
This commit is contained in:
@@ -0,0 +1,104 @@
|
|||||||
|
package apiserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/http/pprof"
|
||||||
|
"sync"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClusterInformer interface {
|
||||||
|
Status() interface{}
|
||||||
|
ClusterStatus(team, cluster string) interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
logger *logrus.Entry
|
||||||
|
http http.Server
|
||||||
|
controller ClusterInformer
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
clusterStatusURL = regexp.MustCompile("^/clusters/(?P<team>[a-zA-Z][a-zA-Z0-9]*)/(?P<cluster>[a-zA-Z][a-zA-Z0-9]*)/?$")
|
||||||
|
teamURL = regexp.MustCompile("^/clusters/(?P<team>[a-zA-Z][a-zA-Z0-9]*)/?$")
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandlerFunc(i interface{}) http.HandlerFunc {
|
||||||
|
b, err := json.Marshal(i)
|
||||||
|
if err != nil {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
fmt.Fprintf(w, "Could not marshal %T: %v", i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Write(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(controller ClusterInformer, port int, logger *logrus.Logger) *Server {
|
||||||
|
s := &Server{
|
||||||
|
logger: logger.WithField("pkg", "apiserver"),
|
||||||
|
controller: controller,
|
||||||
|
}
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
|
||||||
|
mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
|
||||||
|
mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
|
||||||
|
mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
|
||||||
|
mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
|
||||||
|
mux.Handle("/status", HandlerFunc(s.controller.Status()))
|
||||||
|
mux.HandleFunc("/clusters/", func(w http.ResponseWriter, req *http.Request) {
|
||||||
|
var resp interface{}
|
||||||
|
if matches := clusterStatusURL.FindAllStringSubmatch(req.URL.Path, -1); matches != nil {
|
||||||
|
resp = s.controller.ClusterStatus(matches[0][1], matches[0][2])
|
||||||
|
} else if matches := teamURL.FindAllStringSubmatch(req.URL.Path, -1); matches != nil {
|
||||||
|
// TODO
|
||||||
|
} else {
|
||||||
|
http.NotFound(w, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := json.Marshal(resp)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
fmt.Fprintf(w, "Could not marshal %T: %v", resp, err)
|
||||||
|
} else {
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
w.Write(b)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
s.http = http.Server{
|
||||||
|
Addr: fmt.Sprintf(":%d", port),
|
||||||
|
Handler: mux,
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) Run(stopCh <-chan struct{}, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err := s.http.ListenAndServe()
|
||||||
|
if err != http.ErrServerClosed {
|
||||||
|
s.logger.Fatalf("could not start http server: %v", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
s.logger.Infof("listening on %s", s.http.Addr)
|
||||||
|
|
||||||
|
<-stopCh
|
||||||
|
|
||||||
|
ctx, _ := context.WithCancel(context.Background())
|
||||||
|
s.http.Shutdown(ctx)
|
||||||
|
}
|
||||||
@@ -36,11 +36,11 @@ var (
|
|||||||
// Config contains operator-wide clients and configuration used from a cluster. TODO: remove struct duplication.
|
// Config contains operator-wide clients and configuration used from a cluster. TODO: remove struct duplication.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
OpConfig config.Config
|
OpConfig config.Config
|
||||||
RestConfig *rest.Config
|
RestConfig *rest.Config `json:"-"`
|
||||||
InfrastructureRoles map[string]spec.PgUser // inherited from the controller
|
InfrastructureRoles map[string]spec.PgUser // inherited from the controller
|
||||||
}
|
}
|
||||||
|
|
||||||
type kubeResources struct {
|
type KubeResources struct {
|
||||||
Service map[PostgresRole]*v1.Service
|
Service map[PostgresRole]*v1.Service
|
||||||
Endpoint *v1.Endpoints
|
Endpoint *v1.Endpoints
|
||||||
Secrets map[types.UID]*v1.Secret
|
Secrets map[types.UID]*v1.Secret
|
||||||
@@ -50,7 +50,7 @@ type kubeResources struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Cluster struct {
|
type Cluster struct {
|
||||||
kubeResources
|
KubeResources
|
||||||
spec.Postgresql
|
spec.Postgresql
|
||||||
Config
|
Config
|
||||||
logger *logrus.Entry
|
logger *logrus.Entry
|
||||||
@@ -79,7 +79,7 @@ type compareStatefulsetResult struct {
|
|||||||
// New creates a new cluster. This function should be called from a controller.
|
// New creates a new cluster. This function should be called from a controller.
|
||||||
func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec spec.Postgresql, logger *logrus.Entry) *Cluster {
|
func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec spec.Postgresql, logger *logrus.Entry) *Cluster {
|
||||||
lg := logger.WithField("pkg", "cluster").WithField("cluster-name", pgSpec.Name)
|
lg := logger.WithField("pkg", "cluster").WithField("cluster-name", pgSpec.Name)
|
||||||
kubeResources := kubeResources{Secrets: make(map[types.UID]*v1.Secret), Service: make(map[PostgresRole]*v1.Service)}
|
kubeResources := KubeResources{Secrets: make(map[types.UID]*v1.Secret), Service: make(map[PostgresRole]*v1.Service)}
|
||||||
orphanDependents := true
|
orphanDependents := true
|
||||||
|
|
||||||
podEventsQueue := cache.NewFIFO(func(obj interface{}) (string, error) {
|
podEventsQueue := cache.NewFIFO(func(obj interface{}) (string, error) {
|
||||||
@@ -98,7 +98,7 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec spec.Postgresql
|
|||||||
pgUsers: make(map[string]spec.PgUser),
|
pgUsers: make(map[string]spec.PgUser),
|
||||||
systemUsers: make(map[string]spec.PgUser),
|
systemUsers: make(map[string]spec.PgUser),
|
||||||
podSubscribers: make(map[spec.NamespacedName]chan spec.PodEvent),
|
podSubscribers: make(map[spec.NamespacedName]chan spec.PodEvent),
|
||||||
kubeResources: kubeResources,
|
KubeResources: kubeResources,
|
||||||
masterLess: false,
|
masterLess: false,
|
||||||
userSyncStrategy: users.DefaultUserSyncStrategy{},
|
userSyncStrategy: users.DefaultUserSyncStrategy{},
|
||||||
deleteOptions: &metav1.DeleteOptions{OrphanDependents: &orphanDependents},
|
deleteOptions: &metav1.DeleteOptions{OrphanDependents: &orphanDependents},
|
||||||
|
|||||||
@@ -15,10 +15,11 @@ import (
|
|||||||
"github.com/zalando-incubator/postgres-operator/pkg/util/config"
|
"github.com/zalando-incubator/postgres-operator/pkg/util/config"
|
||||||
"github.com/zalando-incubator/postgres-operator/pkg/util/constants"
|
"github.com/zalando-incubator/postgres-operator/pkg/util/constants"
|
||||||
"github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil"
|
"github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil"
|
||||||
|
"github.com/zalando-incubator/postgres-operator/pkg/apiserver"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
RestConfig *rest.Config
|
RestConfig *rest.Config `json:"-"`
|
||||||
InfrastructureRoles map[string]spec.PgUser
|
InfrastructureRoles map[string]spec.PgUser
|
||||||
|
|
||||||
NoDatabaseAccess bool
|
NoDatabaseAccess bool
|
||||||
@@ -34,6 +35,7 @@ type Controller struct {
|
|||||||
logger *logrus.Entry
|
logger *logrus.Entry
|
||||||
KubeClient k8sutil.KubernetesClient
|
KubeClient k8sutil.KubernetesClient
|
||||||
RestClient rest.Interface // kubernetes API group REST client
|
RestClient rest.Interface // kubernetes API group REST client
|
||||||
|
apiserver *apiserver.Server
|
||||||
|
|
||||||
clustersMu sync.RWMutex
|
clustersMu sync.RWMutex
|
||||||
clusters map[spec.NamespacedName]*cluster.Cluster
|
clusters map[spec.NamespacedName]*cluster.Cluster
|
||||||
@@ -166,6 +168,8 @@ func (c *Controller) initController() {
|
|||||||
return fmt.Sprintf("%s-%s", e.EventType, e.UID), nil
|
return fmt.Sprintf("%s-%s", e.EventType, e.UID), nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.apiserver = apiserver.New(c, c.opConfig.APIPort, c.logger.Logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Controller) Run(stopCh <-chan struct{}, wg *sync.WaitGroup) {
|
func (c *Controller) Run(stopCh <-chan struct{}, wg *sync.WaitGroup) {
|
||||||
@@ -176,7 +180,7 @@ func (c *Controller) Run(stopCh <-chan struct{}, wg *sync.WaitGroup) {
|
|||||||
go c.runPostgresqlInformer(stopCh, wg)
|
go c.runPostgresqlInformer(stopCh, wg)
|
||||||
go c.podEventsDispatcher(stopCh, wg)
|
go c.podEventsDispatcher(stopCh, wg)
|
||||||
go c.clusterResync(stopCh, wg)
|
go c.clusterResync(stopCh, wg)
|
||||||
go c.RunAPIServer(stopCh, wg)
|
go c.apiserver.Run(stopCh, wg)
|
||||||
|
|
||||||
for i := range c.clusterEventQueues {
|
for i := range c.clusterEventQueues {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/http/pprof"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *Controller) RunAPIServer(stopCh <-chan struct{}, wg *sync.WaitGroup) {
|
|
||||||
defer wg.Done()
|
|
||||||
|
|
||||||
mux := http.NewServeMux()
|
|
||||||
|
|
||||||
mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
|
|
||||||
mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
|
|
||||||
mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
|
|
||||||
mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
|
|
||||||
mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))
|
|
||||||
|
|
||||||
server := http.Server{
|
|
||||||
Addr: fmt.Sprintf(":%d", c.opConfig.APIPort),
|
|
||||||
Handler: mux,
|
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
err := server.ListenAndServe()
|
|
||||||
if err != http.ErrServerClosed {
|
|
||||||
c.logger.Fatalf("could not start http server: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
c.logger.Infof("listening on %s", server.Addr)
|
|
||||||
|
|
||||||
<-stopCh
|
|
||||||
|
|
||||||
ctx, _ := context.WithCancel(context.Background())
|
|
||||||
server.Shutdown(ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Controller) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
fmt.Printf("Request: %+v\n", r.URL.String())
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
package controller
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/zalando-incubator/postgres-operator/pkg/util/config"
|
||||||
|
"github.com/zalando-incubator/postgres-operator/pkg/spec"
|
||||||
|
"github.com/zalando-incubator/postgres-operator/pkg/cluster"
|
||||||
|
)
|
||||||
|
|
||||||
|
type controllerStatus struct {
|
||||||
|
ControllerConfig Config
|
||||||
|
OperatorConfig config.Config
|
||||||
|
LastSyncTime int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type clusterStatus struct {
|
||||||
|
Team string
|
||||||
|
Cluster string
|
||||||
|
|
||||||
|
Config cluster.Config
|
||||||
|
Status spec.PostgresStatus
|
||||||
|
Resources cluster.KubeResources
|
||||||
|
Spec spec.PostgresSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) ClusterStatus(team, cluster string) interface{} {
|
||||||
|
clusterName := spec.NamespacedName{
|
||||||
|
Namespace: c.opConfig.Namespace,
|
||||||
|
Name: team + "-" + cluster,
|
||||||
|
}
|
||||||
|
|
||||||
|
cl, ok := c.clusters[clusterName]
|
||||||
|
if !ok {
|
||||||
|
return struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clusterStatus{
|
||||||
|
Config: cl.Config,
|
||||||
|
Cluster: cl.Spec.ClusterName,
|
||||||
|
Team: cl.Spec.TeamID,
|
||||||
|
Status: cl.Status,
|
||||||
|
Resources: cl.KubeResources,
|
||||||
|
Spec: cl.Spec,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) Status() interface{} {
|
||||||
|
return controllerStatus{
|
||||||
|
ControllerConfig: c.config,
|
||||||
|
OperatorConfig: *c.opConfig,
|
||||||
|
LastSyncTime: c.lastClusterSyncTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user