major overhaul, including:
Some checks failed
continuous-integration/drone/push Build is failing

* builds with echo now instead of gin
* beginning of web UI
* factor out util functions
This commit is contained in:
2020-03-30 16:05:53 -07:00
parent 9655265d85
commit 06df947186
17 changed files with 447 additions and 113 deletions

View File

@@ -83,6 +83,7 @@ func (f *Feta) identify() {
}
func (f *Feta) setupDatabase() {
f.dbm = database.New()
}
func (f *Feta) setupLogging() {
@@ -121,14 +122,10 @@ func (f *Feta) uptime() time.Duration {
func (f *Feta) runForever() int {
f.startup = time.Now()
//f.setupDatabase()
// FIXME move this channel creation into the manager's constructor
// and add getters/setters on the manager/locator
newInstanceHostnameNotifications := make(chan string)
f.dbm = database.New()
f.locator = locator.New()
f.manager = manager.New(f.dbm)
@@ -140,6 +137,7 @@ func (f *Feta) runForever() int {
panic("can't find home directory")
}
// TODO make the ingester support multiple storage backends simultaneously
if viper.GetBool("TootsToDB") {
f.ingester.SetStorageBackend(f.dbm)
} else if viper.GetBool("TootsToDisk") {

View File

@@ -1,14 +1,16 @@
package process
import (
"encoding/json"
"fmt"
"net/http"
"runtime"
"strings"
"time"
u "git.eeqj.de/sneak/goutil"
"github.com/flosch/pongo2"
"github.com/gin-gonic/gin"
"github.com/labstack/echo"
)
type hash map[string]interface{}
@@ -26,7 +28,7 @@ func (a *Server) instances() []hash {
i["successCount"] = v.SuccessCount
i["errorCount"] = v.ErrorCount
i["identified"] = v.Identified
i["status"] = v.Status()
i["status"] = v.Status() //FIXME maybe this is why
i["software"] = "unknown"
i["version"] = "unknown"
if v.Identified {
@@ -45,14 +47,15 @@ func (a *Server) instanceSummary() map[string]int {
v.Lock()
resp[fmt.Sprintf("STATUS_%s", v.Status())]++
if v.ServerImplementationString != "" {
//FIXME(sneak) sanitize this to a-z0-9, it is server-provided
resp[fmt.Sprintf("SOFTWARE_%s", strings.ToUpper(v.ServerImplementationString))]++
impl := strings.ToUpper(u.FilterToAlnum(v.ServerImplementationString))
resp[fmt.Sprintf("SOFTWARE_%s", impl)]++
}
v.Unlock()
}
return resp
}
/*
func (a *Server) getInstanceListHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
@@ -70,46 +73,38 @@ func (a *Server) getInstanceListHandler() http.HandlerFunc {
w.Write(json)
}
}
*/
func (a *Server) getIndexHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
index := &gin.H{
"server": &gin.H{
"now": time.Now().UTC().Format(time.RFC3339),
"uptime": a.feta.uptime().String(),
"goroutines": runtime.NumGoroutine(),
"goversion": runtime.Version(),
"version": a.feta.version,
"buildarch": a.feta.buildarch,
},
"instanceSummary": a.instanceSummary(),
}
json, err := json.Marshal(index)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(json)
func (a *Server) indexHandler(c echo.Context) error {
tc := pongo2.Context{
"time": time.Now().UTC().Format(time.RFC3339Nano),
"gitrev": a.feta.version,
}
return c.Render(http.StatusOK, "index.html", tc)
}
func (a *Server) getHealthCheckHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
resp := &gin.H{
"status": "ok",
"now": time.Now().UTC().Format(time.RFC3339),
"uptime": a.feta.uptime().String(),
}
json, err := json.Marshal(resp)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(json)
func (a *Server) statsHandler(c echo.Context) error {
index := &gin.H{
"server": &gin.H{
"now": time.Now().UTC().Format(time.RFC3339),
"uptime": a.feta.uptime().String(),
"goroutines": runtime.NumGoroutine(),
"goversion": runtime.Version(),
"version": a.feta.version,
"buildarch": a.feta.buildarch,
},
"instanceSummary": a.instanceSummary(),
}
return c.JSONPretty(http.StatusOK, index, " ")
}
func (a *Server) healthCheckHandler(c echo.Context) error {
resp := &gin.H{
"status": "ok",
"now": time.Now().UTC().Format(time.RFC3339),
"uptime": a.feta.uptime().String(),
}
return c.JSONPretty(http.StatusOK, resp, " ")
}

View File

@@ -1,22 +1,33 @@
package process
import "fmt"
import "net/http"
import "os"
import "strconv"
import "time"
import (
"fmt"
"net/http"
"os"
"strconv"
"time"
import "github.com/rs/zerolog/log"
import "github.com/gin-gonic/gin"
import "github.com/dn365/gin-zerolog"
"github.com/gin-gonic/gin"
"github.com/labstack/echo"
"github.com/labstack/echo/middleware"
gl "github.com/labstack/gommon/log"
ep2 "github.com/mayowa/echo-pongo2"
"github.com/ziflex/lecho"
//"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
"github.com/rs/zerolog/log"
)
// Server is the HTTP webserver object
type Server struct {
feta *Feta
port uint
router *gin.Engine
server *http.Server
debug bool
feta *Feta
port uint
e *echo.Echo
router *gin.Engine
httpserver *http.Server
debug bool
db *gorm.DB
}
// SetFeta tells the http Server where to find the Process object so that it
@@ -28,6 +39,7 @@ func (a *Server) SetFeta(feta *Feta) {
// Serve is the entrypoint for the Server, which should run in its own
// goroutine (started by the Process)
func (a *Server) Serve() {
if a.feta == nil {
panic("must have feta app from which to serve stats")
}
@@ -47,43 +59,53 @@ func (a *Server) Serve() {
a.initRouter()
a.initServer()
err := a.server.ListenAndServe()
a.e.Logger.Fatal(a.e.StartServer(a.httpserver))
}
func (s *Server) initRouter() {
// Echo instance
s.e = echo.New()
s.e.HideBanner = true
lev := gl.INFO
if os.Getenv("DEBUG") != "" {
lev = gl.DEBUG
}
logger := lecho.New(
os.Stdout,
lecho.WithLevel(lev),
lecho.WithTimestamp(),
lecho.WithCaller(),
)
s.e.Logger = logger
s.e.Use(middleware.RequestID())
// Middleware
s.e.Use(middleware.Logger())
s.e.Use(middleware.Recover())
r, err := ep2.NewRenderer("view")
if err != nil {
log.Fatal().Err(err).Msg("webserver failure")
return
s.e.Logger.Fatal(err)
}
s.e.Renderer = r
// Routes
s.e.GET("/", s.indexHandler)
s.e.GET("/stats.json", s.statsHandler)
s.e.GET("/.well-known/healthcheck.json", s.healthCheckHandler)
//a.e.GET("/about", s.aboutHandler)
}
func (a *Server) initRouter() {
func (s *Server) initServer() {
log.Info().Uint("port", s.port).Msg("starting webserver")
// empty router
r := gin.New()
// wrap panics:
r.Use(gin.Recovery())
// attach logger middleware
r.Use(ginzerolog.Logger("gin"))
r.GET("/.well-known/healthcheck.json", gin.WrapF(a.getHealthCheckHandler()))
r.GET("/", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "/feta") })
r.GET("/feta", gin.WrapF(a.getIndexHandler()))
r.GET("/feta/list/instances", gin.WrapF(a.getInstanceListHandler()))
a.router = r
}
func (a *Server) initServer() {
if !a.debug {
gin.SetMode(gin.ReleaseMode)
}
log.Info().Uint("port", a.port).Msg("starting webserver")
a.server = &http.Server{
Addr: fmt.Sprintf(":%d", a.port),
Handler: a.router,
s.httpserver = &http.Server{
Addr: fmt.Sprintf(":%d", s.port),
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,