shad-go/distbuild/pkg/api/build_handler.go

143 lines
2.9 KiB
Go
Raw Normal View History

2020-03-29 16:03:07 +00:00
package api
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"go.uber.org/zap"
"gitlab.com/slon/shad-go/distbuild/pkg/build"
)
2020-04-04 21:13:45 +00:00
func NewBuildService(l *zap.Logger, s Service) *BuildHandler {
return &BuildHandler{
2020-03-29 16:03:07 +00:00
l: l,
s: s,
}
}
2020-04-04 21:13:45 +00:00
type BuildHandler struct {
2020-03-29 16:03:07 +00:00
l *zap.Logger
s Service
}
2020-04-04 21:13:45 +00:00
func (h *BuildHandler) Register(mux *http.ServeMux) {
mux.HandleFunc("/build", h.build)
mux.HandleFunc("/signal", h.signal)
2020-03-29 16:03:07 +00:00
}
type statusWriter struct {
2020-04-04 21:13:45 +00:00
id build.ID
h *BuildHandler
2020-03-29 16:03:07 +00:00
written bool
w http.ResponseWriter
2020-04-04 21:13:45 +00:00
flush http.Flusher
2020-03-29 16:03:07 +00:00
enc *json.Encoder
}
func (w *statusWriter) Started(rsp *BuildStarted) error {
2020-04-04 21:13:45 +00:00
w.id = rsp.ID
2020-03-29 16:03:07 +00:00
w.written = true
2020-04-04 21:13:45 +00:00
w.h.l.Debug("build started", zap.String("build_id", w.id.String()), zap.Any("started", rsp))
2020-03-29 16:03:07 +00:00
w.w.Header().Set("content-type", "application/json")
w.w.WriteHeader(http.StatusOK)
2020-04-04 21:13:45 +00:00
defer w.flush.Flush()
2020-03-29 16:03:07 +00:00
return w.enc.Encode(rsp)
}
func (w *statusWriter) Updated(update *StatusUpdate) error {
2020-04-04 21:13:45 +00:00
w.h.l.Debug("build updated", zap.String("build_id", w.id.String()), zap.Any("update", update))
defer w.flush.Flush()
2020-03-29 16:03:07 +00:00
return w.enc.Encode(update)
}
2020-04-04 21:13:45 +00:00
func (h *BuildHandler) doBuild(w http.ResponseWriter, r *http.Request) error {
2020-03-29 16:03:07 +00:00
reqJSON, err := ioutil.ReadAll(r.Body)
if err != nil {
return err
}
var req BuildRequest
2020-03-29 17:35:39 +00:00
if err = json.Unmarshal(reqJSON, &req); err != nil {
2020-03-29 16:03:07 +00:00
return err
}
2020-04-04 21:13:45 +00:00
flush, ok := w.(http.Flusher)
if !ok {
return fmt.Errorf("response writer does not implement http.Flusher")
}
sw := &statusWriter{h: h, w: w, enc: json.NewEncoder(w), flush: flush}
err = h.s.StartBuild(r.Context(), &req, sw)
2020-03-29 16:03:07 +00:00
if err != nil {
if sw.written {
_ = sw.Updated(&StatusUpdate{BuildFailed: &BuildFailed{Error: err.Error()}})
return nil
}
return err
}
return nil
}
2020-04-04 21:13:45 +00:00
func (h *BuildHandler) build(w http.ResponseWriter, r *http.Request) {
if err := h.doBuild(w, r); err != nil {
2020-03-29 16:03:07 +00:00
w.WriteHeader(http.StatusBadRequest)
_, _ = fmt.Fprintf(w, "%v", err)
}
}
2020-04-04 21:13:45 +00:00
func (h *BuildHandler) doSignal(w http.ResponseWriter, r *http.Request) error {
2020-03-29 16:03:07 +00:00
buildIDParam := r.URL.Query().Get("build_id")
if buildIDParam == "" {
return fmt.Errorf(`"build_id" parameter is missing`)
}
var buildID build.ID
if err := buildID.UnmarshalText([]byte(buildIDParam)); err != nil {
return err
}
reqJSON, err := ioutil.ReadAll(r.Body)
if err != nil {
return err
}
var req SignalRequest
2020-03-29 17:35:39 +00:00
if err = json.Unmarshal(reqJSON, &req); err != nil {
2020-03-29 16:03:07 +00:00
return err
}
2020-04-04 21:13:45 +00:00
rsp, err := h.s.SignalBuild(r.Context(), buildID, &req)
2020-03-29 16:03:07 +00:00
if err != nil {
return err
}
rspJSON, err := json.Marshal(rsp)
if err != nil {
return err
}
w.Header().Set("content-type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write(rspJSON)
return nil
}
2020-04-04 21:13:45 +00:00
func (h *BuildHandler) signal(w http.ResponseWriter, r *http.Request) {
if err := h.doSignal(w, r); err != nil {
h.l.Warn("build signal failed", zap.Error(err))
2020-03-29 16:03:07 +00:00
w.WriteHeader(http.StatusBadRequest)
_, _ = fmt.Fprintf(w, "%v", err)
}
}