haproxy-lb/pkg/api/v1/server/handler.go
2021-04-09 22:38:03 +02:00

170 lines
3.9 KiB
Go

package server
import (
//"golang.org/x/crypto/bcrypt"
//"net/http"
"context"
"encoding/json"
"net"
"git.giftfish.de/ston1th/haproxy-lb/pkg/api/types"
"git.giftfish.de/ston1th/haproxy-lb/pkg/api/v1/schema"
"git.giftfish.de/ston1th/haproxy-lb/pkg/cluster"
)
func authHandler(h types.CtxHandler) types.CtxHandler {
return func(ctx *types.Context) {
user, pass, ok := ctx.Request.BasicAuth()
if !ok {
ctx.Err(types.ErrUnauthorized)
return
}
if !ctx.Data.Auth.Login(user, pass, ctx.Path()) {
ctx.Err(types.ErrForbidden)
return
}
h(ctx)
}
}
func healthzHandler(ctx *types.Context) {
// TODO maybe report etcd/raft stats
ctx.OK()
}
func name(c, n string) string {
return c + "/" + n
}
func lbname(c, n string) string {
return c + "_" + n
}
func getIP(cidr string) (string, error) {
ip, _, err := net.ParseCIDR(cidr)
if err != nil {
return "", err
}
return ip.String(), nil
}
func lbHandler(ctx *types.Context) {
cl := ctx.Var("cluster")
n := ctx.Var("name")
name := name(cl, n)
switch ctx.Method() {
case "GET":
b, err := ctx.Data.DB.GetLB(name)
if err != nil {
ctx.Log.Error(err, "error reading loadbalancer", "cluster", cl, "name", n)
if err == cluster.ErrKeyNotFound {
ctx.Err(types.ErrNotFound)
return
}
ctx.Err(types.ErrISE)
return
}
ctx.Body(b)
case "POST":
b, err := ctx.ReadBody()
if err != nil {
ctx.Log.Error(err, "error reading request body", "cluster", cl, "name", n)
ctx.Err(types.ErrISE)
return
}
var lb schema.LoadBalancer
err = json.Unmarshal(b, &lb)
if err != nil {
ctx.Log.Error(err, "error decoding request body", "cluster", cl, "name", n)
ctx.Err(types.ErrInvalid)
return
}
lb.Name = lbname(cl, n)
cidr, err := ctx.Data.DB.GetIP(name)
if err != nil && err != cluster.ErrKeyNotFound {
ctx.Log.Error(err, "error getting existing loadbalancer ip", "cluster", cl, "name", n)
ctx.Err(types.ErrISE)
return
}
if cidr != "" {
lb.IP, err = getIP(cidr)
if err != nil {
ctx.Log.Error(err, "error parsing existing ip", "cluster", cl, "name", n, "cidr", cidr)
ctx.Err(types.ErrISE)
return
}
} else {
lb.IP, err = ctx.Data.Alloc.AllocIP(context.Background(), name)
if err != nil {
ctx.Log.Error(err, "error allocating ip for loadbalancer", "cluster", cl, "name", n)
ctx.Err(types.ErrISE)
return
}
}
err = lb.ValidateServer()
if err != nil {
ctx.Log.Error(err, "error validating loadbalancer", "cluster", cl, "name", n)
ctx.Err(types.ErrInvalid)
return
}
b, err = lb.JSON()
if err != nil {
ctx.Log.Error(err, "error encoding loadbalancer", "cluster", cl, "name", n)
ctx.Err(types.ErrISE)
return
}
err = ctx.Data.DB.SetLB(name, b)
if err != nil {
ctx.Log.Error(err, "error writing loadbalancer", "cluster", cl, "name", n)
ctx.Err(types.ErrISE)
return
}
ctx.OK()
case "DELETE":
if !ctx.Data.DB.LBExists(name) {
ctx.Err(types.ErrNotFound)
return
}
err := ctx.Data.Alloc.FreeIP(context.Background(), name)
if err != nil && err != cluster.ErrKeyNotFound {
ctx.Log.Error(err, "error freeing IP of loadbalancer", "cluster", cl, "name", n)
ctx.Err(types.ErrISE)
return
}
err = ctx.Data.DB.DeleteLB(name)
if err != nil {
ctx.Log.Error(err, "error deleting loadbalancer", "cluster", cl, "name", n)
ctx.Err(types.ErrISE)
return
}
ctx.OK()
}
}
func lbList(lbs map[string][]byte) (m map[string]json.RawMessage) {
m = make(map[string]json.RawMessage)
for k, v := range lbs {
m[k] = json.RawMessage(v)
}
return
}
func lbClusterListHandler(ctx *types.Context) {
cl := ctx.Var("cluster")
m, err := ctx.Data.DB.GetLBs(cl)
if err != nil {
ctx.Log.Error(err, "error reading loadbalancers")
ctx.Err(types.ErrISE)
return
}
ctx.JSON(lbList(m))
}
func lbListHandler(ctx *types.Context) {
m, err := ctx.Data.DB.GetLBs("")
if err != nil {
ctx.Log.Error(err, "error reading loadbalancers")
ctx.Err(types.ErrISE)
return
}
ctx.JSON(lbList(m))
}