tinkerbell/http.go

151 lines
3.1 KiB
Go

package main
import (
"context"
"errors"
"fmt"
"net/http"
"path/filepath"
"github.com/barakmich/tinkerbell/ray_rpc"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"go.uber.org/zap"
)
func runHttp(addr, rootPath string, raylet *Raylet) error {
r := gin.Default()
h := rayletHandler{raylet}
api := r.Group("/api")
api.POST("/get", h.GetObject)
api.POST("/put", h.PutObject)
api.POST("/schedule", h.Schedule)
api.GET("/ws", h.WorkerWS)
r.Static("/web", rootPath)
r.StaticFile("console.html", filepath.Join(rootPath, "console.html"))
r.StaticFile("index.html", filepath.Join(rootPath, "index.html"))
r.StaticFile("/", filepath.Join(rootPath, "index.html"))
err := r.Run(addr)
return err
}
type rayletHandler struct {
raylet *Raylet
}
func (h *rayletHandler) GetObject(c *gin.Context) {
var req ray_rpc.GetRequest
c.BindJSON(&req)
res, err := h.raylet.GetObject(context.TODO(), &req)
if err != nil {
c.JSON(500, gin.H{"error": err})
return
}
c.JSON(http.StatusOK, res)
}
func (h *rayletHandler) PutObject(c *gin.Context) {
var req ray_rpc.PutRequest
c.BindJSON(&req)
res, err := h.raylet.PutObject(context.TODO(), &req)
if err != nil {
c.JSON(500, gin.H{"error": err})
return
}
c.JSON(http.StatusOK, res)
}
func (h *rayletHandler) Schedule(c *gin.Context) {
var req ray_rpc.ClientTask
c.BindJSON(&req)
res, err := h.raylet.Schedule(context.TODO(), &req)
if err != nil {
c.JSON(500, gin.H{"error": err})
return
}
c.JSON(http.StatusOK, res)
}
func (h *rayletHandler) WorkerWS(c *gin.Context) {
conn, err := wsupgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
zap.S().Error("Failed to set websocket upgrade: %v\n", err)
return
}
worker := &WSWorker{
conn: conn,
workChan: make(chan *ray_rpc.Work),
h: h,
}
h.raylet.Workers.Register(worker)
err = worker.Run()
if err != nil {
zap.S().Info("Closing WS worker:", err)
}
h.raylet.Workers.Deregister(worker)
}
var wsupgrader = websocket.Upgrader{
ReadBufferSize: 10240,
WriteBufferSize: 10240,
}
type WSWorker struct {
conn *websocket.Conn
workChan chan *ray_rpc.Work
h *rayletHandler
sched bool
}
func (ws *WSWorker) AssignWork(work *ray_rpc.Work) error {
ws.workChan <- work
return nil
}
func (ws *WSWorker) Close() error {
close(ws.workChan)
return nil
}
func (ws *WSWorker) Schedulable() bool {
return ws.sched
}
func (ws *WSWorker) Run() error {
var sentinel ray_rpc.WorkStatus
err := ws.conn.ReadJSON(&sentinel)
if err != nil {
return err
}
if sentinel.Status != ray_rpc.READY {
return errors.New("Sent wrong sentinel? Closing...")
}
ws.sched = true
zap.S().Info("New worker:", sentinel.ErrorMsg)
go func() {
for work := range ws.workChan {
fmt.Println("sending work")
ws.sched = false
err = ws.conn.WriteJSON(work)
if err != nil {
zap.S().Error("Error sending:", err)
return
}
}
}()
for {
var result ray_rpc.WorkStatus
err := ws.conn.ReadJSON(&result)
if err != nil {
zap.S().Error("Error on channel:", err)
return err
}
ws.sched = true
err = ws.h.raylet.Workers.Finish(&result)
if err != nil {
zap.S().Error("Error finishing:", err)
return err
}
}
return nil
}