mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-12-26 13:29:05 +01:00
Cleanup and restructure
This commit is contained in:
parent
4cccbb20d8
commit
ad705f1424
77
routes.go
77
routes.go
@ -2,10 +2,13 @@ package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ClusterCockpit/cc-backend/auth"
|
||||
"github.com/ClusterCockpit/cc-backend/config"
|
||||
"github.com/ClusterCockpit/cc-backend/schema"
|
||||
"github.com/ClusterCockpit/cc-backend/templates"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
@ -20,10 +23,59 @@ type Route struct {
|
||||
Setup func(i InfoType, r *http.Request) InfoType
|
||||
}
|
||||
|
||||
func buildFilterPresets(query url.Values) map[string]interface{} {
|
||||
filterPresets := map[string]interface{}{}
|
||||
|
||||
if query.Get("cluster") != "" {
|
||||
filterPresets["cluster"] = query.Get("cluster")
|
||||
}
|
||||
if query.Get("partition") != "" {
|
||||
filterPresets["partition"] = query.Get("partition")
|
||||
}
|
||||
if query.Get("project") != "" {
|
||||
filterPresets["project"] = query.Get("project")
|
||||
filterPresets["projectMatch"] = "eq"
|
||||
}
|
||||
if query.Get("state") != "" && schema.JobState(query.Get("state")).Valid() {
|
||||
filterPresets["state"] = query.Get("state")
|
||||
}
|
||||
if rawtags, ok := query["tag"]; ok {
|
||||
tags := make([]int, len(rawtags))
|
||||
for i, tid := range rawtags {
|
||||
var err error
|
||||
tags[i], err = strconv.Atoi(tid)
|
||||
if err != nil {
|
||||
tags[i] = -1
|
||||
}
|
||||
}
|
||||
filterPresets["tags"] = tags
|
||||
}
|
||||
if query.Get("numNodes") != "" {
|
||||
parts := strings.Split(query.Get("numNodes"), "-")
|
||||
if len(parts) == 2 {
|
||||
a, e1 := strconv.Atoi(parts[0])
|
||||
b, e2 := strconv.Atoi(parts[1])
|
||||
if e1 == nil && e2 == nil {
|
||||
filterPresets["numNodes"] = map[string]int{"from": a, "to": b}
|
||||
}
|
||||
}
|
||||
}
|
||||
if query.Get("jobId") != "" {
|
||||
filterPresets["jobId"] = query.Get("jobId")
|
||||
}
|
||||
if query.Get("arrayJobId") != "" {
|
||||
if num, err := strconv.Atoi(query.Get("arrayJobId")); err == nil {
|
||||
filterPresets["arrayJobId"] = num
|
||||
}
|
||||
}
|
||||
|
||||
return filterPresets
|
||||
}
|
||||
|
||||
func setupRoutes(router *mux.Router, routes []Route) {
|
||||
for _, route := range routes {
|
||||
_route := route
|
||||
router.HandleFunc(_route.Route, func(rw http.ResponseWriter, r *http.Request) {
|
||||
route := route
|
||||
router.HandleFunc(route.Route, func(rw http.ResponseWriter, r *http.Request) {
|
||||
conf, err := config.GetUIConfig(r)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
@ -42,17 +94,22 @@ func setupRoutes(router *mux.Router, routes []Route) {
|
||||
infos["admin"] = false
|
||||
}
|
||||
|
||||
infos = _route.Setup(infos, r)
|
||||
infos = route.Setup(infos, r)
|
||||
if id, ok := infos["id"]; ok {
|
||||
_route.Title = strings.Replace(_route.Title, "<ID>", id.(string), 1)
|
||||
route.Title = strings.Replace(route.Title, "<ID>", id.(string), 1)
|
||||
}
|
||||
|
||||
templates.Render(rw, r, _route.Template, &templates.Page{
|
||||
Title: _route.Title,
|
||||
Config: conf,
|
||||
Infos: infos,
|
||||
FilterPresets: buildFilterPresets(r.URL.Query()),
|
||||
})
|
||||
page := templates.Page{
|
||||
Title: route.Title,
|
||||
Config: conf,
|
||||
Infos: infos,
|
||||
}
|
||||
|
||||
if route.Filter {
|
||||
page.FilterPresets = buildFilterPresets(r.URL.Query())
|
||||
}
|
||||
|
||||
templates.Render(rw, r, route.Template, &page)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
124
runtimeSetup.go
Normal file
124
runtimeSetup.go
Normal file
@ -0,0 +1,124 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func loadEnv(file string) error {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
s := bufio.NewScanner(bufio.NewReader(f))
|
||||
for s.Scan() {
|
||||
line := s.Text()
|
||||
if strings.HasPrefix(line, "#") || len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(line, "#") {
|
||||
return errors.New("'#' are only supported at the start of a line")
|
||||
}
|
||||
|
||||
line = strings.TrimPrefix(line, "export ")
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("unsupported line: %#v", line)
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(parts[0])
|
||||
val := strings.TrimSpace(parts[1])
|
||||
if strings.HasPrefix(val, "\"") {
|
||||
if !strings.HasSuffix(val, "\"") {
|
||||
return fmt.Errorf("unsupported line: %#v", line)
|
||||
}
|
||||
|
||||
runes := []rune(val[1 : len(val)-1])
|
||||
sb := strings.Builder{}
|
||||
for i := 0; i < len(runes); i++ {
|
||||
if runes[i] == '\\' {
|
||||
i++
|
||||
switch runes[i] {
|
||||
case 'n':
|
||||
sb.WriteRune('\n')
|
||||
case 'r':
|
||||
sb.WriteRune('\r')
|
||||
case 't':
|
||||
sb.WriteRune('\t')
|
||||
case '"':
|
||||
sb.WriteRune('"')
|
||||
default:
|
||||
return fmt.Errorf("unsupprorted escape sequence in quoted string: backslash %#v", runes[i])
|
||||
}
|
||||
continue
|
||||
}
|
||||
sb.WriteRune(runes[i])
|
||||
}
|
||||
|
||||
val = sb.String()
|
||||
}
|
||||
|
||||
os.Setenv(key, val)
|
||||
}
|
||||
|
||||
return s.Err()
|
||||
}
|
||||
|
||||
func dropPrivileges() error {
|
||||
if programConfig.Group != "" {
|
||||
g, err := user.LookupGroup(programConfig.Group)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gid, _ := strconv.Atoi(g.Gid)
|
||||
if err := syscall.Setgid(gid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if programConfig.User != "" {
|
||||
u, err := user.Lookup(programConfig.User)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uid, _ := strconv.Atoi(u.Uid)
|
||||
if err := syscall.Setuid(uid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// If started via systemd, inform systemd that we are running:
|
||||
// https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
||||
func systemdNotifiy(ready bool, status string) {
|
||||
if os.Getenv("NOTIFY_SOCKET") == "" {
|
||||
// Not started using systemd
|
||||
return
|
||||
}
|
||||
|
||||
args := []string{fmt.Sprintf("--pid=%d", os.Getpid())}
|
||||
if ready {
|
||||
args = append(args, "--ready")
|
||||
}
|
||||
|
||||
if status != "" {
|
||||
args = append(args, fmt.Sprintf("--status=%s", status))
|
||||
}
|
||||
|
||||
cmd := exec.Command("systemd-notify", args...)
|
||||
cmd.Run() // errors ignored on purpose, there is not much to do anyways.
|
||||
}
|
313
server.go
313
server.go
@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
@ -11,12 +10,8 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"os/user"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
@ -31,7 +26,6 @@ import (
|
||||
"github.com/ClusterCockpit/cc-backend/graph/generated"
|
||||
"github.com/ClusterCockpit/cc-backend/log"
|
||||
"github.com/ClusterCockpit/cc-backend/metricdata"
|
||||
"github.com/ClusterCockpit/cc-backend/schema"
|
||||
"github.com/ClusterCockpit/cc-backend/templates"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
@ -455,310 +449,3 @@ func main() {
|
||||
wg.Wait()
|
||||
log.Print("Gracefull shutdown completed!")
|
||||
}
|
||||
|
||||
func buildFilterPresets(query url.Values) map[string]interface{} {
|
||||
filterPresets := map[string]interface{}{}
|
||||
|
||||
if query.Get("cluster") != "" {
|
||||
filterPresets["cluster"] = query.Get("cluster")
|
||||
}
|
||||
if query.Get("partition") != "" {
|
||||
filterPresets["partition"] = query.Get("partition")
|
||||
}
|
||||
if query.Get("project") != "" {
|
||||
filterPresets["project"] = query.Get("project")
|
||||
filterPresets["projectMatch"] = "eq"
|
||||
}
|
||||
if query.Get("state") != "" && schema.JobState(query.Get("state")).Valid() {
|
||||
filterPresets["state"] = query.Get("state")
|
||||
}
|
||||
if rawtags, ok := query["tag"]; ok {
|
||||
tags := make([]int, len(rawtags))
|
||||
for i, tid := range rawtags {
|
||||
var err error
|
||||
tags[i], err = strconv.Atoi(tid)
|
||||
if err != nil {
|
||||
tags[i] = -1
|
||||
}
|
||||
}
|
||||
filterPresets["tags"] = tags
|
||||
}
|
||||
if query.Get("numNodes") != "" {
|
||||
parts := strings.Split(query.Get("numNodes"), "-")
|
||||
if len(parts) == 2 {
|
||||
a, e1 := strconv.Atoi(parts[0])
|
||||
b, e2 := strconv.Atoi(parts[1])
|
||||
if e1 == nil && e2 == nil {
|
||||
filterPresets["numNodes"] = map[string]int{"from": a, "to": b}
|
||||
}
|
||||
}
|
||||
}
|
||||
if query.Get("jobId") != "" {
|
||||
filterPresets["jobId"] = query.Get("jobId")
|
||||
}
|
||||
if query.Get("arrayJobId") != "" {
|
||||
if num, err := strconv.Atoi(query.Get("arrayJobId")); err == nil {
|
||||
filterPresets["arrayJobId"] = num
|
||||
}
|
||||
}
|
||||
|
||||
return filterPresets
|
||||
}
|
||||
|
||||
// func monitoringRoutes(router *mux.Router, resolver *graph.Resolver) {
|
||||
|
||||
// router.HandleFunc("/monitoring/jobs/", func(rw http.ResponseWriter, r *http.Request) {
|
||||
// conf, infos, err := prepareRoute(r)
|
||||
// if err != nil {
|
||||
// http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
|
||||
// templates.Render(rw, r, "monitoring/jobs.tmpl", &templates.Page{
|
||||
// Title: "Jobs - ClusterCockpit",
|
||||
// Config: conf,
|
||||
// Infos: infos,
|
||||
// FilterPresets: buildFilterPresets(r.URL.Query()),
|
||||
// })
|
||||
// })
|
||||
|
||||
// router.HandleFunc("/monitoring/job/{id:[0-9]+}", func(rw http.ResponseWriter, r *http.Request) {
|
||||
// conf, infos, err := prepareRoute(r)
|
||||
// if err != nil {
|
||||
// http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
|
||||
// id := mux.Vars(r)["id"]
|
||||
// job, err := resolver.Query().Job(r.Context(), id)
|
||||
// if err != nil {
|
||||
// http.Error(rw, err.Error(), http.StatusNotFound)
|
||||
// return
|
||||
// }
|
||||
|
||||
// infos["id"] = id
|
||||
// infos["jobId"] = job.JobID
|
||||
// infos["clusterId"] = job.Cluster
|
||||
|
||||
// templates.Render(rw, r, "monitoring/job.tmpl", &templates.Page{
|
||||
// Title: fmt.Sprintf("Job %d - ClusterCockpit", job.JobID),
|
||||
// Config: conf,
|
||||
// Infos: infos,
|
||||
// })
|
||||
// })
|
||||
|
||||
// router.HandleFunc("/monitoring/users/", func(rw http.ResponseWriter, r *http.Request) {
|
||||
// conf, infos, err := prepareRoute(r)
|
||||
// if err != nil {
|
||||
// http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
|
||||
// infos["listType"] = "USER"
|
||||
|
||||
// templates.Render(rw, r, "monitoring/list.tmpl", &templates.Page{
|
||||
// Title: "Users - ClusterCockpit",
|
||||
// Config: conf,
|
||||
// FilterPresets: buildFilterPresets(r.URL.Query()),
|
||||
// Infos: infos,
|
||||
// })
|
||||
// })
|
||||
|
||||
// router.HandleFunc("/monitoring/projects/", func(rw http.ResponseWriter, r *http.Request) {
|
||||
// conf, infos, err := prepareRoute(r)
|
||||
// if err != nil {
|
||||
// http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
|
||||
// infos["listType"] = "PROJECT"
|
||||
|
||||
// templates.Render(rw, r, "monitoring/list.tmpl", &templates.Page{
|
||||
// Title: "Projects - ClusterCockpit",
|
||||
// Config: conf,
|
||||
// FilterPresets: buildFilterPresets(r.URL.Query()),
|
||||
// Infos: infos,
|
||||
// })
|
||||
// })
|
||||
|
||||
// router.HandleFunc("/monitoring/user/{id}", func(rw http.ResponseWriter, r *http.Request) {
|
||||
// conf, infos, err := prepareRoute(r)
|
||||
// if err != nil {
|
||||
// http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
|
||||
// id := mux.Vars(r)["id"]
|
||||
// // TODO: One could check if the user exists, but that would be unhelpfull if authentication
|
||||
// // is disabled or the user does not exist but has started jobs.
|
||||
// infos["username"] = id
|
||||
|
||||
// templates.Render(rw, r, "monitoring/user.tmpl", &templates.Page{
|
||||
// Title: fmt.Sprintf("User %s - ClusterCockpit", id),
|
||||
// Config: conf,
|
||||
// Infos: infos,
|
||||
// FilterPresets: buildFilterPresets(r.URL.Query()),
|
||||
// })
|
||||
// })
|
||||
|
||||
// router.HandleFunc("/monitoring/systems/", func(rw http.ResponseWriter, r *http.Request) {
|
||||
// // TODO: List all clusters?
|
||||
// http.Redirect(rw, r, "/", http.StatusTemporaryRedirect)
|
||||
// })
|
||||
|
||||
// router.HandleFunc("/monitoring/systems/{cluster}", func(rw http.ResponseWriter, r *http.Request) {
|
||||
// conf, infos, err := prepareRoute(r)
|
||||
// if err != nil {
|
||||
// http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
|
||||
// vars := mux.Vars(r)
|
||||
// infos["cluster"] = vars["cluster"]
|
||||
// from, to := r.URL.Query().Get("from"), r.URL.Query().Get("to")
|
||||
// if from != "" || to != "" {
|
||||
// infos["from"] = from
|
||||
// infos["to"] = to
|
||||
// }
|
||||
|
||||
// templates.Render(rw, r, "monitoring/systems.tmpl", &templates.Page{
|
||||
// Title: fmt.Sprintf("Cluster %s - ClusterCockpit", vars["cluster"]),
|
||||
// Config: conf,
|
||||
// Infos: infos,
|
||||
// })
|
||||
// })
|
||||
|
||||
// router.HandleFunc("/monitoring/node/{cluster}/{hostname}", func(rw http.ResponseWriter, r *http.Request) {
|
||||
// conf, infos, err := prepareRoute(r)
|
||||
// if err != nil {
|
||||
// http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
// return
|
||||
// }
|
||||
|
||||
// vars := mux.Vars(r)
|
||||
// infos["cluster"] = vars["cluster"]
|
||||
// infos["hostname"] = vars["hostname"]
|
||||
// from, to := r.URL.Query().Get("from"), r.URL.Query().Get("to")
|
||||
// if from != "" || to != "" {
|
||||
// infos["from"] = from
|
||||
// infos["to"] = to
|
||||
// }
|
||||
|
||||
// templates.Render(rw, r, "monitoring/node.tmpl", &templates.Page{
|
||||
// Title: fmt.Sprintf("Host %s - ClusterCockpit", vars["hostname"]),
|
||||
// Config: conf,
|
||||
// Infos: infos,
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
|
||||
func loadEnv(file string) error {
|
||||
f, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
s := bufio.NewScanner(bufio.NewReader(f))
|
||||
for s.Scan() {
|
||||
line := s.Text()
|
||||
if strings.HasPrefix(line, "#") || len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(line, "#") {
|
||||
return errors.New("'#' are only supported at the start of a line")
|
||||
}
|
||||
|
||||
line = strings.TrimPrefix(line, "export ")
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("unsupported line: %#v", line)
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(parts[0])
|
||||
val := strings.TrimSpace(parts[1])
|
||||
if strings.HasPrefix(val, "\"") {
|
||||
if !strings.HasSuffix(val, "\"") {
|
||||
return fmt.Errorf("unsupported line: %#v", line)
|
||||
}
|
||||
|
||||
runes := []rune(val[1 : len(val)-1])
|
||||
sb := strings.Builder{}
|
||||
for i := 0; i < len(runes); i++ {
|
||||
if runes[i] == '\\' {
|
||||
i++
|
||||
switch runes[i] {
|
||||
case 'n':
|
||||
sb.WriteRune('\n')
|
||||
case 'r':
|
||||
sb.WriteRune('\r')
|
||||
case 't':
|
||||
sb.WriteRune('\t')
|
||||
case '"':
|
||||
sb.WriteRune('"')
|
||||
default:
|
||||
return fmt.Errorf("unsupprorted escape sequence in quoted string: backslash %#v", runes[i])
|
||||
}
|
||||
continue
|
||||
}
|
||||
sb.WriteRune(runes[i])
|
||||
}
|
||||
|
||||
val = sb.String()
|
||||
}
|
||||
|
||||
os.Setenv(key, val)
|
||||
}
|
||||
|
||||
return s.Err()
|
||||
}
|
||||
|
||||
func dropPrivileges() error {
|
||||
if programConfig.Group != "" {
|
||||
g, err := user.LookupGroup(programConfig.Group)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gid, _ := strconv.Atoi(g.Gid)
|
||||
if err := syscall.Setgid(gid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if programConfig.User != "" {
|
||||
u, err := user.Lookup(programConfig.User)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uid, _ := strconv.Atoi(u.Uid)
|
||||
if err := syscall.Setuid(uid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// If started via systemd, inform systemd that we are running:
|
||||
// https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
||||
func systemdNotifiy(ready bool, status string) {
|
||||
if os.Getenv("NOTIFY_SOCKET") == "" {
|
||||
// Not started using systemd
|
||||
return
|
||||
}
|
||||
|
||||
args := []string{fmt.Sprintf("--pid=%d", os.Getpid())}
|
||||
if ready {
|
||||
args = append(args, "--ready")
|
||||
}
|
||||
|
||||
if status != "" {
|
||||
args = append(args, fmt.Sprintf("--status=%s", status))
|
||||
}
|
||||
|
||||
cmd := exec.Command("systemd-notify", args...)
|
||||
cmd.Run() // errors ignored on purpose, there is not much to do anyways.
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user