package process import ( "fmt" "net/http" "runtime" "strings" "time" u "git.eeqj.de/sneak/goutil" "github.com/flosch/pongo2" "github.com/google/uuid" "github.com/labstack/echo" ) type hash map[string]interface{} func (a *Server) instances() []hash { resp := make([]hash, 0) now := time.Now() for _, v := range a.feta.manager.ListInstances() { i := make(hash) // TODO move this locking onto a method on Instance that just // returns a new hash //this only locks the FSM, not the whole instance struct i["status"] = v.Status() // now do a quick lock of the whole instance just to copy out the // attrs v.Lock() i["hostname"] = v.Hostname i["uuid"] = v.UUID.String() i["nextCheck"] = v.NextFetch.UTC().Format(time.RFC3339) i["nextCheckAfter"] = (-1 * now.Sub(v.NextFetch)).String() i["successCount"] = v.SuccessCount i["errorCount"] = v.ErrorCount i["consecutiveErrorCount"] = v.ConsecutiveErrorCount i["identified"] = v.Identified i["software"] = "unknown" i["version"] = "unknown" if v.Identified { i["software"] = v.ServerImplementationString i["version"] = v.ServerVersionString } v.Unlock() resp = append(resp, i) } for _, item := range resp { count, err := a.feta.dbm.TootCountForHostname(item["hostname"].(string)) item["tootCount"] = 0 if err != nil { item["tootCount"] = count } } return resp } func (a *Server) instanceStatusSummary() map[string]int { resp := make(map[string]int) for _, v := range a.feta.manager.ListInstances() { v.Lock() resp[fmt.Sprintf("STATUS_%s", v.Status())]++ if v.ServerImplementationString != "" { impl := strings.ToUpper(u.FilterToAlnum(v.ServerImplementationString)) resp[fmt.Sprintf("SOFTWARE_%s", impl)]++ } v.Unlock() } return resp } func (a *Server) notFoundHandler(c echo.Context) error { return c.String(http.StatusNotFound, "404 not found") } func (a *Server) instanceHandler(c echo.Context) error { tu := c.Param("uuid") u, err := uuid.Parse(tu) if err != nil { return a.notFoundHandler(c) } tc := pongo2.Context{} instances := a.feta.manager.ListInstances() found := false for _, item := range instances { if item.UUID == u { tc["instance"] = item found = true } } if !found { return a.notFoundHandler(c) } return c.Render(http.StatusOK, "instance.html", tc) } func (a *Server) indexHandler(c echo.Context) error { count, err := a.feta.dbm.TotalTootCount() if err != nil { count = 0 } tc := pongo2.Context{ "time": time.Now().UTC().Format(time.RFC3339Nano), "gitrev": a.feta.version, "tootCount": count, "instances": a.instances(), "instanceStatusSummary": a.instanceStatusSummary(), } return c.Render(http.StatusOK, "index.html", tc) } func (a *Server) instanceListHandler(c echo.Context) error { il := a.instances() tc := pongo2.Context{ "time": time.Now().UTC().Format(time.RFC3339Nano), "gitrev": a.feta.version, "instances": il, } return c.Render(http.StatusOK, "instancelist.html", tc) } func (a *Server) statsHandler(c echo.Context) error { index := &hash{ "server": &hash{ "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, }, "instanceStatusSummary": a.instanceStatusSummary(), } return c.JSONPretty(http.StatusOK, index, " ") } func (a *Server) healthCheckHandler(c echo.Context) error { resp := &hash{ "status": "ok", "now": time.Now().UTC().Format(time.RFC3339), "uptime": a.feta.uptime().String(), } return c.JSONPretty(http.StatusOK, resp, " ") }