runservice: add runEvents handler

This commit is contained in:
Simone Gotti 2019-05-15 09:46:21 +02:00
parent ac893f1c91
commit 81d557d785
3 changed files with 89 additions and 1 deletions

View File

@ -36,6 +36,9 @@ import (
"github.com/gorilla/mux"
"github.com/pkg/errors"
etcdclientv3 "go.etcd.io/etcd/clientv3"
etcdclientv3rpc "go.etcd.io/etcd/etcdserver/api/v3rpc/rpctypes"
"go.etcd.io/etcd/mvcc/mvccpb"
"go.uber.org/zap"
)
@ -117,7 +120,6 @@ func NewLogsHandler(logger *zap.Logger, e *etcd.Store, ost *objectstorage.ObjSto
func (h *LogsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// TODO(sgotti) Check authorized call from client
q := r.URL.Query()
runID := q.Get("runid")
@ -706,3 +708,79 @@ func (h *RunTaskActionsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
return
}
}
type RunEventsHandler struct {
log *zap.SugaredLogger
e *etcd.Store
ost *objectstorage.ObjStorage
dm *datamanager.DataManager
}
func NewRunEventsHandler(logger *zap.Logger, e *etcd.Store, ost *objectstorage.ObjStorage, dm *datamanager.DataManager) *RunEventsHandler {
return &RunEventsHandler{
log: logger.Sugar(),
e: e,
ost: ost,
dm: dm,
}
}
//
func (h *RunEventsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
q := r.URL.Query()
// TODO(sgotti) handle additional events filtering (by type, etc...)
startRunEventID := q.Get("startruneventid")
if err := h.sendRunEvents(ctx, startRunEventID, w); err != nil {
h.log.Errorf("err: %+v", err)
}
}
func (h *RunEventsHandler) sendRunEvents(ctx context.Context, startRunEventID string, w http.ResponseWriter) error {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
var flusher http.Flusher
if fl, ok := w.(http.Flusher); ok {
flusher = fl
}
// TODO(sgotti) fetch from previous events (handle startRunEventID).
// Use the readdb instead of etcd
wctx, cancel := context.WithCancel(ctx)
defer cancel()
wctx = etcdclientv3.WithRequireLeader(wctx)
wch := h.e.WatchKey(wctx, common.EtcdRunEventKey, 0)
for wresp := range wch {
if wresp.Canceled {
err := wresp.Err()
if err == etcdclientv3rpc.ErrCompacted {
h.log.Errorf("required events already compacted")
}
return errors.Wrapf(err, "watch error")
}
for _, ev := range wresp.Events {
switch ev.Type {
case mvccpb.PUT:
var runEvent *types.RunEvent
if err := json.Unmarshal(ev.Kv.Value, &runEvent); err != nil {
return errors.Wrap(err, "failed to unmarshal run")
}
if _, err := w.Write([]byte(fmt.Sprintf("data: %s\n\n", ev.Kv.Value))); err != nil {
return err
}
}
}
if flusher != nil {
flusher.Flush()
}
}
return nil
}

View File

@ -303,3 +303,10 @@ func (c *Client) GetLogs(ctx context.Context, runID, taskID string, setup bool,
return c.getResponse(ctx, "GET", "/logs", q, -1, nil, nil)
}
func (c *Client) GetRunEvents(ctx context.Context, startRunEventID string) (*http.Response, error) {
q := url.Values{}
q.Add("startruneventid", startRunEventID)
return c.getResponse(ctx, "GET", "/runs/events", q, -1, nil, nil)
}

View File

@ -189,6 +189,8 @@ func (s *Runservice) Run(ctx context.Context) error {
runsHandler := api.NewRunsHandler(logger, s.readDB)
runActionsHandler := api.NewRunActionsHandler(logger, s.ah)
runCreateHandler := api.NewRunCreateHandler(logger, s.ah)
runEventsHandler := api.NewRunEventsHandler(logger, s.e, s.ost, s.dm)
changeGroupsUpdateTokensHandler := api.NewChangeGroupsUpdateTokensHandler(logger, s.readDB)
router := mux.NewRouter()
@ -209,6 +211,7 @@ func (s *Runservice) Run(ctx context.Context) error {
apirouter.Handle("/logs", logsHandler).Methods("GET")
apirouter.Handle("/runs/events", runEventsHandler).Methods("GET")
apirouter.Handle("/runs/{runid}", runHandler).Methods("GET")
apirouter.Handle("/runs/{runid}/actions", runActionsHandler).Methods("PUT")
apirouter.Handle("/runs/{runid}/tasks/{taskid}/actions", runTaskActionsHandler).Methods("PUT")