2024-12-08 01:30:07 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"log"
|
|
|
|
"net/http"
|
2024-12-19 20:10:34 +00:00
|
|
|
"strings"
|
2024-12-08 01:30:07 +00:00
|
|
|
|
|
|
|
_ "gitea.ceperka.net/rosti/lobby2/docs" // This line is necessary for swag to find your docs!
|
|
|
|
"gitea.ceperka.net/rosti/lobby2/nodes"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
|
|
"github.com/labstack/echo/v4/middleware"
|
|
|
|
echoSwagger "github.com/swaggo/echo-swagger"
|
|
|
|
)
|
|
|
|
|
|
|
|
// @title Lobby2 API
|
|
|
|
// @version 2.0
|
|
|
|
// @description API of Lobby 2 project that helps to discover and connect to other nodes and their services.
|
|
|
|
// @BasePath /
|
|
|
|
// @securityDefinitions.apikey Bearer
|
|
|
|
// @in header
|
|
|
|
// @name Authorization
|
|
|
|
|
|
|
|
type API struct {
|
|
|
|
listen string
|
|
|
|
token string
|
|
|
|
np *nodes.NodesProcessor
|
|
|
|
|
|
|
|
e *echo.Echo
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewAPI(np *nodes.NodesProcessor, listen string, token string) *API {
|
|
|
|
return &API{
|
|
|
|
listen: listen,
|
|
|
|
token: token,
|
|
|
|
np: np,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *API) Run() error {
|
|
|
|
if a.token == "" {
|
|
|
|
log.Fatalln("TOKEN is required")
|
|
|
|
}
|
|
|
|
|
|
|
|
a.e = echo.New()
|
|
|
|
|
|
|
|
a.e.Use(middleware.Logger())
|
|
|
|
a.e.Use(tokenMiddlware(a.token))
|
|
|
|
|
|
|
|
a.e.GET("/", func(c echo.Context) error {
|
|
|
|
return c.Redirect(http.StatusTemporaryRedirect, "/swagger/index.html")
|
|
|
|
})
|
|
|
|
a.e.GET("/swagger/*", echoSwagger.WrapHandler)
|
|
|
|
|
|
|
|
a.e.GET("/nodes", a.listHandler)
|
|
|
|
a.e.GET("/nodes/:hostname", a.getHandler)
|
|
|
|
a.e.POST("/nodes/:hostname", a.refreshHandler)
|
2024-12-19 20:10:34 +00:00
|
|
|
a.e.GET("/prometheus/:service", a.prometheusHandler)
|
2024-12-08 01:30:07 +00:00
|
|
|
|
|
|
|
// Start the server in a goroutine so that it doesn't block the signal listening
|
|
|
|
return a.e.Start(a.listen)
|
|
|
|
}
|
|
|
|
|
|
|
|
// @Summary List of nodes
|
|
|
|
// @Description List of all discovered nodes and their labels.
|
|
|
|
// @Produce application/json
|
|
|
|
// @Success 200 {object} nodes.Nodes "List of nodes"
|
|
|
|
// @Failure 401 {object} Message "Forbidden access"
|
|
|
|
// @Security Bearer
|
|
|
|
// @Router /nodes [get]
|
|
|
|
func (a *API) listHandler(c echo.Context) error {
|
|
|
|
return c.JSON(http.StatusOK, a.np.List())
|
|
|
|
}
|
|
|
|
|
|
|
|
// @Summary Get node
|
|
|
|
// @Description Return one nodes based on given hostname
|
|
|
|
// @Produce application/json
|
|
|
|
// @Param hostname path string true "Node hostname"
|
|
|
|
// @Success 200 {array} nodes.Node "Node details"
|
|
|
|
// @Failure 401 {object} Message "Forbidden access"
|
|
|
|
// @Security Bearer
|
|
|
|
// @Router /nodes/{hostname} [get]
|
|
|
|
func (a *API) getHandler(c echo.Context) error {
|
|
|
|
hostname := c.Param("hostname")
|
|
|
|
|
|
|
|
node, ok := a.np.Get(hostname)
|
|
|
|
if !ok {
|
|
|
|
return echo.NewHTTPError(http.StatusNotFound, "node not found")
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.JSON(http.StatusOK, node)
|
|
|
|
}
|
|
|
|
|
|
|
|
// @Summary Refresh node
|
|
|
|
// @Description Send new data or update existing data about a node
|
|
|
|
// @Produce application/json
|
|
|
|
// @Param hostname path string true "Node hostname"
|
|
|
|
// @Param labels body nodes.Labels true "Node labels"
|
|
|
|
// @Param kv body nodes.KV true "Key-value"
|
|
|
|
// @Success 200 {array} nodes.Node "Node details"
|
|
|
|
// @Failure 401 {object} Message "Forbidden access"
|
|
|
|
// @Security Bearer
|
|
|
|
// @Router /nodes/{hostname} [post]
|
|
|
|
func (a *API) refreshHandler(c echo.Context) error {
|
|
|
|
hostname := c.Param("hostname")
|
|
|
|
|
|
|
|
params := params{}
|
|
|
|
err := c.Bind(¶ms)
|
|
|
|
if err != nil {
|
|
|
|
return c.JSON(http.StatusBadRequest, Message{Message: err.Error()})
|
|
|
|
}
|
|
|
|
|
|
|
|
a.np.Refresh(hostname, params.Labels, params.KV)
|
|
|
|
|
|
|
|
return c.NoContent(http.StatusNoContent)
|
|
|
|
}
|
2024-12-19 20:10:34 +00:00
|
|
|
|
|
|
|
// @Summary Prometheus service discovery
|
|
|
|
// @Description Return one nodes based on given hostname
|
|
|
|
// @Produce application/json
|
|
|
|
// @Success 200 {array} []prometheusDiscovery "Node details"
|
|
|
|
// @Failure 401 {object} Message "Forbidden access"
|
|
|
|
// @Security Bearer
|
|
|
|
// @Router /nodes/{hostname} [get]
|
|
|
|
func (a *API) prometheusHandler(c echo.Context) error {
|
|
|
|
ss := c.QueryParam("service")
|
|
|
|
|
|
|
|
ns := a.np.List()
|
|
|
|
|
|
|
|
pds := []prometheusDiscovery{
|
|
|
|
{
|
|
|
|
Labels: nodes.Labels{},
|
|
|
|
Targets: []string{},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, node := range ns {
|
|
|
|
v, ok := node.KV["prometheus_exporters"]
|
|
|
|
if ok {
|
|
|
|
services := strings.Split(v, ",")
|
|
|
|
for _, service := range services {
|
|
|
|
if ss == service {
|
|
|
|
pds[0].Targets = append(pds[0].Targets, node.HostName)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.JSONPretty(http.StatusOK, pds, " ")
|
|
|
|
}
|