181 lines
4 KiB
Go
181 lines
4 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"net"
|
|
"net/http"
|
|
"time"
|
|
|
|
"git.giftfish.de/ston1th/haproxy-lb/pkg/api/types"
|
|
serverv1 "git.giftfish.de/ston1th/haproxy-lb/pkg/api/v1/server"
|
|
"git.giftfish.de/ston1th/haproxy-lb/pkg/config"
|
|
"git.giftfish.de/ston1th/haproxy-lb/pkg/db"
|
|
|
|
"git.giftfish.de/ston1th/netalloc"
|
|
"git.giftfish.de/ston1th/netalloc/netbox"
|
|
|
|
"github.com/go-logr/logr"
|
|
"github.com/gorilla/mux"
|
|
)
|
|
|
|
// Server is the webapp and api server
|
|
type Server struct {
|
|
srv *http.Server
|
|
mux *mux.Router
|
|
log logr.Logger
|
|
|
|
Data *types.ContextData
|
|
|
|
init chan struct{}
|
|
stop chan struct{}
|
|
stopKeyReset chan struct{}
|
|
|
|
tlsConfig *tls.Config
|
|
listen net.Listener
|
|
|
|
prefix string
|
|
store config.Store
|
|
}
|
|
|
|
type notFoundHandler struct {
|
|
s *Server
|
|
}
|
|
|
|
func (nf *notFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
types.NewContext(w, r, nf.s.Data, nf.s.log).NotFound()
|
|
}
|
|
|
|
// NewHTTPServer returns a new HTTPServer
|
|
func NewServer(c *config.Config, log logr.Logger) (*Server, error) {
|
|
s := &Server{
|
|
mux: mux.NewRouter(),
|
|
log: log,
|
|
Data: &types.ContextData{},
|
|
|
|
init: make(chan struct{}),
|
|
stop: make(chan struct{}),
|
|
stopKeyReset: make(chan struct{}),
|
|
|
|
prefix: c.VIP.Prefix,
|
|
store: c.VIP.Store,
|
|
}
|
|
s.Data.AuthDisabled = c.APIServer.DisableAuth
|
|
if !s.Data.AuthDisabled {
|
|
s.Data.Auth = types.NewAuth(c.APIServer.BasicAuth)
|
|
}
|
|
|
|
if c.APIServer.TLS != nil {
|
|
cert, err := tls.LoadX509KeyPair(c.APIServer.TLS.Cert, c.APIServer.TLS.Key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
s.tlsConfig = &tls.Config{
|
|
MinVersion: tls.VersionTLS12,
|
|
CipherSuites: []uint16{
|
|
tls.TLS_AES_256_GCM_SHA384,
|
|
tls.TLS_CHACHA20_POLY1305_SHA256,
|
|
tls.TLS_AES_128_GCM_SHA256,
|
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
|
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
|
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
},
|
|
CurvePreferences: []tls.CurveID{
|
|
tls.X25519,
|
|
tls.CurveP384,
|
|
tls.CurveP521,
|
|
},
|
|
PreferServerCipherSuites: true,
|
|
Certificates: []tls.Certificate{cert},
|
|
}
|
|
l, err := tls.Listen("tcp", c.APIServer.Listen, s.tlsConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
s.listen = l
|
|
} else {
|
|
l, err := net.Listen("tcp", c.APIServer.Listen)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
s.listen = l
|
|
}
|
|
|
|
s.mux.NotFoundHandler = ¬FoundHandler{s}
|
|
for _, v := range serverv1.Routes {
|
|
s.mux.HandleFunc(v.Path, s.contextWrapper(v.Handler)).Methods(v.Methods...)
|
|
}
|
|
s.start()
|
|
return s, nil
|
|
}
|
|
|
|
func (s *Server) UpdateDB(db *db.DB, leader bool) error {
|
|
s.Data.DB = db
|
|
s.Data.Leader = leader
|
|
if s.store.Local != nil {
|
|
if s.Data.Net == nil {
|
|
na, err := netalloc.NewGenericAlloc(db, s.prefix)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.Data.Net = na
|
|
close(s.init)
|
|
} else {
|
|
s.Data.Net.SetStore(db)
|
|
}
|
|
} else {
|
|
if s.Data.Net == nil {
|
|
nb := s.store.Netbox
|
|
na, err := netbox.NewAllocator(
|
|
netbox.WithEndpoint(nb.Endpoint),
|
|
netbox.WithToken(nb.Token),
|
|
netbox.WithPrefix(s.prefix),
|
|
netbox.WithInsecureClient(nb.Insecure),
|
|
)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
s.Data.Net = na
|
|
close(s.init)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Server) start() {
|
|
s.srv = &http.Server{
|
|
Handler: s.mux,
|
|
TLSConfig: s.tlsConfig,
|
|
ReadTimeout: 10 * time.Second,
|
|
WriteTimeout: 10 * time.Second,
|
|
}
|
|
go func() {
|
|
<-s.init
|
|
err := s.srv.Serve(s.listen)
|
|
if err != nil {
|
|
s.log.Error(err, "")
|
|
}
|
|
}()
|
|
return
|
|
}
|
|
|
|
func (s *Server) contextWrapper(h types.CtxHandler) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
h(types.NewContext(w, r, s.Data, s.log))
|
|
}
|
|
}
|
|
|
|
// Stop stops listening for incoming connections and closes currently open connections
|
|
func (s *Server) Stop() {
|
|
s.log.Info("stopping")
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
|
defer cancel()
|
|
err := s.srv.Shutdown(ctx)
|
|
if err != nil {
|
|
s.log.Error(err, "")
|
|
}
|
|
s.log.Info("stopped")
|
|
}
|