diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 5e30fab8d..dec7c1742 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -55,7 +55,7 @@ jobs: # uses a compiled language #- run: | - # make all + # make all # make release - name: Perform CodeQL Analysis diff --git a/.idea/TUM-Live-Backend.iml b/.idea/TUM-Live-Backend.iml index 8f9c84fa8..fa6749226 100644 --- a/.idea/TUM-Live-Backend.iml +++ b/.idea/TUM-Live-Backend.iml @@ -10,7 +10,9 @@ - + + + diff --git a/.idea/runConfigurations/clean.xml b/.idea/runConfigurations/clean.xml deleted file mode 100644 index 12476b0b0..000000000 --- a/.idea/runConfigurations/clean.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml deleted file mode 100644 index de8270284..000000000 --- a/.idea/sqldialects.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/api/action.go b/api/action.go new file mode 100644 index 000000000..406e496b4 --- /dev/null +++ b/api/action.go @@ -0,0 +1,54 @@ +package api + +import ( + "context" + "github.com/TUM-Dev/gocast/dao" + "github.com/TUM-Dev/gocast/tools" + "github.com/gin-gonic/gin" + log "github.com/sirupsen/logrus" + "net/http" +) + +func configActionRouter(r *gin.Engine, wrapper dao.DaoWrapper) { + g := r.Group("/api/Actions") + g.Use(tools.Admin) + + routes := actionRoutes{dao: wrapper.ActionDao} + + g.GET("/failed", routes.getFailedActions) + g.GET("/:id", routes.getActionById) +} + +type actionRoutes struct { + dao dao.ActionDao +} + +func (a actionRoutes) getFailedActions(c *gin.Context) { + log.Info("Getting failed actions") + ctx := context.Background() + models, err := a.dao.GetAllFailedActions(ctx) + if err != nil { + _ = c.Error(tools.RequestError{ + Status: http.StatusInternalServerError, + CustomMessage: "Can't fetch failed actions", + Err: err, + }) + return + } + res := make([]gin.H, len(models)) + c.JSON(http.StatusOK, res) +} + +func (a actionRoutes) getActionById(c *gin.Context) { + ctx := context.Background() + model, err := a.dao.GetActionByID(ctx, c.Param("id")) + if err != nil { + _ = c.Error(tools.RequestError{ + Status: http.StatusNotFound, + CustomMessage: "Action not found", + Err: err, + }) + return + } + c.JSON(http.StatusOK, model) +} diff --git a/api/router.go b/api/router.go index f38b96eb0..bd4f4c85e 100755 --- a/api/router.go +++ b/api/router.go @@ -40,6 +40,7 @@ func ConfigGinRouter(router *gin.Engine) { configServerNotificationsRoutes(router, daoWrapper) configTokenRouter(router, daoWrapper) configWorkerRouter(router, daoWrapper) + configRunnerRouter(router, daoWrapper) configNotificationsRouter(router, daoWrapper) configInfoPageRouter(router, daoWrapper) configGinSearchRouter(router, daoWrapper) @@ -47,4 +48,5 @@ func ConfigGinRouter(router *gin.Engine) { configGinBookmarksRouter(router, daoWrapper) configMaintenanceRouter(router, daoWrapper) configSemestersRouter(router, daoWrapper) + configActionRouter(router, daoWrapper) } diff --git a/api/runner.go b/api/runner.go new file mode 100644 index 000000000..e7a4f5fed --- /dev/null +++ b/api/runner.go @@ -0,0 +1,39 @@ +package api + +import ( + "context" + "github.com/TUM-Dev/gocast/dao" + "github.com/TUM-Dev/gocast/tools" + "github.com/gin-gonic/gin" + log "github.com/sirupsen/logrus" + "net/http" +) + +func configRunnerRouter(r *gin.Engine, daoWrapper dao.DaoWrapper) { + g := r.Group("/api/runners") + g.Use(tools.Admin) + + routes := runnerRoutes{dao: daoWrapper.RunnerDao} + + g.DELETE("/:HostName", routes.deleteRunner) +} + +type runnerRoutes struct { + dao dao.RunnerDao +} + +func (r runnerRoutes) deleteRunner(c *gin.Context) { + log.Info("delete runner with hostname: ", c.Param("Hostname")) + ctx := context.Background() + err := r.dao.Delete(ctx, c.Param("Hostname")) + if err != nil { + //logging for later + _ = c.Error(tools.RequestError{ + Status: http.StatusInternalServerError, + CustomMessage: "can not delete runner", + Err: err, + }) + return + } + +} diff --git a/api/runner_grpc.go b/api/runner_grpc.go new file mode 100644 index 000000000..97b3c21cc --- /dev/null +++ b/api/runner_grpc.go @@ -0,0 +1,684 @@ +package api + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "github.com/TUM-Dev/gocast/dao" + "github.com/TUM-Dev/gocast/model" + "github.com/TUM-Dev/gocast/tools" + "github.com/getsentry/sentry-go" + log "github.com/sirupsen/logrus" + "github.com/tum-dev/gocast/runner/protobuf" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/keepalive" + "google.golang.org/grpc/reflection" + "google.golang.org/protobuf/types/known/timestamppb" + "io" + "net" + "net/http" + "regexp" + "strings" + "time" +) + +var _ protobuf.FromRunnerServer = nil + +type GrpcRunnerServer struct { + protobuf.UnimplementedFromRunnerServer + + dao.DaoWrapper +} + +// + +func (g GrpcRunnerServer) Register(ctx context.Context, request *protobuf.RegisterRequest) (*protobuf.RegisterResponse, error) { + runner, err := g.RunnerDao.Get(ctx, request.Hostname) + if runner == nil || runner.Hostname == "" { + runner = &model.Runner{ + Hostname: request.Hostname, + Port: int(request.Port), + LastSeen: time.Now(), + Status: "Alive", + Workload: 0, + } + } + err = g.RunnerDao.Create(ctx, runner) + if err != nil { + return nil, err + } + return &protobuf.RegisterResponse{ID: runner.Hostname}, nil +} + +func (g GrpcRunnerServer) Heartbeat(ctx context.Context, request *protobuf.HeartbeatRequest) (*protobuf.HeartbeatResponse, error) { + runner := model.Runner{ + Hostname: request.Hostname, + Port: int(request.Port), + } + + r, err := g.RunnerDao.Get(ctx, runner.Hostname) + if err != nil { + log.WithError(err).Error("Failed to get runner") + return &protobuf.HeartbeatResponse{Ok: false}, err + } + + newStats := model.Runner{ + Hostname: request.Hostname, + Port: int(request.Port), + LastSeen: time.Now(), + Status: "Alive", + Workload: uint(request.Workload), + CPU: request.CPU, + Memory: request.Memory, + Disk: request.Disk, + Uptime: request.Uptime, + Version: request.Version, + Actions: request.CurrentAction, + } + ctx = context.WithValue(ctx, "newStats", newStats) + log.Info("Updating runner stats ", "runner", r) + p, err := r.UpdateStats(dao.DB, ctx) + return &protobuf.HeartbeatResponse{Ok: p}, err +} + +func StreamRequest(ctx context.Context, dao dao.DaoWrapper, runner model.Runner) { + streamID := fmt.Sprintf("%f", ctx.Value("stream")) + stream, err := dao.StreamsDao.GetStreamByID(ctx, streamID) + if err != nil { + logger.Error("Can't get stream", "err", err) + return + } + course, err := dao.CoursesDao.GetCourseById(ctx, uint(ctx.Value("course").(float64))) + if err != nil { + logger.Error("Can't get course", "err", err) + return + } + source := fmt.Sprintf("%v", ctx.Value("source")) + version := fmt.Sprintf("%v", ctx.Value("version")) + actionID := fmt.Sprintf("%v", ctx.Value("actionID")) + stringEnd := fmt.Sprintf("%v", ctx.Value("end")) + end, err := time.Parse(time.RFC3339, stringEnd) + if err != nil { + logger.Error("Can't parse end", "err", err) + return + } + if source == "" { + logger.Error("No source", "source", source) + return + } + + //TODO: Implement environment variable for ingest + ingest := false + if ingest { + server, err := dao.IngestServerDao.GetBestIngestServer() + if err != nil { + logger.Error("can't find ingest server", "err", err) + return + } + + var slot model.StreamName + if version == "COMB" { //try to find a transcoding slot for comb view: + slot, err = dao.IngestServerDao.GetTranscodedStreamSlot(server.ID) + } + if version != "COMB" || err != nil { + slot, err = dao.IngestServerDao.GetStreamSlot(server.ID) + if err != nil { + logger.Error("No free stream slot", "err", err) + return + } + } + slot.StreamID = stream.ID + dao.IngestServerDao.SaveSlot(slot) + } + + src := "rtsp://" + source + req := protobuf.StreamRequest{ + ActionID: actionID, + Stream: uint64(stream.ID), + Course: uint64(course.ID), + Version: version, + End: timestamppb.New(end), + Source: src, + } + conn, err := grpc.Dial(fmt.Sprintf("%s:%d", runner.Hostname, runner.Port), grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + logger.Error("Can't dial runner", "err", err) + return + } + client := protobuf.NewToRunnerClient(conn) + resp, err := client.RequestStream(context.Background(), &req) + if err != nil { + logger.Error("Can't request stream", "err", err) + return + } + err = dao.StreamsDao.SetStreamRequested(stream) + if err != nil { + logger.Error("Can't set stream requested", "err", err) + return + } + logger.Info("Stream requested", "ActionID", resp.ActionID) + if err = conn.Close(); err != nil { + logger.Error("Can't close connection", "err", err) + } + + return +} +func TranscodingRequest(ctx context.Context, dao dao.DaoWrapper, runner model.Runner) { + stream, err := dao.StreamsDao.GetStreamByID(ctx, ctx.Value("stream").(string)) + if err != nil { + logger.Error("Can't get stream", "err", err) + return + } + course, err := dao.CoursesDao.GetCourseById(ctx, ctx.Value("course").(uint)) + if err != nil { + logger.Error("Can't get course", "err", err) + return + } + source := ctx.Value("source").(string) + version := ctx.Value("version").(string) + actionID := ctx.Value("actionID").(string) + + if source == "" { + return + } + + //gather all data into one part url + + conn, err := grpc.Dial(fmt.Sprintf("%s:%d", runner.Hostname, runner.Port), grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + logger.Error("Can't dial runner", "err", err) + return + } + client := protobuf.NewToRunnerClient(conn) + resp, err := client.RequestTranscoding(context.Background(), &protobuf.TranscodingRequest{ + ActionID: actionID, + DataURL: "", + RunnerID: runner.Hostname, + StreamName: stream.StreamName, + CourseName: course.Name, + SourceType: version, + }) + if err != nil { + logger.Error("Can't request transcode", "err", err) + return + } + logger.Info("Transcode requested", "actionID", resp.ActionID) + if err = conn.Close(); err != nil { + logger.Error("Can't close connection", "err", err) + } + +} + +func getRunnerWithLeastWorkloadForJob(runner []model.Runner, Job string) (model.Runner, error) { + if len(runner) == 0 { + return model.Runner{}, errors.New("runner array is empty") + } + chosen := runner[0] + switch Job { + + } + for _, r := range runner { + if r.Workload < chosen.Workload { + chosen = r + } + } + return chosen, nil +} + +// RequestSelfStream is called by the runner when a stream is supposed to be started by obs or other third party software +// returns an error if anything goes wrong OR the stream may not be published +func (g GrpcRunnerServer) RequestSelfStream(ctx context.Context, request *protobuf.SelfStreamRequest) (*protobuf.SelfStreamResponse, error) { + //TODO Test me/Improve me + if request.StreamKey == "" { + return nil, errors.New("stream key is empty") + } + stream, err := g.StreamsDao.GetStreamByKey(ctx, request.StreamKey) + if err != nil { + return nil, err + } + course, err := g.CoursesDao.GetCourseById(ctx, stream.CourseID) + if err != nil { + return nil, err + } + if !(time.Now().After(stream.Start.Add(time.Minute*-30)) && time.Now().Before(stream.End.Add(time.Minute*30))) { + log.WithFields(log.Fields{"streamId": stream.ID}).Warn("Stream rejected, time out of bounds") + return nil, errors.New("stream rejected") + } + ingestServer, err := g.IngestServerDao.GetBestIngestServer() + if err != nil { + return nil, err + } + slot, err := g.IngestServerDao.GetStreamSlot(ingestServer.ID) + if err != nil { + return nil, err + } + slot.StreamID = stream.ID + g.IngestServerDao.SaveSlot(slot) + + return &protobuf.SelfStreamResponse{ + Stream: uint64(stream.ID), + Course: uint64(course.ID), + CourseYear: uint64(course.Year), + StreamStart: timestamppb.New(stream.Start), + StreamEnd: timestamppb.New(stream.End), + UploadVoD: course.VODEnabled, + IngestServer: ingestServer.Url, + StreamName: stream.StreamName, + OutURL: ingestServer.OutUrl, + }, nil +} + +func (g GrpcRunnerServer) NotifyStreamEnded(ctx context.Context, request *protobuf.StreamEnded) (*protobuf.Status, error) { + //TODO Test me + stream, err := g.StreamsDao.GetStreamByID(ctx, fmt.Sprintf("%v", request.StreamID)) + if err != nil { + return &protobuf.Status{Ok: false}, err + } + err = g.StreamsDao.SaveEndedState(stream.ID, true) + if err != nil { + return &protobuf.Status{Ok: false}, err + } + return &protobuf.Status{Ok: true}, nil +} + +func (g GrpcRunnerServer) NotifyStreamStarted(ctx context.Context, request *protobuf.StreamStarted) (*protobuf.Status, error) { + mutex.Lock() + defer mutex.Unlock() + runner, err := g.RunnerDao.Get(ctx, request.Hostname) + if err != nil { + logger.Error("Failed to get runner", err) + return nil, err + } + stream, err := g.StreamsDao.GetStreamByID(ctx, fmt.Sprintf("%d", request.StreamID)) + if err != nil { + logger.Error("Failed to get stream", err) + return nil, err + } + course, err := g.CoursesDao.GetCourseById(ctx, (uint)(request.CourseID)) + if err != nil { + logger.Error("Failed to get course", err) + return nil, err + } + go func() { + err := handleLightOnSwitch(stream, g.DaoWrapper) + if err != nil { + logger.Error("Can't handle light on switch", "err", err) + } + err = handleCameraPositionSwitch(stream, g.DaoWrapper) + if err != nil { + logger.Error("Can't handle camera position switch", "err", err) + } + err = g.DaoWrapper.DeleteSilences(fmt.Sprintf("%d", stream.ID)) + if err != nil { + logger.Error("Can't delete silences", "err", err) + } + }() + go func() { + stream.LiveNow = true + stream.Private = course.LivePrivate + + err := g.StreamsDao.SaveStream(&stream) + if err != nil { + logger.Error("Failed to save stream", err) + } + err = g.StreamsDao.SetStreamLiveNowTimestampById(uint(request.StreamID), time.Now()) + if err != nil { + logger.Error("Can't set StreamLiveNowTimestamp", "err", err) + } + + hlsUrl := fmt.Sprintf("%v:%v/%v", tools.Cfg.Edge.Domain, tools.Cfg.Edge.Port, request.HLSUrl) + + time.Sleep(time.Second * 5) + if !isHLSUrlOk(hlsUrl) { + sentry.WithScope(func(scope *sentry.Scope) { + scope.SetExtra("URL", request.HLSUrl) + scope.SetExtra("StreamID", request.StreamID) + scope.SetExtra("LectureHall", stream.LectureHallID) + scope.SetExtra("Runner", runner.Hostname) + scope.SetExtra("Version", request.SourceType) + sentry.CaptureException(errors.New("DVR URL 404s")) + }) + hlsUrl = strings.ReplaceAll(hlsUrl, "?dvr", "") + } + + logger.Info("hls url", "url", hlsUrl) + + switch request.Version { + case "CAM": + g.StreamsDao.SaveCAMURL(&stream, hlsUrl) + case "PRES": + g.StreamsDao.SavePRESURL(&stream, hlsUrl) + default: + g.StreamsDao.SaveCOMBURL(&stream, hlsUrl) + } + + NotifyViewersLiveState(stream.Model.ID, true) + NotifyLiveUpdateCourseWentLive(stream.Model.ID) + }() + return &protobuf.Status{Ok: true}, nil +} + +func isHLSUrlOk(url string) bool { + r, err := http.Get(url) + if err != nil { + return false + } + all, err := io.ReadAll(r.Body) + if err != nil { + return false + } + re, _ := regexp.Compile(`chunklist.*\.m3u8`) + x := re.Find(all) + if x == nil { + return false + } + y := strings.ReplaceAll(r.Request.URL.String(), "playlist.m3u8", string(x)) + get, err := http.Get(y) + if err != nil { + return false + } + if get.StatusCode == http.StatusNotFound { + return false + } + return true +} + +func (g GrpcRunnerServer) NotifyVoDUploadFinished(ctx context.Context, request *protobuf.VoDUploadFinished) (*protobuf.Status, error) { + + panic("implement!") +} + +func (g GrpcRunnerServer) NotifyActionFinished(ctx context.Context, request *protobuf.ActionFinished) (*protobuf.Status, error) { + _, err := g.RunnerDao.Get(ctx, request.RunnerID) + + status := &protobuf.Status{Ok: false} + switch request.Type { + case "Upload": + status, err = SetUploadFinished(ctx, request) + if err != nil { + return nil, err + } + case "Transcode": + status, err = SetTranscodeFinished(ctx, request) + if err != nil { + return nil, err + } + case "Stream": + } + + return &protobuf.Status{Ok: status.Ok}, nil + +} + +func SetUploadFinished(ctx context.Context, req *protobuf.ActionFinished) (*protobuf.Status, error) { + panic("implement me") +} + +func SetTranscodeFinished(ctx context.Context, req *protobuf.ActionFinished) (*protobuf.Status, error) { + panic("implement me") +} + +func NotifyForStreams(dao dao.DaoWrapper) func() { + return func() { + + logger.Info("Collecting due streams") + + streams := dao.StreamsDao.GetDueStreamsForWorkers() + for i := range streams { + err := dao.StreamsDao.SaveEndedState(streams[i].ID, false) + if err != nil { + logger.Warn("Can't save ended state", err) + sentry.CaptureException(err) + continue + } + courseForStream, err := dao.CoursesDao.GetCourseById(context.Background(), streams[i].CourseID) + if err != nil { + logger.Warn("Can't get course for stream", err) + sentry.CaptureException(err) + continue + } + lectureHallForStream, err := dao.LectureHallsDao.GetLectureHallByID(streams[i].LectureHallID) + if err != nil { + logger.Warn("Can't get lecture hall for stream", err) + sentry.CaptureException(err) + continue + } + ctx := context.WithValue(context.Background(), "type", "stream") + values := map[string]interface{}{ + "type": "stream", + "stream": streams[i].ID, + "course": courseForStream.ID, + "end": streams[i].End, + } + switch courseForStream.GetSourceModeForLectureHall(streams[i].LectureHallID) { + case 1: + values["version"] = "PRES" + values["source"] = lectureHallForStream.PresIP + err = CreateJob(dao, ctx, values) //presentation + if err != nil { + log.Error("Can't create job", err) + } + break + case 2: //camera + values["version"] = "CAM" + values["source"] = lectureHallForStream.CamIP + err = CreateJob(dao, ctx, values) + if err != nil { + logger.Error("Can't create job", err) + } + break + default: //combined + values["version"] = "PRES" + values["source"] = lectureHallForStream.PresIP + err = CreateJob(dao, ctx, values) + + if err != nil { + logger.Error("Can't create job", err) + } + + values["version"] = "CAM" + values["source"] = lectureHallForStream.CamIP + err = CreateJob(dao, ctx, values) + if err != nil { + logger.Error("Can't create job", err) + } + + values["version"] = "COMB" + values["source"] = lectureHallForStream.CombIP + err = CreateJob(dao, ctx, values) + if err != nil { + logger.Error("Can't create job", err) + } + break + } + } + } +} + +func NotifyRunnerAssignments(dao dao.DaoWrapper) func() { + return func() { + logger.Info("Assigning runners to action") + ctx := context.Background() + + //checking for each running action if the runner is still doing the job or if it is dead + activeAction, err := dao.ActionDao.GetRunningActions(ctx) + if err != nil { + logger.Error("Can't get running actions", err) + } + for _, action := range activeAction { + if action.End.Before(time.Now().Add(5 * time.Minute)) { + action.SetToIgnored() + log.Info("Action ignored, check for progress manually", "action", action.ID) + continue + } + runner, err := action.GetCurrentRunner() + if err != nil { + logger.Error("Can't get current runner", err) + action.SetToFailed() + err = dao.ActionDao.UpdateAction(ctx, &action) + if err != nil { + return + } + continue + } + if !runner.IsAlive() && !action.IsCompleted() { + action.SetToFailed() + err = dao.ActionDao.UpdateAction(ctx, &action) + if err != nil { + return + } + } + } + + failedActions, err := dao.ActionDao.GetAllFailedActions(ctx) + if err != nil { + logger.Error("Can't get failed actions", err) + return + } + for _, failedAction := range failedActions { + failedAction.SetToRunning() + err = AssignRunnerAction(dao, &failedAction) + err = dao.ActionDao.UpdateAction(ctx, &failedAction) + if err != nil { + return + } + if err != nil { + logger.Error("Can't assign runner to action", err) + } + } + + //Running normal jobs with the idea that they are working as they should + jobs, err := dao.JobDao.GetAllOpenJobs(ctx) + if err != nil { + logger.Error("Can't get jobs", err) + return + } + for _, job := range jobs { + action, err := job.GetNextAction() + if err != nil { + logger.Error("Can't get next action", err) + continue + } + if dao.JobDao.UpdateJob(ctx, job) != nil { + logger.Error("Can't update job", err) + continue + } + err = AssignRunnerAction(dao, action) + if err != nil { + logger.Error("Can't assign runner to action", err) + continue + } + action.SetToRunning() + err = dao.ActionDao.UpdateAction(ctx, action) + if err != nil { + return + } + } + + } +} + +func AssignRunnerAction(dao dao.DaoWrapper, action *model.Action) error { + //here is where we are going to selectively get the runner for each type of action + runners, err := dao.RunnerDao.GetAll(context.Background()) + if err != nil { + return err + } + if len(runners) == 0 { + logger.Error("No runners available") + return err + } + runner, err := getRunnerWithLeastWorkloadForJob(runners, action.Type) + action.AssignRunner(runner) + ctx := context.Background() + + if err != nil { + logger.Error("Can't unmarshal json", err) + return err + } + values := map[string]interface{}{} + err = json.Unmarshal([]byte(action.Values), &values) + for key, value := range values { + //logger.Info("values", "value", value) + ctx = context.WithValue(ctx, key, value) + } + ctx = context.WithValue(ctx, "actionID", fmt.Sprintf("%v", action.ID)) + + switch action.Type { + case "stream": + StreamRequest(ctx, dao, runner) + action.SetToRunning() + break + case "transcoding": + //TranscodingRequest(ctx, dao, runner) + break + } + action.SetToRunning() + return nil +} + +func CreateJob(dao dao.DaoWrapper, ctx context.Context, values map[string]interface{}) error { + logger.Info("Creating Job", "values", values) + job := model.Job{ + Start: time.Now(), + Completed: false, + } + value, err := json.Marshal(values) + if err != nil { + return err + } + var actions []model.Action + switch ctx.Value("type") { + case "stream": + actions = append(actions, model.Action{ + Status: 3, + Type: "stream", + Values: string(value), + }, model.Action{ + Status: 3, + Type: "transcode", + Values: string(value), + }, model.Action{ + Status: 3, + Type: "upload", + Values: string(value), + }) + job.Actions = append(job.Actions, actions...) + } + err = dao.CreateJob(ctx, job) + if err != nil { + logger.Error("couldn't create job in database", err) + return err + } + + return nil +} + +func (g GrpcRunnerServer) mustEmbedUnimplementedFromRunnerServer() { + //TODO implement me + panic("implement me") +} + +func StartGrpcRunnerServer() { + lis, err := net.Listen("tcp", ":50056") + if err != nil { + log.WithError(err).Error("Failed to init grpc server") + return + } + grpcServer := grpc.NewServer(grpc.KeepaliveParams(keepalive.ServerParameters{ + MaxConnectionIdle: time.Minute, + MaxConnectionAge: time.Minute * 5, + MaxConnectionAgeGrace: time.Second * 5, + Time: time.Minute * 10, + Timeout: time.Second * 20, + })) + protobuf.RegisterFromRunnerServer(grpcServer, &GrpcRunnerServer{DaoWrapper: dao.NewDaoWrapper()}) + reflection.Register(grpcServer) + go func() { + if err = grpcServer.Serve(lis); err != nil { + log.WithError(err).Errorf("Can't serve grpc") + } + }() +} diff --git a/api/stream.go b/api/stream.go index 0238b2f7c..3170b071e 100644 --- a/api/stream.go +++ b/api/stream.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/getsentry/sentry-go" "net/http" "os" "path/filepath" @@ -18,7 +19,6 @@ import ( "github.com/TUM-Dev/gocast/tools" "github.com/TUM-Dev/gocast/tools/bot" "github.com/TUM-Dev/gocast/voice-service/pb" - "github.com/getsentry/sentry-go" "github.com/gin-gonic/gin" uuid "github.com/satori/go.uuid" "gorm.io/gorm" @@ -893,4 +893,5 @@ func (r streamRoutes) updateChatEnabled(c *gin.Context) { c.AbortWithStatusJSON(http.StatusBadRequest, "could not update stream") return } + } diff --git a/cmd/tumlive/tumlive.go b/cmd/tumlive/tumlive.go index 82175b75d..bf533a725 100755 --- a/cmd/tumlive/tumlive.go +++ b/cmd/tumlive/tumlive.go @@ -38,6 +38,7 @@ var logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ var initializers = []initializer{ tools.LoadConfig, api.ServeWorkerGRPC, + api.StartGrpcRunnerServer, tools.InitBranding, } @@ -189,6 +190,9 @@ func main() { &model.Subtitles{}, &model.TranscodingFailure{}, &model.Email{}, + &model.Runner{}, + &model.Job{}, + &model.Action{}, ) if err != nil { sentry.CaptureException(err) @@ -243,8 +247,11 @@ func initCron() { _ = tools.Cron.AddFunc("collectStats", api.CollectStats(daoWrapper), "0-59 * * * *") // Flush stale sentry exceptions and transactions every 5 minutes _ = tools.Cron.AddFunc("sentryFlush", func() { sentry.Flush(time.Minute * 2) }, "0-59/5 * * * *") - // Look for due streams and notify workers about them - _ = tools.Cron.AddFunc("triggerDueStreams", api.NotifyWorkers(daoWrapper), "0-59 * * * *") + //Look for due streams and notify workers about them + //_ = tools.Cron.AddFunc("triggerDueStreams", api.NotifyWorkers(daoWrapper), "0-59 * * * *") + //Look for due work to do and notify runner about them + _ = tools.Cron.AddFunc("triggerDueStreams", api.NotifyForStreams(daoWrapper), "1-59/2 * * * *") + _ = tools.Cron.AddFunc("AssignOpenActions", api.NotifyRunnerAssignments(daoWrapper), "0-58/2 * * * *") // update courses available _ = tools.Cron.AddFunc("prefetchCourses", tum.PrefetchCourses(daoWrapper), "30 3 * * *") // export data to meili search diff --git a/config.yaml b/config.yaml index d89a8bdcf..c48f9fea4 100644 --- a/config.yaml +++ b/config.yaml @@ -30,6 +30,9 @@ db: database: tumlive password: example user: root +edge: + domain: http://localhost + port: 8089 ingestbase: rtmp://ingest.tum.live/ jwtkey: # This is an example key, delete and restart to generate a proper one | diff --git a/dao/Action.go b/dao/Action.go new file mode 100644 index 000000000..912921890 --- /dev/null +++ b/dao/Action.go @@ -0,0 +1,84 @@ +package dao + +import ( + "context" + "github.com/TUM-Dev/gocast/model" + "gorm.io/gorm" +) + +//go:generate mockgen -source=action.go -destination ../mock_dao/action.go + +type ActionDao interface { + CreateAction(ctx context.Context, action *model.Action) error + CompleteAction(ctx context.Context, actionID string) error + GetActionByID(ctx context.Context, actionID string) (model.Action, error) + GetActionsByJobID(ctx context.Context, jobID string) ([]model.Action, error) + GetAwaitingActions(ctx context.Context) ([]model.Action, error) + GetRunningActions(ctx context.Context) ([]model.Action, error) + GetAll(ctx context.Context) ([]model.Action, error) + GetAllFailedActions(ctx context.Context) ([]model.Action, error) + UpdateAction(ctx context.Context, action *model.Action) error + GetAllActionOfRunner(ctx context.Context, runnerID string) ([]model.Action, error) +} + +type actionDao struct { + db *gorm.DB +} + +func NewActionDao() ActionDao { + return actionDao{db: DB} +} + +func (d actionDao) CreateAction(ctx context.Context, action *model.Action) error { + return d.db.WithContext(ctx).Create(&action).Error +} + +func (d actionDao) CompleteAction(ctx context.Context, actionID string) error { + return d.db.WithContext(ctx).Model(&model.Action{}).Where("id = ?", actionID).Update("status", "completed").Error +} + +func (d actionDao) GetActionByID(ctx context.Context, actionID string) (model.Action, error) { + var action model.Action + err := d.db.WithContext(ctx).First(&action, "id = ?", actionID).Error + return action, err +} + +func (d actionDao) GetActionsByJobID(ctx context.Context, jobID string) ([]model.Action, error) { + var actions []model.Action + err := d.db.WithContext(ctx).Find(&actions, "job_id = ?", jobID).Error + return actions, err +} + +func (d actionDao) GetAwaitingActions(ctx context.Context) ([]model.Action, error) { + var actions []model.Action + err := d.db.WithContext(ctx).Find(&actions, "status = ?", 3).Error + return actions, err +} + +func (d actionDao) GetRunningActions(ctx context.Context) ([]model.Action, error) { + var actions []model.Action + err := d.db.WithContext(ctx).Find(&actions, "status = ?", 1).Error + return actions, err +} + +func (d actionDao) GetAll(ctx context.Context) ([]model.Action, error) { + var actions []model.Action + err := d.db.WithContext(ctx).Find(&actions).Error + return actions, err +} + +func (d actionDao) GetAllFailedActions(ctx context.Context) ([]model.Action, error) { + var actions []model.Action + err := d.db.WithContext(ctx).Find(&actions, "status = ?", 2).Error + return actions, err +} + +func (d actionDao) UpdateAction(ctx context.Context, action *model.Action) error { + return d.db.WithContext(ctx).Model(&model.Action{}).Where("id = ?", action.ID).Updates(action).Error +} + +func (d actionDao) GetAllActionOfRunner(ctx context.Context, runnerID string) ([]model.Action, error) { + var actions []model.Action + err := d.db.WithContext(ctx).Joins("AllRunners").Where("id = ?", runnerID).Find(&actions).Error + return actions, err +} diff --git a/dao/Jobs.go b/dao/Jobs.go new file mode 100644 index 000000000..5b8d0fac0 --- /dev/null +++ b/dao/Jobs.go @@ -0,0 +1,60 @@ +package dao + +import ( + "context" + "github.com/TUM-Dev/gocast/model" + "gorm.io/gorm" +) + +//go:generate mockgen -source=Jobs.go -destination ../mock_dao/jobs.go + +type JobDao interface { + CreateJob(ctx context.Context, job model.Job) error + Get(ctx context.Context, jobID string) (model.Job, error) + CompleteJob(ctx context.Context, job model.Job) error + GetRunners(ctx context.Context, job model.Job) ([]*model.Runner, error) + RemoveAction(ctx context.Context, job model.Job, actionID uint32) error + GetAllOpenJobs(ctx context.Context) ([]model.Job, error) + UpdateJob(ctx context.Context, job model.Job) error +} + +type jobDao struct { + db *gorm.DB +} + +func NewJobDao() JobDao { + return jobDao{db: DB} +} + +func (j jobDao) Get(ctx context.Context, jobID string) (res model.Job, err error) { + return res, j.db.WithContext(ctx).First(&res, "id = ?", jobID).Error +} + +func (j jobDao) CreateJob(ctx context.Context, job model.Job) error { + return j.db.WithContext(ctx).Create(&job).Error +} + +func (j jobDao) CompleteJob(ctx context.Context, job model.Job) error { + return j.db.WithContext(ctx).Model(&job).Update("complete", true).Error +} + +func (j jobDao) GetRunners(ctx context.Context, job model.Job) ([]*model.Runner, error) { + var runners []*model.Runner + err := j.db.WithContext(ctx).Model(&job).Association("JobRunner").Find(&runners) + return runners, err +} + +func (j jobDao) RemoveAction(ctx context.Context, job model.Job, actionID uint32) error { + return j.db.WithContext(ctx).Delete(&model.Action{}, "job_id = ? AND action_id = ?", job.ID, actionID).Error + +} + +func (j jobDao) GetAllOpenJobs(ctx context.Context) ([]model.Job, error) { + var jobs []model.Job + err := j.db.WithContext(ctx).Model(&model.Job{}).Preload("Actions").Find(&jobs).Where("completed = ?", false).Error + return jobs, err +} + +func (j jobDao) UpdateJob(ctx context.Context, job model.Job) error { + return j.db.WithContext(ctx).Model(&job).Updates(&job).Error +} diff --git a/dao/dao_base.go b/dao/dao_base.go index fc831d9d0..99a6e126f 100644 --- a/dao/dao_base.go +++ b/dao/dao_base.go @@ -36,6 +36,9 @@ type DaoWrapper struct { SubtitlesDao TranscodingFailureDao EmailDao + RunnerDao + JobDao + ActionDao } func NewDaoWrapper() DaoWrapper { @@ -63,5 +66,8 @@ func NewDaoWrapper() DaoWrapper { SubtitlesDao: NewSubtitlesDao(), TranscodingFailureDao: NewTranscodingFailureDao(), EmailDao: NewEmailDao(), + RunnerDao: NewRunnerDao(), + JobDao: NewJobDao(), + ActionDao: NewActionDao(), } } diff --git a/dao/runner.go b/dao/runner.go new file mode 100644 index 000000000..bc7baa6a0 --- /dev/null +++ b/dao/runner.go @@ -0,0 +1,68 @@ +package dao + +import ( + "context" + "github.com/TUM-Dev/gocast/model" + log "github.com/sirupsen/logrus" + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +//go:generate mockgen -source=runner.go -destination ../mock_dao/runner.go + +type RunnerDao interface { + // Get Runner by hostname + Get(context.Context, string) (*model.Runner, error) + + // Get all Runners in an array + GetAll(context.Context) ([]model.Runner, error) + + // Create a new Runner for the database + Create(context.Context, *model.Runner) error + + // Delete a Runner by hostname. + Delete(context.Context, string) error +} + +type runnerDao struct { + db *gorm.DB +} + +func NewRunnerDao() RunnerDao { + return runnerDao{db: DB} +} + +// Get a Runner by id. +func (d runnerDao) Get(c context.Context, hostname string) (res *model.Runner, err error) { + return res, DB.WithContext(c).First(&res, "hostname = ?", hostname).Error +} + +// Get all Runners in an array +func (d runnerDao) GetAll(c context.Context) ([]model.Runner, error) { + var runners []model.Runner + err := d.db.WithContext(c).Model(&model.Runner{}).Find(&runners).Error + if err != nil { + log.Error("no runners found") + return nil, err + } + var aliveRunner []model.Runner + for _, runner := range runners { + if runner.IsAlive() { + aliveRunner = append(aliveRunner, runner) + } + } + return aliveRunner, err +} + +// Create a Runner. +func (d runnerDao) Create(c context.Context, it *model.Runner) error { + return DB.WithContext(c).Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "hostname"}}, + UpdateAll: true, + }).Create(&it).Error +} + +// Delete a Runner by hostname. +func (d runnerDao) Delete(c context.Context, hostname string) error { + return DB.WithContext(c).Delete(&model.Runner{}, hostname).Error +} diff --git a/dao/streams.go b/dao/streams.go index 92bfcaf75..f344e1193 100755 --- a/dao/streams.go +++ b/dao/streams.go @@ -62,12 +62,18 @@ type StreamsDao interface { DeleteStreamsWithTumID(ids []uint) UpdateLectureSeries(model.Stream) error DeleteLectureSeries(string) error + + SetStreamRequested(stream model.Stream) error } type streamsDao struct { db *gorm.DB } +func (d streamsDao) SetStreamRequested(stream model.Stream) error { + return DB.Model(&stream).Updates(map[string]interface{}{"requested": true}).Error +} + func (d streamsDao) GetTranscodingProgressByVersion(v model.StreamVersion, streamId uint) (p model.TranscodingProgress, err error) { err = DB.Where("version = ? AND stream_id = ?", v, streamId).First(&p).Error return @@ -464,7 +470,8 @@ func (d streamsDao) UpdateStreamFullAssoc(vod *model.Stream) error { func (d streamsDao) SetStreamNotLiveById(streamID uint) error { defer Cache.Clear() - return DB.Debug().Exec("UPDATE `streams` SET `live_now`='0' WHERE id = ?", streamID).Error + return DB.Model(model.Stream{}).Where("id = ?", streamID).Updates(map[string]interface{}{"live_now": 0}).Error + //return DB.Debug().Exec("UPDATE `streams` SET `live_now`='0' WHERE id = ?", streamID).Error } // SetStreamLiveNowTimestampById stores timestamp when stream is going live. diff --git a/go.work b/go.work index 71de48fe3..d1d9a8d2f 100644 --- a/go.work +++ b/go.work @@ -1,8 +1,9 @@ -go 1.21 +go 1.21.0 use ( . ./worker ./worker/edge vod-service + ./runner ) diff --git a/go.work.sum b/go.work.sum index a86b78d80..e89e0d21a 100644 --- a/go.work.sum +++ b/go.work.sum @@ -13,6 +13,8 @@ cloud.google.com/go/aiplatform v1.58.0 h1:xyCAfpI4yUMOQ4VtHN/bdmxPQ8xoEkTwFM1nbV cloud.google.com/go/aiplatform v1.58.0/go.mod h1:pwZMGvqe0JRkI1GWSZCtnAfrR4K1bv65IHILGA//VEU= cloud.google.com/go/analytics v0.21.6 h1:fnV7B8lqyEYxCU0LKk+vUL7mTlqRAq4uFlIthIdr/iA= cloud.google.com/go/analytics v0.21.6/go.mod h1:eiROFQKosh4hMaNhF85Oc9WO97Cpa7RggD40e/RBy8w= +cloud.google.com/go/analytics v0.22.0 h1:w8KIgW8NRUHFVKjpkwCpLaHsr685tJ+ckPStOaSCZz0= +cloud.google.com/go/analytics v0.22.0/go.mod h1:eiROFQKosh4hMaNhF85Oc9WO97Cpa7RggD40e/RBy8w= cloud.google.com/go/apigateway v1.6.4 h1:VVIxCtVerchHienSlaGzV6XJGtEM9828Erzyr3miUGs= cloud.google.com/go/apigateway v1.6.4/go.mod h1:0EpJlVGH5HwAN4VF4Iec8TAzGN1aQgbxAWGJsnPCGGY= cloud.google.com/go/apigeeconnect v1.6.4 h1:jSoGITWKgAj/ssVogNE9SdsTqcXnryPzsulENSRlusI= @@ -27,6 +29,8 @@ cloud.google.com/go/artifactregistry v1.14.6 h1:/hQaadYytMdA5zBh+RciIrXZQBWK4vN7 cloud.google.com/go/artifactregistry v1.14.6/go.mod h1:np9LSFotNWHcjnOgh8UVK0RFPCTUGbO0ve3384xyHfE= cloud.google.com/go/asset v1.16.0 h1:VjwWNtEVsbpXfJqZbb2RLOBzSgAjN69vf2UJADHnkxk= cloud.google.com/go/asset v1.16.0/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= +cloud.google.com/go/asset v1.17.0 h1:dLWfTnbwyrq/Kt8Tr2JiAbre1MEvS2Bl5cAMiYAy5Pg= +cloud.google.com/go/asset v1.17.0/go.mod h1:yYLfUD4wL4X589A9tYrv4rFrba0QlDeag0CMcM5ggXU= cloud.google.com/go/assuredworkloads v1.11.4 h1:FsLSkmYYeNuzDm8L4YPfLWV+lQaUrJmH5OuD37t1k20= cloud.google.com/go/assuredworkloads v1.11.4/go.mod h1:4pwwGNwy1RP0m+y12ef3Q/8PaiWrIDQ6nD2E8kvWI9U= cloud.google.com/go/automl v1.13.4 h1:i9tOKXX+1gE7+rHpWKjiuPfGBVIYoWvLNIGpWgPtF58= @@ -48,6 +52,8 @@ cloud.google.com/go/certificatemanager v1.7.4 h1:5YMQ3Q+dqGpwUZ9X5sipsOQ1fLPsxod cloud.google.com/go/certificatemanager v1.7.4/go.mod h1:FHAylPe/6IIKuaRmHbjbdLhGhVQ+CWHSD5Jq0k4+cCE= cloud.google.com/go/channel v1.17.3 h1:Rd4+fBrjiN6tZ4TR8R/38elkyEkz6oogGDr7jDyjmMY= cloud.google.com/go/channel v1.17.3/go.mod h1:QcEBuZLGGrUMm7kNj9IbU1ZfmJq2apotsV83hbxX7eE= +cloud.google.com/go/channel v1.17.4 h1:yYHOORIM+wkBy3EdwArg/WL7Lg+SoGzlKH9o3Bw2/jE= +cloud.google.com/go/channel v1.17.4/go.mod h1:QcEBuZLGGrUMm7kNj9IbU1ZfmJq2apotsV83hbxX7eE= cloud.google.com/go/cloudbuild v1.15.0 h1:9IHfEMWdCklJ1cwouoiQrnxmP0q3pH7JUt8Hqx4Qbck= cloud.google.com/go/cloudbuild v1.15.0/go.mod h1:eIXYWmRt3UtggLnFGx4JvXcMj4kShhVzGndL1LwleEM= cloud.google.com/go/clouddms v1.7.3 h1:xe/wJKz55VO1+L891a1EG9lVUgfHr9Ju/I3xh1nwF84= @@ -80,6 +86,8 @@ cloud.google.com/go/datalabeling v0.8.4 h1:zrq4uMmunf2KFDl/7dS6iCDBBAxBnKVDyw6+a cloud.google.com/go/datalabeling v0.8.4/go.mod h1:Z1z3E6LHtffBGrNUkKwbwbDxTiXEApLzIgmymj8A3S8= cloud.google.com/go/dataplex v1.13.0 h1:ACVOuxwe7gP0SqEso9SLyXbcZNk5l8hjcTX+XLntI5s= cloud.google.com/go/dataplex v1.13.0/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= +cloud.google.com/go/dataplex v1.14.0 h1:/WhVTR4v/L6ACKjlz/9CqkxkrVh2z7C44CLMUf0f60A= +cloud.google.com/go/dataplex v1.14.0/go.mod h1:mHJYQQ2VEJHsyoC0OdNyy988DvEbPhqFs5OOLffLX0c= cloud.google.com/go/dataproc v1.12.0 h1:W47qHL3W4BPkAIbk4SWmIERwsWBaNnWm0P2sdx3YgGU= cloud.google.com/go/dataproc v1.12.0/go.mod h1:zrF3aX0uV3ikkMz6z4uBbIKyhRITnxvr4i3IjKsKrw4= cloud.google.com/go/dataproc/v2 v2.3.0 h1:tTVP9tTxmc8fixxOd/8s6Q6Pz/+yzn7r7XdZHretQH0= @@ -95,6 +103,8 @@ cloud.google.com/go/deploy v1.16.0 h1:5OVjzm8MPC5kP+Ywbs0mdE0O7AXvAUXksSyHAyMFyM cloud.google.com/go/deploy v1.16.0/go.mod h1:e5XOUI5D+YGldyLNZ21wbp9S8otJbBE4i88PtO9x/2g= cloud.google.com/go/dialogflow v1.47.0 h1:tLCWad8HZhlyUNfDzDP5m+oH6h/1Uvw/ei7B9AnsWMk= cloud.google.com/go/dialogflow v1.47.0/go.mod h1:mHly4vU7cPXVweuB5R0zsYKPMzy240aQdAu06SqBbAQ= +cloud.google.com/go/dialogflow v1.48.0 h1:I7UsYowDdNhFI7RCix1uoThDp+8ULHByOo4n1T96y1A= +cloud.google.com/go/dialogflow v1.48.0/go.mod h1:mHly4vU7cPXVweuB5R0zsYKPMzy240aQdAu06SqBbAQ= cloud.google.com/go/dlp v1.11.1 h1:OFlXedmPP/5//X1hBEeq3D9kUVm9fb6ywYANlpv/EsQ= cloud.google.com/go/dlp v1.11.1/go.mod h1:/PA2EnioBeXTL/0hInwgj0rfsQb3lpE3R8XUJxqUNKI= cloud.google.com/go/documentai v1.23.7 h1:hlYieOXUwiJ7HpBR/vEPfr8nfSxveLVzbqbUkSK0c/4= @@ -125,6 +135,8 @@ cloud.google.com/go/gkehub v0.14.4 h1:J5tYUtb3r0cl2mM7+YHvV32eL+uZQ7lONyUZnPikCE cloud.google.com/go/gkehub v0.14.4/go.mod h1:Xispfu2MqnnFt8rV/2/3o73SK1snL8s9dYJ9G2oQMfc= cloud.google.com/go/gkemulticloud v1.0.3 h1:NmJsNX9uQ2CT78957xnjXZb26TDIMvv+d5W2vVUt0Pg= cloud.google.com/go/gkemulticloud v1.0.3/go.mod h1:7NpJBN94U6DY1xHIbsDqB2+TFZUfjLUKLjUX8NGLor0= +cloud.google.com/go/gkemulticloud v1.1.0 h1:C2Suwn3uPz+Yy0bxVjTlsMrUCaDovkgvfdyIa+EnUOU= +cloud.google.com/go/gkemulticloud v1.1.0/go.mod h1:7NpJBN94U6DY1xHIbsDqB2+TFZUfjLUKLjUX8NGLor0= cloud.google.com/go/grafeas v0.3.0 h1:oyTL/KjiUeBs9eYLw/40cpSZglUC+0F7X4iu/8t7NWs= cloud.google.com/go/grafeas v0.3.0/go.mod h1:P7hgN24EyONOTMyeJH6DxG4zD7fwiYa5Q6GUgyFSOU8= cloud.google.com/go/gsuiteaddons v1.6.4 h1:uuw2Xd37yHftViSI8J2hUcCS8S7SH3ZWH09sUDLW30Q= @@ -177,6 +189,8 @@ cloud.google.com/go/orchestration v1.8.4 h1:kgwZ2f6qMMYIVBtUGGoU8yjYWwMTHDanLwM/ cloud.google.com/go/orchestration v1.8.4/go.mod h1:d0lywZSVYtIoSZXb0iFjv9SaL13PGyVOKDxqGxEf/qI= cloud.google.com/go/orgpolicy v1.11.4 h1:RWuXQDr9GDYhjmrredQJC7aY7cbyqP9ZuLbq5GJGves= cloud.google.com/go/orgpolicy v1.11.4/go.mod h1:0+aNV/nrfoTQ4Mytv+Aw+stBDBjNf4d8fYRA9herfJI= +cloud.google.com/go/orgpolicy v1.12.0 h1:sab7cDiyfdthpAL0JkSpyw1C3mNqkXToVOhalm79PJQ= +cloud.google.com/go/orgpolicy v1.12.0/go.mod h1:0+aNV/nrfoTQ4Mytv+Aw+stBDBjNf4d8fYRA9herfJI= cloud.google.com/go/osconfig v1.12.4 h1:OrRCIYEAbrbXdhm13/JINn9pQchvTTIzgmOCA7uJw8I= cloud.google.com/go/osconfig v1.12.4/go.mod h1:B1qEwJ/jzqSRslvdOCI8Kdnp0gSng0xW4LOnIebQomA= cloud.google.com/go/oslogin v1.12.2 h1:NP/KgsD9+0r9hmHC5wKye0vJXVwdciv219DtYKYjgqE= @@ -240,6 +254,8 @@ cloud.google.com/go/trace v1.10.4 h1:2qOAuAzNezwW3QN+t41BtkDJOG42HywL73q8x/f6fnM cloud.google.com/go/trace v1.10.4/go.mod h1:Nso99EDIK8Mj5/zmB+iGr9dosS/bzWCJ8wGmE6TXNWY= cloud.google.com/go/translate v1.9.3 h1:t5WXTqlrk8VVJu/i3WrYQACjzYJiff5szARHiyqqPzI= cloud.google.com/go/translate v1.9.3/go.mod h1:Kbq9RggWsbqZ9W5YpM94Q1Xv4dshw/gr/SHfsl5yCZ0= +cloud.google.com/go/translate v1.10.0 h1:tncNaKmlZnayMMRX/mMM2d5AJftecznnxVBD4w070NI= +cloud.google.com/go/translate v1.10.0/go.mod h1:Kbq9RggWsbqZ9W5YpM94Q1Xv4dshw/gr/SHfsl5yCZ0= cloud.google.com/go/video v1.20.3 h1:Xrpbm2S9UFQ1pZEeJt9Vqm5t2T/z9y/M3rNXhFoo8Is= cloud.google.com/go/video v1.20.3/go.mod h1:TnH/mNZKVHeNtpamsSPygSR0iHtvrR/cW1/GDjN5+GU= cloud.google.com/go/videointelligence v1.11.4 h1:YS4j7lY0zxYyneTFXjBJUj2r4CFe/UoIi/PJG0Zt/Rg= @@ -277,6 +293,7 @@ github.com/alecthomas/kingpin/v2 v2.3.2 h1:H0aULhgmSzN8xQ3nX1uxtdlTHYoPLu5AhHxWr github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= @@ -320,6 +337,8 @@ github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XP github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0 h1:sDMmm+q/3+BukdIpxwO365v/Rbspp2Nt5XntgQRXq8Q= github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMux2sDi4oo5YOo= @@ -348,7 +367,7 @@ github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= @@ -373,6 +392,7 @@ github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/K github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= @@ -488,6 +508,7 @@ github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNf github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2 h1:JAEbJn3j/FrhdWA9jW8B5ajsLIjeuEHLi8xE4fk997o= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mediocregopher/radix/v3 v3.8.0 h1:HI8EgkaM7WzsrFpYAkOXIgUKbjNonb2Ne7K6Le61Pmg= @@ -565,12 +586,14 @@ github.com/smartystreets/assertions v1.13.0 h1:Dx1kYM01xsSqKPno3aqLnrwac2LetPvN2 github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spf13/cobra v1.2.1 h1:+KmjbUw1hriSNMF55oPrkZcb27aECyrj8V2ytv7kWDw= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tdewolff/minify/v2 v2.12.4 h1:kejsHQMM17n6/gwdw53qsi6lg0TGddZADVyQOz1KMdE= github.com/tdewolff/parse/v2 v2.6.4 h1:KCkDvNUMof10e3QExio9OPZJT8SbdKojLBumw8YZycQ= github.com/tdewolff/test v1.0.7 h1:8Vs0142DmPFW/bQeHRP3MV19m1gvndjUb1sn8yy74LM= github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= @@ -632,6 +655,7 @@ go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95a go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= @@ -646,8 +670,10 @@ golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= @@ -671,13 +697,17 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -688,6 +718,7 @@ golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= @@ -703,8 +734,11 @@ google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqv google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= +google.golang.org/genproto v0.0.0-20231212172506-995d672761c0/go.mod h1:l/k7rMz0vFTBPy+tFSGvXEd3z+BcoG1k7EHbqm+YBsY= google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg= google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0= +google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac h1:ZL/Teoy/ZGnzyrqK/Optxxp2pmVh+fmJ97slxSRyzUg= +google.golang.org/genproto v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:+Rvu7ElI+aLzyDQhpHMFMMltsD6m7nqpuWDd2CwJw3k= google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9 h1:m8v1xLLLzMe1m5P+gCTF8nJB9epwZQUBERm20Oy1poQ= google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= @@ -713,14 +747,18 @@ google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1: google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 h1:s1w3X6gQxwrLEpxnLd/qXTVLgQE2yXwaOaoa6IlY/+o= google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c= +google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917 h1:rcS6EyEaoCO52hQDupoSfrxI3R6C2Tq741is7X8OvnM= +google.golang.org/genproto/googleapis/api v0.0.0-20240102182953-50ed04b92917/go.mod h1:CmlNWB9lSezaYELKS5Ym1r44VrrbPUa7JTvw+6MbpJ0= google.golang.org/genproto/googleapis/bytestream v0.0.0-20231120223509-83a465c0220f h1:hL+1ptbhFoeL1HcROQ8OGXaqH0jYRRibgWQWco0/Ugc= google.golang.org/genproto/googleapis/bytestream v0.0.0-20231120223509-83a465c0220f/go.mod h1:iIgEblxoG4klcXsG0d9cpoxJ4xndv6+1FkDROCHhPRI= google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= google.golang.org/genproto/googleapis/rpc v0.0.0-20231211222908-989df2bf70f3/go.mod h1:eJVxU6o+4G1PSczBr85xmyvSNYAKvAYgkub40YGomFM= google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917/go.mod h1:xtjpI3tXFPP051KaWnhvxkiubL/6dJ18vLVf7q2pTOU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/grpc v1.60.0/go.mod h1:OlCHIeLYqSSsLi6i49B5QGdzaMZK9+M7LXN2FKz4eGM= @@ -728,10 +766,10 @@ google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQf google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gorm.io/gorm v1.25.1 h1:nsSALe5Pr+cM3V1qwwQ7rOkw+6UeLrX5O4v3llhHa64= honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8= diff --git a/mock_dao/runner.go b/mock_dao/runner.go new file mode 100644 index 000000000..b8d947a68 --- /dev/null +++ b/mock_dao/runner.go @@ -0,0 +1,79 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: runner.go + +// Package mock_dao is a generated GoMock package. +package mock_dao + +import ( + context "context" + reflect "reflect" + + model "github.com/TUM-Dev/gocast/model" + gomock "github.com/golang/mock/gomock" +) + +// MockRunnerDao is a mock of RunnerDao interface. +type MockRunnerDao struct { + ctrl *gomock.Controller + recorder *MockRunnerDaoMockRecorder +} + +// MockRunnerDaoMockRecorder is the mock recorder for MockRunnerDao. +type MockRunnerDaoMockRecorder struct { + mock *MockRunnerDao +} + +// NewMockRunnerDao creates a new mock instance. +func NewMockRunnerDao(ctrl *gomock.Controller) *MockRunnerDao { + mock := &MockRunnerDao{ctrl: ctrl} + mock.recorder = &MockRunnerDaoMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockRunnerDao) EXPECT() *MockRunnerDaoMockRecorder { + return m.recorder +} + +// Create mocks base method. +func (m *MockRunnerDao) Create(arg0 context.Context, arg1 *model.Runner) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create. +func (mr *MockRunnerDaoMockRecorder) Create(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockRunnerDao)(nil).Create), arg0, arg1) +} + +// Delete mocks base method. +func (m *MockRunnerDao) Delete(arg0 context.Context, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Delete", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// Delete indicates an expected call of Delete. +func (mr *MockRunnerDaoMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockRunnerDao)(nil).Delete), arg0, arg1) +} + +// Get mocks base method. +func (m *MockRunnerDao) Get(arg0 context.Context, arg1 string) (model.Runner, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", arg0, arg1) + ret0, _ := ret[0].(model.Runner) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Get indicates an expected call of Get. +func (mr *MockRunnerDaoMockRecorder) Get(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRunnerDao)(nil).Get), arg0, arg1) +} diff --git a/mock_dao/streams.go b/mock_dao/streams.go index 1a92479b7..959e449e6 100644 --- a/mock_dao/streams.go +++ b/mock_dao/streams.go @@ -527,6 +527,20 @@ func (mr *MockStreamsDaoMockRecorder) SetStreamNotLiveById(streamID interface{}) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetStreamNotLiveById", reflect.TypeOf((*MockStreamsDao)(nil).SetStreamNotLiveById), streamID) } +// SetStreamRequested mocks base method. +func (m *MockStreamsDao) SetStreamRequested(stream model.Stream) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "SetStreamRequested", stream) + ret0, _ := ret[0].(error) + return ret0 +} + +// SetStreamRequested indicates an expected call of SetStreamRequested. +func (mr *MockStreamsDaoMockRecorder) SetStreamRequested(stream interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetStreamRequested", reflect.TypeOf((*MockStreamsDao)(nil).SetStreamRequested), stream) +} + // ToggleVisibility mocks base method. func (m *MockStreamsDao) ToggleVisibility(streamId uint, private bool) error { m.ctrl.T.Helper() diff --git a/model/action.go b/model/action.go new file mode 100644 index 000000000..9d065af0b --- /dev/null +++ b/model/action.go @@ -0,0 +1,107 @@ +package model + +import ( + "errors" + "gorm.io/gorm" + "time" +) + +const ( + completed = iota + running + failed + awaiting + ignored +) + +type Action struct { + gorm.Model + + AllRunners []Runner `gorm:"many2many:action_runners;"` + JobID uint + Type string `gorm:"not null"` + Values string + Description string + assignedDate time.Time + Start time.Time + End time.Time + Status int `gorm:"not null;default:3"` +} + +type Values struct { +} + +func (a *Action) BeforeCreate(*gorm.DB) (err error) { + if a.Type == "" { + return errors.New("type needs to be assigned, unnecessary creation") + } + a.Status = awaiting + return nil +} + +func (a *Action) SetToCompleted() { + a.Status = completed +} + +func (a *Action) SetToRunning() { + a.Status = running +} + +func (a *Action) SetToFailed() { + a.Status = failed +} + +func (a *Action) SetToAwaiting() { + a.Status = awaiting +} + +func (a *Action) SetToIgnored() { + a.Status = ignored +} + +func (a *Action) IsCompleted() bool { + return a.Status == completed +} + +func (a *Action) GetCurrentRunner() (*Runner, error) { + if len(a.AllRunners) == 0 { + return nil, errors.New("no runner assigned") + } + return &a.AllRunners[len(a.AllRunners)-1], nil +} + +func (a *Action) AssignRunner(runner Runner) { + a.AllRunners = append(a.AllRunners, runner) +} + +func (a *Action) GetValues() string { + return a.Values +} + +func (a *Action) GetDescription() string { + return a.Description +} + +func (a *Action) GetStatus() int { + return a.Status +} + +func (a *Action) GetID() uint { + return a.ID +} + +func (a *Action) GetType() string { + return a.Type +} + +func (a *Action) GetAssignedDate() time.Time { + return a.assignedDate +} + +func (a *Action) GetStart() time.Time { + return a.Start +} + +func (a *Action) GetEnd() time.Time { + return a.End +} diff --git a/model/job.go b/model/job.go new file mode 100644 index 000000000..3e9eccbce --- /dev/null +++ b/model/job.go @@ -0,0 +1,73 @@ +package model + +import ( + "errors" + "gorm.io/gorm" + "time" +) + +type Job struct { + gorm.Model + + Actions []Action `gorm:"ForeignKey:JobID"` + Runners []Runner `gorm:"many2many:job_runner"` + Description string + Completed bool + Start time.Time + End time.Time +} + +func (j *Job) BeforeCreate(*gorm.DB) (err error) { + if j.Actions == nil { + return errors.New("job has no actions, unnecessary job creation") + } + if !(j.Start.IsZero() || j.End.IsZero() || j.Start.After(time.Now())) { + return errors.New("job has no valid time set. " + + "Please make sure the time for each start and end value is correct") + } + return nil +} + +func (j *Job) GetAllActions() ([]Action, error) { + if j.Actions == nil { + return nil, errors.New("no actions found") + } + return j.Actions, nil +} + +func (j *Job) GetNextAction() (*Action, error) { + if j.Actions == nil { + return nil, errors.New("no actions found") + } else if j.Actions[0].Status != awaiting { + j.Actions = j.Actions[1:] + return nil, errors.New("action not in awaiting, not pushed") + } + if len(j.Actions) == 0 { + j.Completed = true + return nil, nil + } + action := j.Actions[0] + j.Actions = j.Actions[1:] + return &action, nil +} + +func (j *Job) GetAllRunners() ([]Runner, error) { + if j.Runners == nil { + return nil, errors.New("no actions found") + } + return j.Runners, nil +} + +func (j *Job) SetToCompleted() error { + if j.Completed == true { + return errors.New("job already completed") + } + j.Completed = true + + return nil +} + +func (j *Job) AddAction(a Action) error { + j.Actions = append(j.Actions, a) + return nil +} diff --git a/model/runner.go b/model/runner.go new file mode 100644 index 000000000..ab592e7db --- /dev/null +++ b/model/runner.go @@ -0,0 +1,58 @@ +package model + +import ( + "context" + "errors" + "gorm.io/gorm" + "time" +) + +// Runner represents a runner that creates, converts and postprocessing streams and does other heavy lifting. +type Runner struct { + gorm.Model + Hostname string `gorm:"UniqueKey;type:varchar(80)"` + Port int + LastSeen time.Time + Alive bool + + Status string + Workload uint + CPU string + Memory string + Disk string + Uptime string + Version string + Actions string +} + +// BeforeCreate returns errors if hostnames and ports of workers are invalid. +func (r *Runner) BeforeCreate(tx *gorm.DB) (err error) { + if r.Hostname == "" { + return errors.New("missing hostname") + } + if r.Port < 0 || r.Port > 65535 { + return errors.New("port out of range") + } + return nil +} + +// UpdateStats SendHeartbeat updates the last seen time of the runner and gives runner stats +func (r *Runner) UpdateStats(tx *gorm.DB, ctx context.Context) (bool, error) { + newStats := ctx.Value("newStats").(Runner) + err := tx.WithContext(ctx).Model(&r).Updates(newStats).Error + if err != nil { + return false, err + } + + return true, nil +} + +func (r *Runner) IsAlive() bool { + r.Alive = r.LastSeen.After(time.Now().Add(time.Minute * -2)) + if r.Alive { + r.Status = "Alive" + } else { + r.Status = "Dead" + } + return r.Alive +} diff --git a/model/stream.go b/model/stream.go index 4231a17af..d1d9d736d 100755 --- a/model/stream.go +++ b/model/stream.go @@ -56,7 +56,8 @@ type Stream struct { TranscodingProgresses []TranscodingProgress `gorm:"foreignKey:StreamID"` Private bool `gorm:"not null;default:false"` - Watched bool `gorm:"-"` // Used to determine if stream is watched when loaded for a specific user. + Requested bool `gorm:"default:false"` + Watched bool `gorm:"-"` // Used to determine if stream is watched when loaded for a specific user. } type DownloadableVod struct { diff --git a/runner/.gitignore b/runner/.gitignore new file mode 100644 index 000000000..31eb45db6 --- /dev/null +++ b/runner/.gitignore @@ -0,0 +1 @@ +mediamtx diff --git a/runner/Dockerfile b/runner/Dockerfile new file mode 100644 index 000000000..309827373 --- /dev/null +++ b/runner/Dockerfile @@ -0,0 +1,27 @@ +FROM amd64/golang:1.21-alpine3.18 as builder + +WORKDIR /go/src/github.com/TUM-Dev/gocast/runner +COPY . . + +RUN GO111MODULE=on go mod download +# bundle version into binary if specified in build-args, dev otherwise. +ARG version=dev +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -ldflags "-w -extldflags '-static' -X main.V=${version}" -o /runner cmd/runner/main.go + +FROM bluenviron/mediamtx:1.1.0 as mediamtx + +FROM alpine:3.18 +ADD entrypoint.sh /entrypoint.sh +ADD mediamtx.yml /mediamtx.yml +RUN chmod +x /entrypoint.sh + +RUN apk add --no-cache \ + ffmpeg \ + tzdata + +COPY --from=builder /runner /runner +RUN chmod +x /runner +COPY --from=mediamtx /mediamtx /mediamtx +RUN chmod +x /mediamtx + +CMD ["/entrypoint.sh"] \ No newline at end of file diff --git a/runner/Makefile b/runner/Makefile new file mode 100644 index 000000000..541ff27f2 --- /dev/null +++ b/runner/Makefile @@ -0,0 +1,36 @@ +.PHONY: all +all: build + +VERSION := $(shell git rev-parse --short origin/HEAD) + +.PHONY: protoGen +protoGen: + protoc ./runner.proto --go-grpc_out=.. --go_out=.. + +.PHONY: build +build: deps + go build -o main -ldflags="-X 'main.VersionTag=$(VERSION)'" cmd/runner/main.go + +.PHONY: deps +deps: + go get ./... + +.PHONY: install +install: + mv main /bin/runner + +.PHONY: clean +clean: + rm -f main + +.PHONY: test +test: + go test -race ./... + +.PHONY: run +run: + go run cmd/runner/main.go + +.PHONY: lint +lint: + golangci-lint run diff --git a/runner/ServerHandler.go b/runner/ServerHandler.go new file mode 100644 index 000000000..2055ca5e5 --- /dev/null +++ b/runner/ServerHandler.go @@ -0,0 +1,278 @@ +package runner + +import ( + "context" + "fmt" + "github.com/tum-dev/gocast/runner/protobuf" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/protobuf/types/known/timestamppb" + "log/slog" + "os" + "time" +) + +const registerRetries = 5 + +func (r *Runner) RegisterWithGocast(retries int) { + r.log.Debug("connecting with gocast", slog.Group("conn", "host", r.cfg.GocastServer, "retriesLeft", retries)) + if retries == 0 { + r.log.Error("no more retries left, can't connect to gocast") + os.Exit(1) + } + con, err := r.dialIn() + if err != nil { + r.log.Warn("error connecting to gocast", "error", err, "sleeping(s)", registerRetries-retries) + time.Sleep(time.Second * time.Duration(registerRetries-retries)) + r.RegisterWithGocast(retries - 1) + r.ReadDiagnostics(5) + return + } + _, err = con.Register(context.Background(), &protobuf.RegisterRequest{Hostname: r.cfg.Hostname, Port: int32(r.cfg.Port)}) + if err != nil { + r.log.Warn("error registering with gocast", "error", err, "sleeping(s)", registerRetries-retries) + time.Sleep(time.Second * time.Duration(registerRetries-retries)) + r.RegisterWithGocast(retries - 1) + r.ReadDiagnostics(5) + return + } + go func() { + for { + r.ReadDiagnostics(5) + time.Sleep(time.Minute) + } + }() +} + +// dialIn connects to manager instance and returns a client +func (r *Runner) dialIn() (protobuf.FromRunnerClient, error) { + credentials := insecure.NewCredentials() + conn, err := grpc.Dial(r.cfg.GocastServer, grpc.WithTransportCredentials(credentials)) + if err != nil { + return nil, err + } + return protobuf.NewFromRunnerClient(conn), nil +} + +func (r *Runner) ReadDiagnostics(retries int) { + + r.log.Info("Started Sending Diagnostic Data", "retriesLeft", retries) + + if retries == 0 { + return + } + err := r.stats.Update() + if err != nil { + r.ReadDiagnostics(retries - 1) + return + } + cpu := r.stats.GetCpuStr() + memory := r.stats.GetMemStr() + disk := r.stats.GetDiskStr() + uptime := time.Now().Sub(r.StartTime).String() + con, err := r.dialIn() + if err != nil { + r.log.Warn("couldn't dial into server", "error", err, "sleeping(s)", 5-retries) + time.Sleep(time.Second * time.Duration(5-retries)) + r.ReadDiagnostics(retries - 1) + return + } + actions := "" + for id, _ := range r.activeActions { + actions += fmt.Sprintln(id + ",") + } + + _, err = con.Heartbeat(context.Background(), &protobuf.HeartbeatRequest{ + Hostname: r.cfg.Hostname, + Port: int32(r.cfg.Port), + LastSeen: timestamppb.New(time.Now()), + Status: "Alive", + Workload: uint32(len(r.activeActions)), + CPU: cpu, + Memory: memory, + Disk: disk, + Uptime: uptime, + Version: r.cfg.Version, + CurrentAction: actions, + }) + if err != nil { + r.log.Warn("Error sending the heartbeat", "error", err, "sleeping(s)", 5-retries) + time.Sleep(time.Second * time.Duration(5-retries)) + r.ReadDiagnostics(retries - 1) + return + } + r.log.Info("Successfully sent heartbeat", "retriesLeft", retries) +} + +func (r *Runner) handleSelfStream(ctx context.Context, retries int) { + r.log.Info("Started Requesting Self Stream", "retriesLeft", retries) + + streamKey := ctx.Value("streamKey").(string) + + if retries == 0 { + r.log.Error("no more retries left, can't start Self Stream") + return + } + + con, err := r.dialIn() + if err != nil { + r.log.Warn("error connecting to gocast", "error", err, "sleeping(s)", 5-retries) + time.Sleep(time.Second * time.Duration(5-retries)) + r.handleSelfStream(ctx, retries-1) + return + } + + _, err = con.RequestSelfStream(context.Background(), &protobuf.SelfStreamRequest{ + StreamKey: streamKey, + }) +} + +func (r *Runner) RequestSelfStream(ctx context.Context, req *protobuf.SelfStreamRequest) *protobuf.SelfStreamResponse { + panic("implement me") +} + +func (r *Runner) NotifyStreamStarted(ctx context.Context, started *protobuf.StreamStarted) *protobuf.Status { + + r.log.Info("Got called with stream start notify", "request", started) + + con, err := r.dialIn() + if err != nil { + r.log.Warn("error connecting to gocast", "error", err) + time.Sleep(time.Second * 5) + return r.NotifyStreamStarted(ctx, started) + + } + + resp, err := con.NotifyStreamStarted(context.Background(), started) + if err != nil { + r.log.Warn("error sending stream started", "error", err) + time.Sleep(time.Second * 5) + return r.NotifyStreamStarted(ctx, started) + } + return resp +} + +func (r *Runner) NotifyVoDUploadFinished(ctx context.Context, request *protobuf.VoDUploadFinished) *protobuf.Status { + //TODO: Test me + + r.log.Info("Got called with VoD upload finished notify", "request", request) + + con, err := r.dialIn() + if err != nil { + r.log.Warn("error connecting to gocast", "error", err) + time.Sleep(time.Second * 5) + return r.NotifyVoDUploadFinished(ctx, request) + } + + resp, err := con.NotifyVoDUploadFinished(ctx, request) + if err != nil { + r.log.Warn("error sending VoD upload finished", "error", err) + time.Sleep(time.Second * 5) + return r.NotifyVoDUploadFinished(ctx, request) + + } + return resp +} + +func (r *Runner) NotifySilenceResults(ctx context.Context, request *protobuf.SilenceResults) *protobuf.Status { + //TODO: Test me + + r.log.Info("Got called with Silence Results notify", "request", request) + + con, err := r.dialIn() + if err != nil { + r.log.Warn("error connecting to gocast", "error", err) + time.Sleep(time.Second * 5) + return r.NotifySilenceResults(ctx, request) + } + + resp, err := con.NotifySilenceResults(ctx, request) + if err != nil { + r.log.Warn("error sending silence results", "error", err) + time.Sleep(time.Second * 5) + return r.NotifySilenceResults(ctx, request) + } + return resp +} + +func (r *Runner) NotifyStreamEnded(ctx context.Context, request *protobuf.StreamEnded) *protobuf.Status { + + //TODO: Test me + + r.log.Info("Got called with Stream end notify", "request", request) + + con, err := r.dialIn() + if err != nil { + r.log.Warn("error connecting to gocast", "error", err) + time.Sleep(time.Second * 5) + return r.NotifyStreamEnded(ctx, request) + } + + resp, err := con.NotifyStreamEnded(ctx, request) + if err != nil { + r.log.Warn("error sending stream end", "error", err) + time.Sleep(time.Second * 5) + return r.NotifyStreamEnded(ctx, request) + } + return resp +} + +func (r *Runner) NotifyThumbnailsFinished(ctx context.Context, request *protobuf.ThumbnailsFinished) *protobuf.Status { + //TODO: Test me + r.log.Info("Got called with Thumbnails Finished notify", "request", request) + + con, err := r.dialIn() + if err != nil { + r.log.Warn("error connecting to gocast", "error", err) + time.Sleep(time.Second * 5) + return r.NotifyThumbnailsFinished(ctx, request) + } + + resp, err := con.NotifyThumbnailsFinished(ctx, request) + if err != nil { + r.log.Warn("error sending thumbnails finished", "error", err) + time.Sleep(time.Second * 5) + return r.NotifyThumbnailsFinished(ctx, request) + } + return resp +} + +func (r *Runner) NotifyTranscodingFailure(ctx context.Context, request *protobuf.TranscodingFailureNotification) *protobuf.Status { + //TODO: Test me + r.log.Info("Got called with Transcoding Failure notify", "request", request) + + con, err := r.dialIn() + if err != nil { + r.log.Warn("error connecting to gocast", "error", err) + time.Sleep(time.Second * 5) + return r.NotifyTranscodingFailure(ctx, request) + } + + resp, err := con.NotifyTranscodingFailure(ctx, request) + if err != nil { + r.log.Warn("error sending transcoding failure", "error", err) + time.Sleep(time.Second * 5) + return r.NotifyTranscodingFailure(ctx, request) + } + return resp +} + +func (r *Runner) GetStreamInfoForUpload(ctx context.Context, request *protobuf.StreamInfoForUploadRequest) *protobuf.StreamInfoForUploadResponse { + //TODO: Test me + r.log.Info("Got called with Stream Info For Upload", "request", request) + + con, err := r.dialIn() + if err != nil { + r.log.Warn("error connecting to gocast", "error", err) + time.Sleep(time.Second * 5) + return r.GetStreamInfoForUpload(ctx, request) + } + + resp, err := con.GetStreamInfoForUpload(ctx, request) + if err != nil { + r.log.Warn("error getting stream info for upload", "error", err) + time.Sleep(time.Second * 5) + return r.GetStreamInfoForUpload(ctx, request) + } + return resp +} diff --git a/runner/actions/actions.go b/runner/actions/actions.go new file mode 100644 index 000000000..92ad35195 --- /dev/null +++ b/runner/actions/actions.go @@ -0,0 +1,63 @@ +package actions + +import ( + "context" + "errors" + "fmt" + "github.com/tum-dev/gocast/runner/config" + "github.com/tum-dev/gocast/runner/pkg/ServerInterface" + "log/slog" + "path" +) + +var ( + ErrActionInputWrongType = errors.New("action input has wrong type") + ErrRequiredContextValNotFound = errors.New("required context value not found") +) + +type ActionProvider struct { + Log *slog.Logger + Cmd config.CmdList + Server ServerInterface.ServerInf + SegmentDir string // for storing live hls segments locally. This should be fast storage (e.g. ssd). + RecDir string // for storing recordings locally. + MassDir string // for storing final files like Thumbnails, mp4s, ... Mass storage like Ceph. +} + +func (a *ActionProvider) GetRecDir(courseID, streamID uint64, version string) string { + return path.Join(a.RecDir, fmt.Sprintf("%d", courseID), fmt.Sprintf("%d", streamID), version) +} + +func (a *ActionProvider) GetLiveDir(courseID, streamID uint64, version string) string { + return path.Join(a.SegmentDir, fmt.Sprintf("%d", courseID), fmt.Sprintf("%d", streamID), version) +} + +func (a *ActionProvider) GetMassDir(courseID, streamID uint64, version string) string { + return path.Join(a.MassDir, fmt.Sprintf("%d", courseID), fmt.Sprintf("%d", streamID), version) +} + +type ActionType string + +const ( + PrepareAction ActionType = "prepare" + StreamAction = "stream" + TranscodeAction = "transcode" + UploadAction = "upload" + ThumbnailAction = "thumbnail" + SelfStreamAction = "selfstream" + SilenceDetectAction = "silence_detect" +) + +type Action struct { + Type ActionType + Cancel context.CancelCauseFunc + Canceled bool + + ActionFn ActionFn +} + +type ActionFn func(ctx context.Context, log *slog.Logger) (context.Context, error) + +func set(ctx context.Context, key string, val interface{}) context.Context { + return context.WithValue(ctx, key, val) +} diff --git a/runner/actions/prepare.go b/runner/actions/prepare.go new file mode 100644 index 000000000..db77411f3 --- /dev/null +++ b/runner/actions/prepare.go @@ -0,0 +1,43 @@ +package actions + +import ( + "context" + "fmt" + "log/slog" + "os" +) + +// PrepareAction prepares the directory structure for the stream and vod. +func (a *ActionProvider) PrepareAction() *Action { + return &Action{ + Type: PrepareAction, + ActionFn: func(ctx context.Context, log *slog.Logger) (context.Context, error) { + streamID, ok := ctx.Value("stream").(uint64) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain stream", ErrRequiredContextValNotFound) + } + courseID, ok := ctx.Value("course").(uint64) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain course", ErrRequiredContextValNotFound) + } + version, ok := ctx.Value("version").(string) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain version", ErrRequiredContextValNotFound) + } + + dirs := []string{ + a.GetRecDir(courseID, streamID, version), + a.GetLiveDir(courseID, streamID, version), + a.GetMassDir(courseID, streamID, version), + } + for _, dir := range dirs { + log.Info("creating directory", "path", dir) + err := os.MkdirAll(dir, 0755) + if err != nil { + return ctx, fmt.Errorf("create directory: %w", err) + } + } + return ctx, nil + }, + } +} diff --git a/runner/actions/selfstream.go b/runner/actions/selfstream.go new file mode 100644 index 000000000..2b0b502bd --- /dev/null +++ b/runner/actions/selfstream.go @@ -0,0 +1,16 @@ +package actions + +import ( + "context" + "log/slog" +) + +func (a *ActionProvider) SelfStreamAction() *Action { + return &Action{ + Type: SelfStreamAction, + ActionFn: func(ctx context.Context, log *slog.Logger) (context.Context, error) { + + return ctx, nil + }, + } +} diff --git a/runner/actions/silence_detect.go b/runner/actions/silence_detect.go new file mode 100644 index 000000000..703f2a4f2 --- /dev/null +++ b/runner/actions/silence_detect.go @@ -0,0 +1,114 @@ +package actions + +import ( + "context" + "fmt" + "github.com/tum-dev/gocast/runner/protobuf" + "log/slog" + "os/exec" + "strconv" + "strings" +) + +func (a *ActionProvider) SilenceDetectAction() *Action { + return &Action{ + Type: SilenceDetectAction, + ActionFn: func(ctx context.Context, log *slog.Logger) (context.Context, error) { + streamID, ok := ctx.Value("stream").(uint64) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain stream", ErrRequiredContextValNotFound) + } + + filename, ok := ctx.Value("outputFilename").(string) + if !ok { + errMsg := "no transcoded file to process for silence detection" + log.Error(errMsg) + return ctx, fmt.Errorf(errMsg) + } + + log.Info("Start detecting silence", "file", filename) + cmd := fmt.Sprintf(a.Cmd.SilenceDetect, filename) + c := exec.CommandContext(ctx, "ffmpeg", strings.Split(cmd, " ")...) + output, err := c.CombinedOutput() + if err != nil { + log.Error("Error executing command", "error", err) + return ctx, err + } + + silences, err := parseSilence(string(output)) + if err != nil { + log.Error("Error parsing silence", "error", err) + return ctx, err + } + + starts, ends := postprocess(silences) + log.Info("Silences detected", "file", filename, "silences", silences) + a.Server.NotifySilenceResults(ctx, &protobuf.SilenceResults{ + RunnerID: "0", // TODO: replace with runner ID + StreamID: uint32(streamID), + Starts: starts, + Ends: ends, + }) + + return ctx, nil + }, + } +} + +func parseSilence(output string) ([]silence, error) { + var silences []silence + lines := strings.Split(output, "\n") + for _, line := range lines { + if strings.Contains(line, "silence_start:") { + start, err := strconv.ParseFloat(strings.Split(line, "silence_start: ")[1], 32) + if err != nil { + return nil, err + } + silences = append(silences, silence{ + Start: uint32(start), + End: 0, + }) + } else if strings.Contains(line, "silence_end:") { + end, err := strconv.ParseFloat(strings.Split(strings.Split(line, "silence_end: ")[1], " |")[0], 32) + if err != nil || silences == nil || len(silences) == 0 { + return nil, err + } + silences[len(silences)-1].End = uint32(end) + } + } + return silences, nil +} + +// postprocess merges short durations of silence into units of silence, +// and returns starts and ends as two separate arrays +func postprocess(silences []silence) ([]uint32, []uint32) { + if len(silences) >= 2 { + if silences[0].Start < 30 { + silences[0].Start = 0 + } + var newSilences []silence + newSilences = append(newSilences, silences[0]) + for i := 1; i < len(silences); i++ { + if silences[i].Start-newSilences[len(newSilences)-1].End < 30 { + newSilences[len(newSilences)-1].End = silences[i].End + } else { + newSilences = append(newSilences, silences[i]) + } + } + silences = newSilences + } + + var starts []uint32 + var ends []uint32 + for i := 1; i < len(silences); i++ { + starts = append(starts, silences[i].Start) + ends = append(ends, silences[i].End) + } + + return starts, ends +} + +type silence struct { + Start uint32 + End uint32 +} diff --git a/runner/actions/stream.go b/runner/actions/stream.go new file mode 100644 index 000000000..7a5c1938a --- /dev/null +++ b/runner/actions/stream.go @@ -0,0 +1,137 @@ +package actions + +import ( + "context" + "fmt" + "github.com/tum-dev/gocast/runner/protobuf" + "log/slog" + "os" + "os/exec" + "path/filepath" + "strings" + "time" +) + +//var edgeTemplate = "%s://%s/live/%s/%d-%s/playlist.m3u8" // e.g. "https://stream.domain.com/workerhostname123/1-COMB/playlist.m3u8" + +// StreamAction streams a video. in is ignored. out is a []string containing the filenames of the recorded stream. +// ctx must contain the following values: +// - streamID (uint64) // e.g. 1 +// - courseID (uint64) // e.g. 1 +// - version (string) // e.g. "PRES", "CAM", "COMB" +// - source (string) // e.g. "rtmp://localhost:1935/live/abc123" for selfstreams or "rtsp://1.2.3.4/extron1" for auditoriums +// - end (time.Time) // the end of the stream for auditoriums or an end date far in the future for selfstreams. +// after StreamAction is done, the following values are set in ctx: +// - files ([]string) // a list of files that were created during the stream +func (a *ActionProvider) StreamAction() *Action { + return &Action{ + Type: StreamAction, + ActionFn: func(ctx context.Context, log *slog.Logger) (context.Context, error) { + // files will contain all files that were created during the stream + var files []string + + hostname, ok := ctx.Value("Hostname").(string) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain hostname", ErrRequiredContextValNotFound) + } + streamID, ok := ctx.Value("stream").(uint64) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain stream", ErrRequiredContextValNotFound) + } + courseID, ok := ctx.Value("course").(uint64) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain courseID", ErrRequiredContextValNotFound) + } + version, ok := ctx.Value("version").(string) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain version", ErrRequiredContextValNotFound) + } + source, ok := ctx.Value("source").(string) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain source", ErrRequiredContextValNotFound) + } + end, ok := ctx.Value("end").(time.Time) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain end", ErrRequiredContextValNotFound) + } + log.Info("streaming", "source", source, "now", time.Now(), "end", end, "before", time.Now().Before(end)) + + //endingTime := time.Now().Add(time.Second * time.Duration(end.Second())) + log.Info("streaming until", "end", end) + + streamAttempt := 0 + for time.Now().Before(end) && ctx.Err() == nil { + streamAttempt++ + filename := filepath.Join(a.GetRecDir(courseID, streamID, version), fmt.Sprintf("%d.ts", streamAttempt)) + files = append(files, filename) + livePlaylist := filepath.Join(a.GetLiveDir(courseID, streamID, version), end.Format("15-04-05"), "playlist.m3u8") + _, err := os.Stat(a.GetLiveDir(courseID, streamID, version) + "/" + end.Format("15-04-05")) + if err != nil { + if os.IsNotExist(err) { + err := os.Mkdir(a.GetLiveDir(courseID, streamID, version)+"/"+end.Format("15-04-05"), 0700) + if err != nil { + log.Warn("streamAction: stream folder couldn't be created", err) + time.Sleep(5 * time.Second) // little backoff to prevent dossing source + continue + } + } + } + + src := "" + if strings.HasPrefix(source, "rtsp") { + src += "-rtsp_transport tcp" + } else if strings.HasPrefix(source, "rtmp") { + src += "-rw_timeout 5000000" // timeout selfstream s after 5 seconds of no data + } else { + src += "-re" // read input at native framerate, e.g. when streaming a file in realtime + } + + log.Info("streaming", "source", source, "end", time.Until(end).Seconds()) + + //changing the end variable from a date to a duration and adding the duration to the current time + cmd := fmt.Sprintf(a.Cmd.Stream, src, time.Until(end).Seconds(), source, filename, filepath.Join(a.GetLiveDir(courseID, streamID, version), end.Format("15-04-05")), livePlaylist) + + c := exec.CommandContext(ctx, "ffmpeg", strings.Split(cmd, " ")...) + c.Stderr = os.Stderr + log.Info("constructed stream command", "cmd", c.String()) + + err = c.Start() + if err != nil { + log.Warn("streamAction: ", err) + time.Sleep(5 * time.Second) // little backoff to prevent dossing source + continue + } + + //this string is the local runner url that will be passed to TUMLive + + localhls := fmt.Sprintf("%v/%d/%d/%s/%s/playlist.m3u8", hostname, courseID, streamID, version, end.Format("15-04-05")) + + resp := a.Server.NotifyStreamStarted(ctx, &protobuf.StreamStarted{ + Hostname: hostname, + StreamID: uint32(streamID), + CourseID: uint32(courseID), + Version: version, + HLSUrl: localhls, + }) + if resp.Ok != true { + log.Warn("streamAction: NotifyStreamStarted failed") + time.Sleep(5 * time.Second) // little backoff to prevent dossing source + continue + } + err = c.Wait() + if err != nil { + log.Warn("stream command exited", "err", err) + time.Sleep(5 * time.Second) // little backoff to prevent dossing source + continue + } + log.Info("stream finished. now sending notification") + resp = a.Server.NotifyStreamEnded(ctx, &protobuf.StreamEnded{ + RunnerID: hostname, + StreamID: uint32(streamID), + CourseID: uint32(courseID), + }) + } + return set(ctx, "files", files), nil + }, + } +} diff --git a/runner/actions/thumbnail.go b/runner/actions/thumbnail.go new file mode 100644 index 000000000..43ccf404e --- /dev/null +++ b/runner/actions/thumbnail.go @@ -0,0 +1,95 @@ +package actions + +import ( + "context" + "fmt" + "github.com/joschahenningsen/thumbgen" + "io" + "log/slog" + "os" +) + +const ( + //ThumbWidth = 160 + LargeThumbWidth = 720 + Compression = 90 +) + +// GenerateVideoThumbnail generate a Thumbnail from the stream the runner just run. +// it will also check if it was the first to generate a thumbnail for the stream. if it wasn't, +// it will combine the two generated thumbnails into one/* +func (a *ActionProvider) GenerateVideoThumbnail() *Action { + return &Action{ + Type: ThumbnailAction, + ActionFn: func(ctx context.Context, log *slog.Logger) (context.Context, error) { + pathForThumb := a.MassDir + pathForRec := a.RecDir + nameOfFile := fmt.Sprintf("%x-thumb-1", ctx.Value("")) + g, err := thumbgen.New(pathForRec, LargeThumbWidth, 1, "", thumbgen.WithJpegCompression(Compression)) + if err != nil { + log.Error("couldn't generate new thumbnail generator") + return ctx, err + } + file, err := g.GenerateOne() + if err != nil { + log.Error("couldn't create a Thumbnail in the middle of the Stream") + return ctx, err + } + thumb, err := os.Open(file) + if err != nil { + log.Error("couldn't open the Thumbnail") + return ctx, err + } + if _, err := os.Stat(fmt.Sprintf("%x", pathForThumb)); os.IsNotExist(err) { + openFile, err := os.OpenFile(fmt.Sprintf("%x/%x", pathForThumb, nameOfFile), os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Error("couldn't open the file for the Thumbnail") + return ctx, err + } + defer openFile.Close() + _, err = io.Copy(openFile, thumb) + if err != nil { + log.Error("couldn't copy the Thumbnail to the file") + return ctx, err + } + err = os.Remove(file) + if err != nil { + log.Error("couldn't remove the Thumbnail") + } + } else { + thumpOne := fmt.Sprintf("%x/%x", pathForThumb, nameOfFile) + nameOfFile := fmt.Sprintf("%x-2", nameOfFile) + openFile, err := os.OpenFile(fmt.Sprintf("%x/%x", pathForThumb, nameOfFile), os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Error("couldn't open the file for the Thumbnail") + return ctx, err + } + defer openFile.Close() + _, err = io.Copy(openFile, thumb) + if err != nil { + log.Error("couldn't copy the Thumbnail to the file") + return ctx, err + } + err = os.Remove(file) + if err != nil { + log.Error("couldn't remove the Thumbnail") + } + err = thumbgen.CombineThumbs(thumpOne, nameOfFile, pathForThumb) + if err != nil { + log.Error("couldn't combine the Thumbnails") + } + } + + return ctx, nil + }, + } +} + +func (a *ActionProvider) GenerateThumbnailSprite() *Action { + return &Action{ + Type: ThumbnailAction, + ActionFn: func(ctx context.Context, log *slog.Logger) (context.Context, error) { + return ctx, nil + }, + } +} diff --git a/runner/actions/transcode.go b/runner/actions/transcode.go new file mode 100644 index 000000000..f12a81e2a --- /dev/null +++ b/runner/actions/transcode.go @@ -0,0 +1,182 @@ +package actions + +import ( + "bufio" + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "log/slog" + "os" + "os/exec" + "regexp" + "strings" + "sync" + "time" +) + +func (a *ActionProvider) TranscodeAction() *Action { + return &Action{ + Type: TranscodeAction, + ActionFn: func(ctx context.Context, log *slog.Logger) (context.Context, error) { + + files, ok := ctx.Value("files").([]string) + if !ok { + return ctx, ErrActionInputWrongType + } + if files == nil { + log.Error("no files to transcode", "files", files) + return ctx, ErrRequiredContextValNotFound + } + streamID, ok := ctx.Value("stream").(uint64) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain stream", ErrRequiredContextValNotFound) + } + courseID, ok := ctx.Value("course").(uint64) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain course", ErrRequiredContextValNotFound) + } + version, ok := ctx.Value("version").(string) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain version", ErrRequiredContextValNotFound) + } + + log.Info("transcoding", "files", files) + time.Sleep(time.Second) + // parse output from previous streamAction + fileName, ok := ctx.Value("files").([]string) + if !ok { + return ctx, ErrActionInputWrongType + } + filenames := "" + if len(fileName) == 1 { + filenames = fileName[0] + } else { + filenames = `"concat:` + fileName[0] + for i := 1; i < len(fileName); i++ { + filenames += "|" + fileName[i] + } + filenames += `"` + } + + outputName := a.GetMassDir(courseID, streamID, version) + "/" + time.Now().Format("2006-01-02") + ".mp4" + i := 1 + _, err := os.Stat(outputName) + for err == nil { + if errors.Is(err, os.ErrNotExist) { + break + } + outputName = fmt.Sprintf(a.GetMassDir(courseID, streamID, version)+"/"+time.Now().Format("2006-01-02")+"_%d"+".mp4", i) + _, err = os.Stat(outputName) + i++ + } + + // Pass 1 of audio normalization. + // Audio normalization is only applied, when only one video of the stream exists. Reasons for this: + // 1. Multiple videos existing for one stream is typically caused by a shutdown of a runner. This does not happen frequently. + // 2. It's much more inefficient to apply the audio normalization operation for more than one file: + // 2.1 Instead of 2 passes, 3 passes are needed: concat - get parameter - execute; + // 2.2 Video files need to be stored 3 times instead of twice (including the raw .ts files), at least temporarily + // (Extracting and only operating/storing the audio is unacceptable due to the problem mentioned in one comment of this answer: https://stackoverflow.com/a/27413824) + var info *InfoForAudioNormalization = nil + if len(fileName) == 1 { + info, err = getInfoForAudioNormalization(ctx, a.Cmd.AudioNormalize1, fileName[0]) + } + + cmd := fmt.Sprintf(a.Cmd.Transcoding, filenames, outputName) + // Pass 2 of audio normalization + // Applied only when pass 1 is successfully executed + // It does the same to the video, and additionally normalizes the audio with the given parameters from pass 1 + if info != nil { + cmd = fmt.Sprintf(a.Cmd.AudioNormalize2, filenames, + info.InputI, info.InputTp, info.InputLra, info.InputThresh, info.TargetOffset, outputName) + log.Info("Transcoding with audio normalization", "files", files) + } + c := exec.CommandContext(ctx, "ffmpeg", strings.Split(cmd, " ")...) + c.Stderr = os.Stderr + err = c.Start() + if err != nil { + return ctx, err + } + err = c.Wait() + return set(ctx, "outputFilename", outputName), err + }, + } +} + +type InfoForAudioNormalization struct { + InputI string `json:"input_i"` + InputTp string `json:"input_tp"` + InputLra string `json:"input_lra"` + InputThresh string `json:"input_thresh"` + OutputI string `json:"output_i"` + OutputTp string `json:"output_tp"` + OutputLra string `json:"output_lra"` + OutputThresh string `json:"output_thresh"` + NormalizationType string `json:"normalization_type"` + TargetOffset string `json:"target_offset"` +} + +func getInfoForAudioNormalization(ctx context.Context, cmdFmt string, filename string) (*InfoForAudioNormalization, error) { + // Errors during pass 1 won't propagate to outside. + // But errors will prevent pass 2 from executing, ultimately resulting in the video not undergoing audio normalization. + cmd := fmt.Sprintf(cmdFmt, filename) + c := exec.CommandContext(ctx, "ffmpeg", strings.Split(cmd, " ")...) + c.Stderr = os.Stderr + stdoutPipe, err := c.StdoutPipe() + if err != nil { + return nil, err + } + defer stdoutPipe.Close() + + err = c.Start() + if err != nil { + return nil, err + } + + var output bytes.Buffer + scanner := bufio.NewScanner(stdoutPipe) + + var wg sync.WaitGroup + wg.Add(1) + + go func() { // Reads the output from FFmpeg + defer wg.Done() + for scanner.Scan() { + line := scanner.Text() + output.WriteString(line + "\n") + } + }() + + err = c.Wait() + if err != nil { + return nil, err + } + + wg.Wait() + + info := &InfoForAudioNormalization{} + err = extractAndParseJSON(output.String(), info) + if err != nil { + return nil, err + } + return info, err +} + +func extractAndParseJSON(output string, info *InfoForAudioNormalization) error { + re := regexp.MustCompile(`(?s)\{.*}`) // Finds JSON data from the output + matches := re.FindStringSubmatch(output) + + if len(matches) == 0 { + return fmt.Errorf("no JSON data found") + } + + jsonData := matches[0] + err := json.Unmarshal([]byte(jsonData), info) + if err != nil { + return err + } + + return nil +} diff --git a/runner/actions/upload.go b/runner/actions/upload.go new file mode 100644 index 000000000..569e67e84 --- /dev/null +++ b/runner/actions/upload.go @@ -0,0 +1,129 @@ +package actions + +import ( + "context" + "fmt" + "github.com/TUM-Dev/gocast/worker/cfg" + "io" + "log/slog" + "mime/multipart" + "net/http" + "os" + "strings" + "time" +) + +func (a *ActionProvider) UploadAction() *Action { + return &Action{ + Type: UploadAction, + ActionFn: func(ctx context.Context, log *slog.Logger) (context.Context, error) { + + streamID, ok := ctx.Value("stream").(uint64) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain stream", ErrRequiredContextValNotFound) + } + courseID, ok := ctx.Value("course").(uint64) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain courseID", ErrRequiredContextValNotFound) + } + version, ok := ctx.Value("version").(string) + if !ok { + return ctx, fmt.Errorf("%w: context doesn't contain version", ErrRequiredContextValNotFound) + } + + URLstring := ctx.Value("URL").(string) + + fileName := fmt.Sprintf("%s/%s/%s/%s.mp4", a.MassDir, courseID, streamID, version) + + client := &http.Client{ + // 5 minutes timeout, some large files can take a while. + Timeout: time.Minute * 5, + } + + r, w := io.Pipe() + writer := multipart.NewWriter(w) + + //the same function as in the worker but without function calling + //so analyzing it and changing it later won't give much to look through + + go func() { + defer func(w *io.PipeWriter) { + err := w.Close() + if err != nil { + + } + }(w) + defer func(writer *multipart.Writer) { + err := writer.Close() + if err != nil { + + } + }(writer) + formFileWriter, err := writer.CreateFormFile("filename", fileName) + if err != nil { + log.Error("Cannot create form file: ", err) + return + } + FileReader, err := os.Open(fileName) + if err != nil { + log.Error("Cannot create form file: ", err) + return + } + defer func(FileReader *os.File) { + err := FileReader.Close() + if err != nil { + + } + }(FileReader) + _, err = io.Copy(formFileWriter, FileReader) + if err != nil { + log.Error("Cannot create form file: ", err) + return + } + + fields := map[string]string{ + "benutzer": cfg.LrzUser, + "mailadresse": cfg.LrzMail, + "telefon": cfg.LrzPhone, + "unidir": "tum", + "subdir": cfg.LrzSubDir, + "info": "", + } + + for name, value := range fields { + formFileWriter, err := writer.CreateFormField(name) + if err != nil { + log.Error("Cannot create form field: ", err) + return + } + _, err = io.Copy(formFileWriter, strings.NewReader(value)) + if err != nil { + log.Error("Cannot create form field: ", err) + return + } + if err != nil { + log.Error("Cannot create form field: ", err) + return + } + } + }() + rsp, err := client.Post(URLstring, writer.FormDataContentType(), r) + if err == nil && rsp.StatusCode != http.StatusOK { + log.Error("Request failed with response code: ", rsp.StatusCode) + } + if err == nil && rsp != nil { + all, err := io.ReadAll(rsp.Body) + if err == nil { + log.Debug(string(all), "fileUploaded", fileName) + } + } + if err != nil { + log.Error("Failed to post video to TUMLive", "error", err) + return ctx, err + } + log.Info("Successfully posted video to TUMLive", "stream", fileName) + + return ctx, err + }, + } +} diff --git a/runner/cmd.yaml b/runner/cmd.yaml new file mode 100644 index 000000000..5d2aa34e7 --- /dev/null +++ b/runner/cmd.yaml @@ -0,0 +1,17 @@ +stream: '-y -hide_banner -loglevel quiet -nostats %v -t %.0f -i %v -c:v copy -c:a copy -f mpegts %v -c:v libx264 -preset veryfast -tune zerolatency -maxrate 2500k -bufsize 3000k -g 60 -r 30 -x264-params keyint=60:scenecut=0 -c:a aac -ar 44100 -b:a 128k -f hls -hls_time 2 -hls_list_size 3600 -hls_playlist_type event -hls_flags append_list -hls_segment_filename %v/%%05d.ts %v' + +SeparateAudioFast: "-i %v -vn -c:a copy %v" +SeparateAudio: "-i %v -vn %v" + +# Two-pass audio normalization with FFmpeg loudnorm +# Loudnorm configuration "I=-23:TP=-2:LRA=7" -- Chosen according to EBU R128 +## First pass: Audio analyse, get parameters needed in second pass +AudioNormalize1: "-i %v -nostats -y -af loudnorm=I=-23:TP=-2:LRA=7:print_format=json -f null -" +## Second pass: Apply normalization to the audio +AudioNormalize2: "-i %v -af loudnorm=I=-23:TP=-2:LRA=7:measured_i=%v:measured_tp=%v:measured_lra=%v:measured_thresh=%v:offset=%v:linear=true:print_format=summary -c:a aac -c:v libx264 -crf 0 -probesize 100M -analyzeduration 250M %v" + + +Transcoding: '-i %v -c:v libx264 -c:a copy -crf 0 -probesize 100M -analyzeduration 250M %v' + +SilenceDetect: '-nostats -i %v -af silencedetect=n=-15dB:d=30 -f null -' + diff --git a/runner/cmd/runner/main.go b/runner/cmd/runner/main.go new file mode 100644 index 000000000..568f6b353 --- /dev/null +++ b/runner/cmd/runner/main.go @@ -0,0 +1,50 @@ +package main + +import ( + "github.com/tum-dev/gocast/runner" + "log/slog" + "os" + "os/signal" + "syscall" +) + +// V (Version) is bundled into binary with -ldflags "-X ..." +var V = "dev" + +func main() { + // ... + + // Init EnvConfig + r := runner.NewRunner(V) + go r.Run() + + shouldShutdown := false // set to true once we receive a shutdown signal + + currentCount := 0 + go func() { + for { + currentCount = <-r.ActionCount // wait for a job to finish + slog.Info("current action count", "count", currentCount) + if shouldShutdown && currentCount == 0 { // if we should shut down and no jobs are running, exit. + slog.Info("No actions left, shutting down") + os.Exit(0) + } + } + }() + + osSignal := make(chan os.Signal, 1) + signal.Notify(osSignal, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR1) + s := <-osSignal + slog.Info("Received signal", "signal", s) + shouldShutdown = true + r.Drain() + + if currentCount == 0 { + slog.Info("No jobs left, shutting down") + os.Exit(0) + } + + blocking := make(chan struct{}) + _ = <-blocking + +} diff --git a/runner/config/cmd.go b/runner/config/cmd.go new file mode 100644 index 000000000..bd69cb954 --- /dev/null +++ b/runner/config/cmd.go @@ -0,0 +1,35 @@ +package config + +import ( + "github.com/ghodss/yaml" + "log/slog" + "os" + "path/filepath" +) + +type CmdList struct { + //this is for adding extra parameters + + Stream string `Default:"-y -hide_banner -nostats %x -t &.0f -i %s -c:v copy -c:a copy -f mpegts %x -c:v libx264 -preset veryfast -tune zerolatency -maxrate 2500k -bufsize 3000k -g 60 -r 30 -x264-params keyint=60:scenecut=0 -c:a aac -ar 44100 -b:a 128k -f hls -hls_time 2 -hls_list_size 3600 -hls_playlist_type event -hls_flags append_list -hls_segment_filename %x %x"` + SeparateAudioFast string `Default:"-i %v -vn -c:a copy %v"` + SeparateAudio string `Default:"-i %v -vn %v"` + AudioNormalize1 string `Default:"-i %v -nostats -y -af loudnorm=I=-23:TP=-2:LRA=7:print_format=json -f null -"` + AudioNormalize2 string `Default:"-i %v -af loudnorm=I=-23:TP=-2:LRA=7:measured_i=%v:measured_tp=%v:measured_lra=%v:measured_thresh=%v:offset=%v:linear=true:print_format=summary -c:a aac -c:v libx264 -crf 0 -probesize 100M -analyzeduration 250M %v"` + Transcoding string `Default:"-i %v -c:v libx264 %v"` + SilenceDetect string `Default:"-nostats -i %v -af silencedetect=n=-15dB:d=30 -f null -"` +} + +func NewCmd(log *slog.Logger) *CmdList { + var c CmdList + path, _ := filepath.Abs("cmd.yaml") + YamlFile, err := os.ReadFile(path) + if err != nil { + log.Error("error reading cmd.yaml", "error", err) + } + + if yaml.Unmarshal(YamlFile, &c) != nil { + log.Error("error unmarshalling cmd.yaml", "error", err) + } + log.Info("cmd loaded", "cmd", &c) + return &c +} diff --git a/runner/entrypoint.sh b/runner/entrypoint.sh new file mode 100644 index 000000000..913b534ee --- /dev/null +++ b/runner/entrypoint.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +exec /mediamtx & +exec /runner diff --git a/runner/go.mod b/runner/go.mod new file mode 100644 index 000000000..3f19b9d93 --- /dev/null +++ b/runner/go.mod @@ -0,0 +1,43 @@ +module github.com/tum-dev/gocast/runner + +go 1.21.0 + +toolchain go1.21.3 + +require ( + github.com/TUM-Dev/gocast/worker v0.0.0-20240129165838-f5b7bd764731 + github.com/caarlos0/env v3.5.0+incompatible + github.com/dusted-go/logging v1.1.3 + github.com/ghodss/yaml v1.0.0 + github.com/golang/protobuf v1.5.3 + github.com/google/uuid v1.6.0 + github.com/icza/gox v0.0.0-20230924165045-adcb03233bb5 + github.com/joschahenningsen/thumbgen v0.1.2 + github.com/shirou/gopsutil/v3 v3.24.1 + github.com/sirupsen/logrus v1.9.3 + golang.org/x/sync v0.6.0 + google.golang.org/grpc v1.61.0 + google.golang.org/protobuf v1.32.0 +) + +require ( + github.com/getsentry/sentry-go v0.26.0 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed // indirect + github.com/makasim/sentryhook v0.4.2 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/tidwall/gjson v1.17.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.1 // indirect + github.com/tklauser/go-sysconf v0.3.13 // indirect + github.com/tklauser/numcpus v0.7.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + golang.org/x/image v0.15.0 // indirect + golang.org/x/net v0.20.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect +) diff --git a/runner/go.sum b/runner/go.sum new file mode 100644 index 000000000..2cbf2c7de --- /dev/null +++ b/runner/go.sum @@ -0,0 +1,921 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet/v6 v6.1.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM= +github.com/TUM-Dev/gocast/worker v0.0.0-20240129165838-f5b7bd764731 h1:Aylv6fChw9mR969iwWjh1KM4+czgjfeMfqFGkXJ0lMg= +github.com/TUM-Dev/gocast/worker v0.0.0-20240129165838-f5b7bd764731/go.mod h1:oCm4gLeaGsCHpR4QpqglnicNwBvXj+UYdsXCHM7IiFk= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/caarlos0/env v3.5.0+incompatible h1:Yy0UN8o9Wtr/jGHZDpCBLpNrzcFLLM2yixi/rBrKyJs= +github.com/caarlos0/env v3.5.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk= +github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE= +github.com/dusted-go/logging v1.1.3 h1:K1XwuarKuQaA+2ZY+yJR9oCrijDtAE9cCVUgiGWA9Us= +github.com/dusted-go/logging v1.1.3/go.mod h1:s58+s64zE5fxSWWZfp+b8ZV0CHyKHjamITGyuY1wzGg= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/getsentry/sentry-go v0.17.0/go.mod h1:B82dxtBvxG0KaPD8/hfSV+VcHD+Lg/xUS4JuQn1P4cM= +github.com/getsentry/sentry-go v0.26.0 h1:IX3++sF6/4B5JcevhdZfdKIHfyvMmAq/UnqcyT2H6mA= +github.com/getsentry/sentry-go v0.26.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= +github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= +github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/icza/gox v0.0.0-20230924165045-adcb03233bb5 h1:K7KEFpKgVcjj98jOu2Z3xMBTtTwfYVT90Zmo3ZuWmbE= +github.com/icza/gox v0.0.0-20230924165045-adcb03233bb5/go.mod h1:VbcN86fRkkUMPX2ufM85Um8zFndLZswoIW1eYtpAcVk= +github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= +github.com/iris-contrib/httpexpect/v2 v2.3.1/go.mod h1:ICTf89VBKSD3KB0fsyyHviKF8G8hyepP0dOXJPWz3T0= +github.com/iris-contrib/jade v1.1.4/go.mod h1:EDqR+ur9piDl6DUgs6qRrlfzmlx/D5UybogqrXvJTBE= +github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= +github.com/joschahenningsen/thumbgen v0.1.2 h1:sHIxmvZkuPaiiCjRvKcFNqTmH5IN4kiQiVt+wpAPC8A= +github.com/joschahenningsen/thumbgen v0.1.2/go.mod h1:h8bDlQ2Bq3U/I/VlN8IRA48P7tHW8SHChVXmUWrG3bU= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/kataras/blocks v0.0.6/go.mod h1:UK+Iwk0Oxpc0GdoJja7sEildotAUKK1LYeYcVF0COWc= +github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I= +github.com/kataras/golog v0.1.7/go.mod h1:jOSQ+C5fUqsNSwurB/oAHq1IFSb0KI3l6GMa7xB6dZA= +github.com/kataras/iris/v12 v12.2.0-beta5/go.mod h1:q26aoWJ0Knx/00iPKg5iizDK7oQQSPjbD8np0XDh6dc= +github.com/kataras/jwt v0.1.8/go.mod h1:Q5j2IkcIHnfwy+oNY3TVWuEBJNw0ADgCcXK9CaZwV4o= +github.com/kataras/neffos v0.0.20/go.mod h1:srdvC/Uo8mgrApWW0AYtiiLgMbyNPf69qPsd2FhE6MQ= +github.com/kataras/pio v0.0.10/go.mod h1:gS3ui9xSD+lAUpbYnjOGiQyY7sUMJO+EHpiRzhtZ5no= +github.com/kataras/pio v0.0.11/go.mod h1:38hH6SWH6m4DKSYmRhlrCJ5WItwWgCVrTNU62XZyUvI= +github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4= +github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= +github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.9.0/go.mod h1:xkCDAdFCIf8jsFQ5NnbK7oqaF/yU1A1X20Ltm0OvSks= +github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= +github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0= +github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/mailgun/raymond/v2 v2.0.46/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/makasim/sentryhook v0.4.2 h1:rqx637SCMMV5mAd9PpSg0OUIpZZaQOQZ6JHONOpUlRI= +github.com/makasim/sentryhook v0.4.2/go.mod h1:AlAU2qjpS3R9bmpfVNcCVQw1tKkH8/lOPa2RQTk8Bzw= +github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mediocregopher/radix/v3 v3.8.0/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= +github.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50= +github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= +github.com/nats-io/jwt/v2 v2.3.0/go.mod h1:0tqz9Hlu6bCBFLWAASKhE5vUA4c24L9KPUUgvwumE/k= +github.com/nats-io/nats-server/v2 v2.8.4/go.mod h1:8zZa+Al3WsESfmgSs98Fi06dRWLH5Bnq90m5bKD/eT4= +github.com/nats-io/nats.go v1.15.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= +github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w= +github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= +github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b h1:0LFwY6Q3gMACTjAbMZBjXAqTOzOwFaj2Ld6cjeQ7Rig= +github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shirou/gopsutil/v3 v3.22.8/go.mod h1:s648gW4IywYzUfE/KjXxUsqrqx/T2xO5VqOXxONeRfI= +github.com/shirou/gopsutil/v3 v3.24.1 h1:R3t6ondCEvmARp3wxODhXMTLC/klMa87h2PHUw5m7QI= +github.com/shirou/gopsutil/v3 v3.24.1/go.mod h1:UU7a2MSBQa+kW1uuDq8DeEBS8kmrnQwsv2b5O513rwU= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/assertions v1.13.0/go.mod h1:wDmR7qL282YbGsPy6H/yAsesrxfxaaSlJazyFLYVFx8= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/tdewolff/minify/v2 v2.12.1/go.mod h1:p5pwbvNs1ghbFED/ZW1towGsnnWwzvM8iz8l0eURi9g= +github.com/tdewolff/minify/v2 v2.12.4/go.mod h1:h+SRvSIX3kwgwTFOpSckvSxgax3uy8kZTSF1Ojrr3bk= +github.com/tdewolff/parse/v2 v2.6.3/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs= +github.com/tdewolff/parse/v2 v2.6.4/go.mod h1:woz0cgbLwFdtbjJu8PIKxhW05KplTFQkOdX78o+Jgrs= +github.com/tdewolff/test v1.0.7/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= +github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= +github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= +github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= +github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= +github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= +github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= +github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI= +github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0= +github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= +github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= +go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= +go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= +golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe h1:bQnxqljG/wqi4NTXu2+DJ3n7APcEA882QZ1JvhQAq9o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= +google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/runner/handlers.go b/runner/handlers.go new file mode 100644 index 000000000..f4329751f --- /dev/null +++ b/runner/handlers.go @@ -0,0 +1,94 @@ +package runner + +import ( + "context" + "errors" + "github.com/tum-dev/gocast/runner/actions" + "github.com/tum-dev/gocast/runner/protobuf" +) + +func contextFromStreamReq(req *protobuf.StreamRequest, ctx context.Context) context.Context { + ctx = context.WithValue(ctx, "stream", req.GetStream()) + ctx = context.WithValue(ctx, "course", req.GetCourse()) + ctx = context.WithValue(ctx, "version", req.GetVersion()) + ctx = context.WithValue(ctx, "source", req.GetSource()) + return context.WithValue(ctx, "end", req.GetEnd().AsTime()) +} + +func contextFromTranscodingReq(req *protobuf.TranscodingRequest, ctx context.Context) context.Context { + ctx = context.WithValue(ctx, "stream", req.StreamName) + ctx = context.WithValue(ctx, "course", req.CourseName) + ctx = context.WithValue(ctx, "version", req.SourceType) + ctx = context.WithValue(ctx, "source", req.DataURL) + return context.WithValue(ctx, "Runner", req.RunnerID) +} + +func (r *Runner) RequestStream(ctx context.Context, req *protobuf.StreamRequest) (*protobuf.StreamResponse, error) { + r.ReadDiagnostics(5) + ctx = context.Background() + ctx = contextFromStreamReq(req, ctx) + ctx = context.WithValue(ctx, "URL", "") + ctx = context.WithValue(ctx, "Hostname", r.cfg.Hostname) + ctx = context.WithValue(ctx, "Port", r.cfg.Port) + ctx = context.WithValue(ctx, "actionID", req.ActionID) + r.log.Info("stream request", "jobID", req.ActionID) + a := []*actions.Action{ + r.actions.PrepareAction(), + r.actions.StreamAction(), + } + aID := r.RunAction(ctx, a) + r.log.Info("job added", "ActionID", aID) + + return &protobuf.StreamResponse{ActionID: aID}, nil +} + +func (r *Runner) RequestUpload(ctx context.Context, req *protobuf.UploadRequest) (*protobuf.UploadResponse, error) { + r.log.Info("upload request", "jobID", req.RunnerID) + + panic("implement me") +} + +func (r *Runner) RequestTranscoding(ctx context.Context, req *protobuf.TranscodingRequest) (*protobuf.TranscodingResponse, error) { + r.log.Info("transcoding request", "jobID", req.RunnerID) + r.ReadDiagnostics(5) + ctx = context.Background() + ctx = contextFromTranscodingReq(req, ctx) + ctx = context.WithValue(ctx, "URL", "") + ctx = context.WithValue(ctx, "Hostname", r.cfg.Hostname) + ctx = context.WithValue(ctx, "ActionID", req.ActionID) + if req.GetRunnerID() != r.cfg.Hostname { + r.log.Error("transcoding request for wrong hostname", "hostname", req.GetRunnerID(), "expected", r.cfg.Hostname) + return nil, errors.New("wrong hostname") + } + a := []*actions.Action{ + r.actions.TranscodeAction(), + } + _ = r.RunAction(ctx, a) + r.log.Info("action added", "action", req.ActionID) + return &protobuf.TranscodingResponse{ActionID: req.ActionID, TranscodingID: req.ActionID}, nil +} + +func (r *Runner) RequestStreamEnd(ctx context.Context, request *protobuf.StreamEndRequest) (*protobuf.StreamEndResponse, error) { + r.ReadDiagnostics(5) + if activeAction, ok := r.activeActions[request.ActionID]; ok { + for _, action := range activeAction { + if action.Cancel != nil { + // action already running -> cancel context + action.Cancel(errors.New("cancelled by user request")) + } + // set canceled flag -> stop action from being started + action.Canceled = true + } + } + return &protobuf.StreamEndResponse{}, nil +} + +func (r *Runner) GenerateLivePreview(ctx context.Context, request *protobuf.LivePreviewRequest) (*protobuf.LivePreviewResponse, error) { + r.ReadDiagnostics(5) + panic("implement me") +} + +func (r *Runner) GenerateSectionImages(ctx context.Context, request *protobuf.GenerateSectionImageRequest) (*protobuf.Status, error) { + r.ReadDiagnostics(5) + panic("implement me") +} diff --git a/runner/hls.go b/runner/hls.go new file mode 100644 index 000000000..bdce778ec --- /dev/null +++ b/runner/hls.go @@ -0,0 +1,26 @@ +package runner + +import ( + "log/slog" + "net/http" +) + +type HLSServer struct { + log *slog.Logger + fs http.Handler +} + +func NewHLSServer(LiveDir string, log *slog.Logger) *HLSServer { + return &HLSServer{fs: http.FileServer(http.Dir(LiveDir)), log: log} +} + +func (h *HLSServer) Start() error { + http.Handle("/", h) + h.log.Info("starting hls server", "port", 8187) + return http.ListenAndServe(":8187", h) +} + +func (h *HLSServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { + //w.Header().Set("Access-Control-Allow-Origin", "*") + h.fs.ServeHTTP(w, r) +} diff --git a/runner/mediamtx.yml b/runner/mediamtx.yml new file mode 100644 index 000000000..122b6ce51 --- /dev/null +++ b/runner/mediamtx.yml @@ -0,0 +1,104 @@ + +############################################### +# General parameters + +# Sets the verbosity of the program; available values are "error", "warn", "info", "debug". +logLevel: debug +# Destinations of log messages; available values are "stdout", "file" and "syslog". +logDestinations: [stdout] + +# Timeout of read operations. +readTimeout: 10s +# Timeout of write operations. +writeTimeout: 10s +# Number of read buffers. +# A higher value allows a wider throughput, a lower value allows to save RAM. +readBufferCount: 512 +# Maximum size of payload of outgoing UDP packets. +# This can be decreased to avoid fragmentation on networks with a low UDP MTU. +udpMaxPayloadSize: 1472 + +# HTTP URL to perform external authentication. +# Every time a user wants to authenticate, the server calls this URL +# with the POST method and a body containing: +# { +# "ip": "ip", +# "user": "user", +# "password": "password", +# "path": "path", +# "protocol": "rtsp|rtmp|hls|webrtc", +# "id": "id", +# "action": "read|publish", +# "query": "query" +# } +# If the response code is 20x, authentication is accepted, otherwise +# it is discarded. +externalAuthenticationURL: http://localhost:8060/on_publish + +# Enable the HTTP API. +api: no +# Address of the API listener. +apiAddress: 127.0.0.1:9997 + +# Enable Prometheus-compatible metrics. +metrics: no +# Address of the metrics listener. +metricsAddress: 127.0.0.1:9998 + + +############################################### +# RTSP parameters + +# Disable support for the RTSP protocol. +rtspDisable: yes + +############################################### +# RTMP parameters + +# Disable support for the RTMP protocol. +rtmpDisable: no +# Address of the RTMP listener. This is needed only when encryption is "no" or "optional". +rtmpAddress: :1935 +# Encrypt connections with TLS (RTMPS). +# Available values are "no", "strict", "optional". +rtmpEncryption: "no" + +############################################### +# HLS parameters + +# Disable support for the HLS protocol. +hlsDisable: yes + +############################################### +# WebRTC parameters + +# Disable support for the WebRTC protocol. +webrtcDisable: yes + +############################################### +# Path parameters + +# These settings are path-dependent, and the map key is the name of the path. +# It's possible to use regular expressions by using a tilde as prefix. +# For example, "~^(test1|test2)$" will match both "test1" and "test2". +# For example, "~^prefix" will match all paths that start with "prefix". +# The settings under the path "all" are applied to all paths that do not match +# another entry. +paths: + all: + # Source of the stream. This can be: + # * publisher -> the stream is published by a RTSP or RTMP client + # * rtsp://existing-url -> the stream is pulled from another RTSP server / camera + # * rtsps://existing-url -> the stream is pulled from another RTSP server / camera with RTSPS + # * rtmp://existing-url -> the stream is pulled from another RTMP server / camera + # * rtmps://existing-url -> the stream is pulled from another RTMP server / camera with RTMPS + # * http://existing-url/stream.m3u8 -> the stream is pulled from another HLS server + # * https://existing-url/stream.m3u8 -> the stream is pulled from another HLS server with HTTPS + # * udp://ip:port -> the stream is pulled from UDP, by listening on the specified IP and port + # * redirect -> the stream is provided by another path or server + # * rpiCamera -> the stream is provided by a Raspberry Pi Camera + source: publisher + + # If the source is an RTSP or RTSPS URL, this is the protocol that will be used to + # pull the stream. available values are "automatic", "udp", "multicast", "tcp". + sourceProtocol: automatic diff --git a/runner/pkg/ServerInterface/ServerInterface.go b/runner/pkg/ServerInterface/ServerInterface.go new file mode 100644 index 000000000..a91a3d9a9 --- /dev/null +++ b/runner/pkg/ServerInterface/ServerInterface.go @@ -0,0 +1,17 @@ +package ServerInterface + +import ( + "context" + "github.com/tum-dev/gocast/runner/protobuf" +) + +type ServerInf interface { + RequestSelfStream(ctx context.Context, request *protobuf.SelfStreamRequest) *protobuf.SelfStreamResponse + NotifyVoDUploadFinished(ctx context.Context, request *protobuf.VoDUploadFinished) *protobuf.Status + NotifySilenceResults(ctx context.Context, request *protobuf.SilenceResults) *protobuf.Status + NotifyStreamStarted(ctx context.Context, request *protobuf.StreamStarted) *protobuf.Status + NotifyStreamEnded(ctx context.Context, request *protobuf.StreamEnded) *protobuf.Status + NotifyThumbnailsFinished(ctx context.Context, request *protobuf.ThumbnailsFinished) *protobuf.Status + NotifyTranscodingFailure(ctx context.Context, request *protobuf.TranscodingFailureNotification) *protobuf.Status + GetStreamInfoForUpload(ctx context.Context, request *protobuf.StreamInfoForUploadRequest) *protobuf.StreamInfoForUploadResponse +} diff --git a/runner/pkg/logging/grpc.go b/runner/pkg/logging/grpc.go new file mode 100644 index 000000000..8e95905a3 --- /dev/null +++ b/runner/pkg/logging/grpc.go @@ -0,0 +1,19 @@ +package logging + +import ( + "context" + "google.golang.org/grpc" + "log/slog" +) + +// GetGrpcLogInterceptor returns a grpc.ServerOption that logs all requests +func GetGrpcLogInterceptor(logger *slog.Logger) grpc.ServerOption { + return grpc.ChainUnaryInterceptor(grpcLogger(logger)) +} + +func grpcLogger(l *slog.Logger) grpc.UnaryServerInterceptor { + return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) { + l.Info("gRPC call", "method", info.FullMethod, "request", req) + return handler(ctx, req) + } +} diff --git a/runner/pkg/logging/logging.go b/runner/pkg/logging/logging.go new file mode 100644 index 000000000..51e6d13d9 --- /dev/null +++ b/runner/pkg/logging/logging.go @@ -0,0 +1,43 @@ +package logging + +import ( + "log/slog" + "os" + "strings" + + "github.com/dusted-go/logging/prettylog" +) + +// GetLogger creates a *slog.Logger based on the environment, sets it as the default logger and returns it. +func GetLogger(v string) *slog.Logger { + lvlStr := os.Getenv("LOG_LEVEL") + var level slog.Level + switch strings.ToLower(lvlStr) { + case "debug": + level = slog.LevelDebug + case "info": + level = slog.LevelInfo + case "warn": + level = slog.LevelWarn + case "error": + level = slog.LevelError + default: + level = slog.LevelDebug + } + var logger *slog.Logger + if os.Getenv("LOG_FMT") == "json" { + logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ + AddSource: true, + Level: level, + })) + } else { + logger = slog.New(prettylog.NewHandler(&slog.HandlerOptions{ + Level: level, + ReplaceAttr: func(_ []string, a slog.Attr) slog.Attr { + return a // don't replace any attributes + }, + })) + } + slog.SetDefault(logger) + return logger +} diff --git a/runner/pkg/netutil/netutil.go b/runner/pkg/netutil/netutil.go new file mode 100644 index 000000000..f3ae319ed --- /dev/null +++ b/runner/pkg/netutil/netutil.go @@ -0,0 +1,16 @@ +package netutil + +import "net" + +// GetFreePort returns a free port for tcp use. +func GetFreePort() (port int, err error) { + var a *net.TCPAddr + if a, err = net.ResolveTCPAddr("tcp", "localhost:0"); err == nil { + var l *net.TCPListener + if l, err = net.ListenTCP("tcp", a); err == nil { + defer l.Close() + return l.Addr().(*net.TCPAddr).Port, nil + } + } + return port, err +} diff --git a/runner/protobuf/runner.pb.go b/runner/protobuf/runner.pb.go new file mode 100644 index 000000000..4f24eecaa --- /dev/null +++ b/runner/protobuf/runner.pb.go @@ -0,0 +1,3143 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.28.1 +// protoc v3.21.12 +// source: runner.proto + +package protobuf + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type UploadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActionID string `protobuf:"bytes,1,opt,name=ActionID,proto3" json:"ActionID,omitempty"` + RunnerID string `protobuf:"bytes,2,opt,name=RunnerID,proto3" json:"RunnerID,omitempty"` + HLSUrl string `protobuf:"bytes,3,opt,name=HLSUrl,proto3" json:"HLSUrl,omitempty"` + SourceType string `protobuf:"bytes,4,opt,name=SourceType,proto3" json:"SourceType,omitempty"` + CourseName string `protobuf:"bytes,5,opt,name=CourseName,proto3" json:"CourseName,omitempty"` + CourseYear uint32 `protobuf:"varint,6,opt,name=CourseYear,proto3" json:"CourseYear,omitempty"` + CourseTeachingTerm string `protobuf:"bytes,7,opt,name=CourseTeachingTerm,proto3" json:"CourseTeachingTerm,omitempty"` + ThumbnailUrl string `protobuf:"bytes,8,opt,name=ThumbnailUrl,proto3" json:"ThumbnailUrl,omitempty"` +} + +func (x *UploadRequest) Reset() { + *x = UploadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UploadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UploadRequest) ProtoMessage() {} + +func (x *UploadRequest) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UploadRequest.ProtoReflect.Descriptor instead. +func (*UploadRequest) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{0} +} + +func (x *UploadRequest) GetActionID() string { + if x != nil { + return x.ActionID + } + return "" +} + +func (x *UploadRequest) GetRunnerID() string { + if x != nil { + return x.RunnerID + } + return "" +} + +func (x *UploadRequest) GetHLSUrl() string { + if x != nil { + return x.HLSUrl + } + return "" +} + +func (x *UploadRequest) GetSourceType() string { + if x != nil { + return x.SourceType + } + return "" +} + +func (x *UploadRequest) GetCourseName() string { + if x != nil { + return x.CourseName + } + return "" +} + +func (x *UploadRequest) GetCourseYear() uint32 { + if x != nil { + return x.CourseYear + } + return 0 +} + +func (x *UploadRequest) GetCourseTeachingTerm() string { + if x != nil { + return x.CourseTeachingTerm + } + return "" +} + +func (x *UploadRequest) GetThumbnailUrl() string { + if x != nil { + return x.ThumbnailUrl + } + return "" +} + +type UploadResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActionID string `protobuf:"bytes,1,opt,name=ActionID,proto3" json:"ActionID,omitempty"` + UploadKey string `protobuf:"bytes,2,opt,name=UploadKey,proto3" json:"UploadKey,omitempty"` +} + +func (x *UploadResponse) Reset() { + *x = UploadResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UploadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UploadResponse) ProtoMessage() {} + +func (x *UploadResponse) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UploadResponse.ProtoReflect.Descriptor instead. +func (*UploadResponse) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{1} +} + +func (x *UploadResponse) GetActionID() string { + if x != nil { + return x.ActionID + } + return "" +} + +func (x *UploadResponse) GetUploadKey() string { + if x != nil { + return x.UploadKey + } + return "" +} + +type ThumbnailRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActionID string `protobuf:"bytes,1,opt,name=ActionID,proto3" json:"ActionID,omitempty"` + RunnerID string `protobuf:"bytes,2,opt,name=RunnerID,proto3" json:"RunnerID,omitempty"` + HLSUrl string `protobuf:"bytes,3,opt,name=HLSUrl,proto3" json:"HLSUrl,omitempty"` + SourceType string `protobuf:"bytes,4,opt,name=SourceType,proto3" json:"SourceType,omitempty"` + CourseName string `protobuf:"bytes,5,opt,name=CourseName,proto3" json:"CourseName,omitempty"` + CourseYear uint32 `protobuf:"varint,6,opt,name=CourseYear,proto3" json:"CourseYear,omitempty"` + CourseTeachingTerm string `protobuf:"bytes,7,opt,name=CourseTeachingTerm,proto3" json:"CourseTeachingTerm,omitempty"` +} + +func (x *ThumbnailRequest) Reset() { + *x = ThumbnailRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ThumbnailRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ThumbnailRequest) ProtoMessage() {} + +func (x *ThumbnailRequest) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ThumbnailRequest.ProtoReflect.Descriptor instead. +func (*ThumbnailRequest) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{2} +} + +func (x *ThumbnailRequest) GetActionID() string { + if x != nil { + return x.ActionID + } + return "" +} + +func (x *ThumbnailRequest) GetRunnerID() string { + if x != nil { + return x.RunnerID + } + return "" +} + +func (x *ThumbnailRequest) GetHLSUrl() string { + if x != nil { + return x.HLSUrl + } + return "" +} + +func (x *ThumbnailRequest) GetSourceType() string { + if x != nil { + return x.SourceType + } + return "" +} + +func (x *ThumbnailRequest) GetCourseName() string { + if x != nil { + return x.CourseName + } + return "" +} + +func (x *ThumbnailRequest) GetCourseYear() uint32 { + if x != nil { + return x.CourseYear + } + return 0 +} + +func (x *ThumbnailRequest) GetCourseTeachingTerm() string { + if x != nil { + return x.CourseTeachingTerm + } + return "" +} + +type ThumbnailResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActionID string `protobuf:"bytes,1,opt,name=ActionID,proto3" json:"ActionID,omitempty"` + ThumbnailUrl string `protobuf:"bytes,2,opt,name=ThumbnailUrl,proto3" json:"ThumbnailUrl,omitempty"` +} + +func (x *ThumbnailResponse) Reset() { + *x = ThumbnailResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ThumbnailResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ThumbnailResponse) ProtoMessage() {} + +func (x *ThumbnailResponse) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ThumbnailResponse.ProtoReflect.Descriptor instead. +func (*ThumbnailResponse) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{3} +} + +func (x *ThumbnailResponse) GetActionID() string { + if x != nil { + return x.ActionID + } + return "" +} + +func (x *ThumbnailResponse) GetThumbnailUrl() string { + if x != nil { + return x.ThumbnailUrl + } + return "" +} + +type TranscodingRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActionID string `protobuf:"bytes,1,opt,name=ActionID,proto3" json:"ActionID,omitempty"` + RunnerID string `protobuf:"bytes,2,opt,name=RunnerID,proto3" json:"RunnerID,omitempty"` + SourceType string `protobuf:"bytes,3,opt,name=SourceType,proto3" json:"SourceType,omitempty"` + CourseName string `protobuf:"bytes,4,opt,name=CourseName,proto3" json:"CourseName,omitempty"` + DataURL string `protobuf:"bytes,5,opt,name=DataURL,proto3" json:"DataURL,omitempty"` + StreamName string `protobuf:"bytes,6,opt,name=streamName,proto3" json:"streamName,omitempty"` +} + +func (x *TranscodingRequest) Reset() { + *x = TranscodingRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TranscodingRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TranscodingRequest) ProtoMessage() {} + +func (x *TranscodingRequest) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TranscodingRequest.ProtoReflect.Descriptor instead. +func (*TranscodingRequest) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{4} +} + +func (x *TranscodingRequest) GetActionID() string { + if x != nil { + return x.ActionID + } + return "" +} + +func (x *TranscodingRequest) GetRunnerID() string { + if x != nil { + return x.RunnerID + } + return "" +} + +func (x *TranscodingRequest) GetSourceType() string { + if x != nil { + return x.SourceType + } + return "" +} + +func (x *TranscodingRequest) GetCourseName() string { + if x != nil { + return x.CourseName + } + return "" +} + +func (x *TranscodingRequest) GetDataURL() string { + if x != nil { + return x.DataURL + } + return "" +} + +func (x *TranscodingRequest) GetStreamName() string { + if x != nil { + return x.StreamName + } + return "" +} + +type TranscodingResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActionID string `protobuf:"bytes,1,opt,name=ActionID,proto3" json:"ActionID,omitempty"` + TranscodingID string `protobuf:"bytes,2,opt,name=TranscodingID,proto3" json:"TranscodingID,omitempty"` +} + +func (x *TranscodingResponse) Reset() { + *x = TranscodingResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TranscodingResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TranscodingResponse) ProtoMessage() {} + +func (x *TranscodingResponse) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TranscodingResponse.ProtoReflect.Descriptor instead. +func (*TranscodingResponse) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{5} +} + +func (x *TranscodingResponse) GetActionID() string { + if x != nil { + return x.ActionID + } + return "" +} + +func (x *TranscodingResponse) GetTranscodingID() string { + if x != nil { + return x.TranscodingID + } + return "" +} + +type LivePreviewRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActionID string `protobuf:"bytes,1,opt,name=ActionID,proto3" json:"ActionID,omitempty"` + RunnerID string `protobuf:"bytes,2,opt,name=RunnerID,proto3" json:"RunnerID,omitempty"` + HLSUrl string `protobuf:"bytes,3,opt,name=HLSUrl,proto3" json:"HLSUrl,omitempty"` +} + +func (x *LivePreviewRequest) Reset() { + *x = LivePreviewRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LivePreviewRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LivePreviewRequest) ProtoMessage() {} + +func (x *LivePreviewRequest) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LivePreviewRequest.ProtoReflect.Descriptor instead. +func (*LivePreviewRequest) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{6} +} + +func (x *LivePreviewRequest) GetActionID() string { + if x != nil { + return x.ActionID + } + return "" +} + +func (x *LivePreviewRequest) GetRunnerID() string { + if x != nil { + return x.RunnerID + } + return "" +} + +func (x *LivePreviewRequest) GetHLSUrl() string { + if x != nil { + return x.HLSUrl + } + return "" +} + +type LivePreviewResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActionID string `protobuf:"bytes,1,opt,name=ActionID,proto3" json:"ActionID,omitempty"` + LiveThumb []byte `protobuf:"bytes,2,opt,name=LiveThumb,proto3" json:"LiveThumb,omitempty"` +} + +func (x *LivePreviewResponse) Reset() { + *x = LivePreviewResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LivePreviewResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LivePreviewResponse) ProtoMessage() {} + +func (x *LivePreviewResponse) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LivePreviewResponse.ProtoReflect.Descriptor instead. +func (*LivePreviewResponse) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{7} +} + +func (x *LivePreviewResponse) GetActionID() string { + if x != nil { + return x.ActionID + } + return "" +} + +func (x *LivePreviewResponse) GetLiveThumb() []byte { + if x != nil { + return x.LiveThumb + } + return nil +} + +type DeleteSectionImageRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActionID string `protobuf:"bytes,1,opt,name=ActionID,proto3" json:"ActionID,omitempty"` + Path string `protobuf:"bytes,2,opt,name=Path,proto3" json:"Path,omitempty"` +} + +func (x *DeleteSectionImageRequest) Reset() { + *x = DeleteSectionImageRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *DeleteSectionImageRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteSectionImageRequest) ProtoMessage() {} + +func (x *DeleteSectionImageRequest) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use DeleteSectionImageRequest.ProtoReflect.Descriptor instead. +func (*DeleteSectionImageRequest) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{8} +} + +func (x *DeleteSectionImageRequest) GetActionID() string { + if x != nil { + return x.ActionID + } + return "" +} + +func (x *DeleteSectionImageRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +type GenerateSectionImageResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActionID string `protobuf:"bytes,1,opt,name=ActionID,proto3" json:"ActionID,omitempty"` + Paths []string `protobuf:"bytes,2,rep,name=Paths,proto3" json:"Paths,omitempty"` +} + +func (x *GenerateSectionImageResponse) Reset() { + *x = GenerateSectionImageResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenerateSectionImageResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateSectionImageResponse) ProtoMessage() {} + +func (x *GenerateSectionImageResponse) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateSectionImageResponse.ProtoReflect.Descriptor instead. +func (*GenerateSectionImageResponse) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{9} +} + +func (x *GenerateSectionImageResponse) GetActionID() string { + if x != nil { + return x.ActionID + } + return "" +} + +func (x *GenerateSectionImageResponse) GetPaths() []string { + if x != nil { + return x.Paths + } + return nil +} + +type Section struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hours uint32 `protobuf:"varint,1,opt,name=Hours,proto3" json:"Hours,omitempty"` + Minutes uint32 `protobuf:"varint,2,opt,name=Minutes,proto3" json:"Minutes,omitempty"` + Seconds uint32 `protobuf:"varint,3,opt,name=Seconds,proto3" json:"Seconds,omitempty"` +} + +func (x *Section) Reset() { + *x = Section{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Section) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Section) ProtoMessage() {} + +func (x *Section) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[10] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Section.ProtoReflect.Descriptor instead. +func (*Section) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{10} +} + +func (x *Section) GetHours() uint32 { + if x != nil { + return x.Hours + } + return 0 +} + +func (x *Section) GetMinutes() uint32 { + if x != nil { + return x.Minutes + } + return 0 +} + +func (x *Section) GetSeconds() uint32 { + if x != nil { + return x.Seconds + } + return 0 +} + +type GenerateSectionImageRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActionID string `protobuf:"bytes,1,opt,name=ActionID,proto3" json:"ActionID,omitempty"` + PlaylistURL string `protobuf:"bytes,2,opt,name=PlaylistURL,proto3" json:"PlaylistURL,omitempty"` + CourseName string `protobuf:"bytes,3,opt,name=CourseName,proto3" json:"CourseName,omitempty"` + CourseYear uint32 `protobuf:"varint,4,opt,name=CourseYear,proto3" json:"CourseYear,omitempty"` + CourseTeachingTerm string `protobuf:"bytes,5,opt,name=CourseTeachingTerm,proto3" json:"CourseTeachingTerm,omitempty"` + Sections []*Section `protobuf:"bytes,6,rep,name=Sections,proto3" json:"Sections,omitempty"` +} + +func (x *GenerateSectionImageRequest) Reset() { + *x = GenerateSectionImageRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GenerateSectionImageRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateSectionImageRequest) ProtoMessage() {} + +func (x *GenerateSectionImageRequest) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[11] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateSectionImageRequest.ProtoReflect.Descriptor instead. +func (*GenerateSectionImageRequest) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{11} +} + +func (x *GenerateSectionImageRequest) GetActionID() string { + if x != nil { + return x.ActionID + } + return "" +} + +func (x *GenerateSectionImageRequest) GetPlaylistURL() string { + if x != nil { + return x.PlaylistURL + } + return "" +} + +func (x *GenerateSectionImageRequest) GetCourseName() string { + if x != nil { + return x.CourseName + } + return "" +} + +func (x *GenerateSectionImageRequest) GetCourseYear() uint32 { + if x != nil { + return x.CourseYear + } + return 0 +} + +func (x *GenerateSectionImageRequest) GetCourseTeachingTerm() string { + if x != nil { + return x.CourseTeachingTerm + } + return "" +} + +func (x *GenerateSectionImageRequest) GetSections() []*Section { + if x != nil { + return x.Sections + } + return nil +} + +type Status struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ok bool `protobuf:"varint,1,opt,name=ok,proto3" json:"ok,omitempty"` +} + +func (x *Status) Reset() { + *x = Status{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Status) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Status) ProtoMessage() {} + +func (x *Status) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Status.ProtoReflect.Descriptor instead. +func (*Status) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{12} +} + +func (x *Status) GetOk() bool { + if x != nil { + return x.Ok + } + return false +} + +type StreamRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActionID string `protobuf:"bytes,1,opt,name=ActionID,proto3" json:"ActionID,omitempty"` + Stream uint64 `protobuf:"varint,2,opt,name=stream,proto3" json:"stream,omitempty"` + Course uint64 `protobuf:"varint,3,opt,name=course,proto3" json:"course,omitempty"` + Version string `protobuf:"bytes,4,opt,name=version,proto3" json:"version,omitempty"` + End *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=end,proto3" json:"end,omitempty"` + Source string `protobuf:"bytes,6,opt,name=source,proto3" json:"source,omitempty"` +} + +func (x *StreamRequest) Reset() { + *x = StreamRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StreamRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamRequest) ProtoMessage() {} + +func (x *StreamRequest) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamRequest.ProtoReflect.Descriptor instead. +func (*StreamRequest) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{13} +} + +func (x *StreamRequest) GetActionID() string { + if x != nil { + return x.ActionID + } + return "" +} + +func (x *StreamRequest) GetStream() uint64 { + if x != nil { + return x.Stream + } + return 0 +} + +func (x *StreamRequest) GetCourse() uint64 { + if x != nil { + return x.Course + } + return 0 +} + +func (x *StreamRequest) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *StreamRequest) GetEnd() *timestamppb.Timestamp { + if x != nil { + return x.End + } + return nil +} + +func (x *StreamRequest) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +type StreamResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActionID string `protobuf:"bytes,1,opt,name=ActionID,proto3" json:"ActionID,omitempty"` +} + +func (x *StreamResponse) Reset() { + *x = StreamResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StreamResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamResponse) ProtoMessage() {} + +func (x *StreamResponse) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamResponse.ProtoReflect.Descriptor instead. +func (*StreamResponse) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{14} +} + +func (x *StreamResponse) GetActionID() string { + if x != nil { + return x.ActionID + } + return "" +} + +type StreamEndRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ActionID string `protobuf:"bytes,1,opt,name=ActionID,proto3" json:"ActionID,omitempty"` + KeepVod bool `protobuf:"varint,2,opt,name=keepVod,proto3" json:"keepVod,omitempty"` +} + +func (x *StreamEndRequest) Reset() { + *x = StreamEndRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StreamEndRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamEndRequest) ProtoMessage() {} + +func (x *StreamEndRequest) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamEndRequest.ProtoReflect.Descriptor instead. +func (*StreamEndRequest) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{15} +} + +func (x *StreamEndRequest) GetActionID() string { + if x != nil { + return x.ActionID + } + return "" +} + +func (x *StreamEndRequest) GetKeepVod() bool { + if x != nil { + return x.KeepVod + } + return false +} + +type StreamEndResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *StreamEndResponse) Reset() { + *x = StreamEndResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StreamEndResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamEndResponse) ProtoMessage() {} + +func (x *StreamEndResponse) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamEndResponse.ProtoReflect.Descriptor instead. +func (*StreamEndResponse) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{16} +} + +type ActionFinished struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RunnerID string `protobuf:"bytes,1,opt,name=RunnerID,proto3" json:"RunnerID,omitempty"` + ActionID string `protobuf:"bytes,2,opt,name=ActionID,proto3" json:"ActionID,omitempty"` + Type string `protobuf:"bytes,3,opt,name=type,proto3" json:"type,omitempty"` +} + +func (x *ActionFinished) Reset() { + *x = ActionFinished{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ActionFinished) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActionFinished) ProtoMessage() {} + +func (x *ActionFinished) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ActionFinished.ProtoReflect.Descriptor instead. +func (*ActionFinished) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{17} +} + +func (x *ActionFinished) GetRunnerID() string { + if x != nil { + return x.RunnerID + } + return "" +} + +func (x *ActionFinished) GetActionID() string { + if x != nil { + return x.ActionID + } + return "" +} + +func (x *ActionFinished) GetType() string { + if x != nil { + return x.Type + } + return "" +} + +type StreamInfoForUploadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RunnerID string `protobuf:"bytes,1,opt,name=RunnerID,proto3" json:"RunnerID,omitempty"` + UploadKey uint32 `protobuf:"varint,2,opt,name=UploadKey,proto3" json:"UploadKey,omitempty"` +} + +func (x *StreamInfoForUploadRequest) Reset() { + *x = StreamInfoForUploadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StreamInfoForUploadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamInfoForUploadRequest) ProtoMessage() {} + +func (x *StreamInfoForUploadRequest) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamInfoForUploadRequest.ProtoReflect.Descriptor instead. +func (*StreamInfoForUploadRequest) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{18} +} + +func (x *StreamInfoForUploadRequest) GetRunnerID() string { + if x != nil { + return x.RunnerID + } + return "" +} + +func (x *StreamInfoForUploadRequest) GetUploadKey() uint32 { + if x != nil { + return x.UploadKey + } + return 0 +} + +type StreamInfoForUploadResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + CourseSlug string `protobuf:"bytes,1,opt,name=CourseSlug,proto3" json:"CourseSlug,omitempty"` + CourseTerm string `protobuf:"bytes,2,opt,name=CourseTerm,proto3" json:"CourseTerm,omitempty"` + CourseYear uint32 `protobuf:"varint,3,opt,name=CourseYear,proto3" json:"CourseYear,omitempty"` + StreamStart *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=StreamStart,proto3" json:"StreamStart,omitempty"` + StreamEnd *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=StreamEnd,proto3" json:"StreamEnd,omitempty"` + StreamID uint32 `protobuf:"varint,6,opt,name=StreamID,proto3" json:"StreamID,omitempty"` + VideoType string `protobuf:"bytes,7,opt,name=VideoType,proto3" json:"VideoType,omitempty"` +} + +func (x *StreamInfoForUploadResponse) Reset() { + *x = StreamInfoForUploadResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StreamInfoForUploadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamInfoForUploadResponse) ProtoMessage() {} + +func (x *StreamInfoForUploadResponse) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamInfoForUploadResponse.ProtoReflect.Descriptor instead. +func (*StreamInfoForUploadResponse) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{19} +} + +func (x *StreamInfoForUploadResponse) GetCourseSlug() string { + if x != nil { + return x.CourseSlug + } + return "" +} + +func (x *StreamInfoForUploadResponse) GetCourseTerm() string { + if x != nil { + return x.CourseTerm + } + return "" +} + +func (x *StreamInfoForUploadResponse) GetCourseYear() uint32 { + if x != nil { + return x.CourseYear + } + return 0 +} + +func (x *StreamInfoForUploadResponse) GetStreamStart() *timestamppb.Timestamp { + if x != nil { + return x.StreamStart + } + return nil +} + +func (x *StreamInfoForUploadResponse) GetStreamEnd() *timestamppb.Timestamp { + if x != nil { + return x.StreamEnd + } + return nil +} + +func (x *StreamInfoForUploadResponse) GetStreamID() uint32 { + if x != nil { + return x.StreamID + } + return 0 +} + +func (x *StreamInfoForUploadResponse) GetVideoType() string { + if x != nil { + return x.VideoType + } + return "" +} + +type VoDUploadFinished struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RunnerID string `protobuf:"bytes,1,opt,name=RunnerID,proto3" json:"RunnerID,omitempty"` + StreamID uint32 `protobuf:"varint,2,opt,name=StreamID,proto3" json:"StreamID,omitempty"` + HLSUrl string `protobuf:"bytes,3,opt,name=HLSUrl,proto3" json:"HLSUrl,omitempty"` + SourceType string `protobuf:"bytes,4,opt,name=SourceType,proto3" json:"SourceType,omitempty"` + ThumbnailUrl string `protobuf:"bytes,5,opt,name=ThumbnailUrl,proto3" json:"ThumbnailUrl,omitempty"` +} + +func (x *VoDUploadFinished) Reset() { + *x = VoDUploadFinished{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *VoDUploadFinished) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VoDUploadFinished) ProtoMessage() {} + +func (x *VoDUploadFinished) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[20] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use VoDUploadFinished.ProtoReflect.Descriptor instead. +func (*VoDUploadFinished) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{20} +} + +func (x *VoDUploadFinished) GetRunnerID() string { + if x != nil { + return x.RunnerID + } + return "" +} + +func (x *VoDUploadFinished) GetStreamID() uint32 { + if x != nil { + return x.StreamID + } + return 0 +} + +func (x *VoDUploadFinished) GetHLSUrl() string { + if x != nil { + return x.HLSUrl + } + return "" +} + +func (x *VoDUploadFinished) GetSourceType() string { + if x != nil { + return x.SourceType + } + return "" +} + +func (x *VoDUploadFinished) GetThumbnailUrl() string { + if x != nil { + return x.ThumbnailUrl + } + return "" +} + +type SilenceResults struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RunnerID string `protobuf:"bytes,1,opt,name=RunnerID,proto3" json:"RunnerID,omitempty"` + StreamID uint32 `protobuf:"varint,2,opt,name=StreamID,proto3" json:"StreamID,omitempty"` + Starts []uint32 `protobuf:"varint,3,rep,packed,name=starts,proto3" json:"starts,omitempty"` + Ends []uint32 `protobuf:"varint,4,rep,packed,name=ends,proto3" json:"ends,omitempty"` +} + +func (x *SilenceResults) Reset() { + *x = SilenceResults{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SilenceResults) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SilenceResults) ProtoMessage() {} + +func (x *SilenceResults) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SilenceResults.ProtoReflect.Descriptor instead. +func (*SilenceResults) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{21} +} + +func (x *SilenceResults) GetRunnerID() string { + if x != nil { + return x.RunnerID + } + return "" +} + +func (x *SilenceResults) GetStreamID() uint32 { + if x != nil { + return x.StreamID + } + return 0 +} + +func (x *SilenceResults) GetStarts() []uint32 { + if x != nil { + return x.Starts + } + return nil +} + +func (x *SilenceResults) GetEnds() []uint32 { + if x != nil { + return x.Ends + } + return nil +} + +type StreamStarted struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hostname string `protobuf:"bytes,1,opt,name=Hostname,proto3" json:"Hostname,omitempty"` + StreamID uint32 `protobuf:"varint,2,opt,name=StreamID,proto3" json:"StreamID,omitempty"` + CourseID uint32 `protobuf:"varint,3,opt,name=CourseID,proto3" json:"CourseID,omitempty"` + Version string `protobuf:"bytes,4,opt,name=Version,proto3" json:"Version,omitempty"` + HLSUrl string `protobuf:"bytes,5,opt,name=HLSUrl,proto3" json:"HLSUrl,omitempty"` + SourceType string `protobuf:"bytes,6,opt,name=SourceType,proto3" json:"SourceType,omitempty"` +} + +func (x *StreamStarted) Reset() { + *x = StreamStarted{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StreamStarted) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamStarted) ProtoMessage() {} + +func (x *StreamStarted) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[22] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamStarted.ProtoReflect.Descriptor instead. +func (*StreamStarted) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{22} +} + +func (x *StreamStarted) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *StreamStarted) GetStreamID() uint32 { + if x != nil { + return x.StreamID + } + return 0 +} + +func (x *StreamStarted) GetCourseID() uint32 { + if x != nil { + return x.CourseID + } + return 0 +} + +func (x *StreamStarted) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *StreamStarted) GetHLSUrl() string { + if x != nil { + return x.HLSUrl + } + return "" +} + +func (x *StreamStarted) GetSourceType() string { + if x != nil { + return x.SourceType + } + return "" +} + +type StreamEnded struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RunnerID string `protobuf:"bytes,1,opt,name=RunnerID,proto3" json:"RunnerID,omitempty"` + StreamID uint32 `protobuf:"varint,2,opt,name=StreamID,proto3" json:"StreamID,omitempty"` + CourseID uint32 `protobuf:"varint,3,opt,name=CourseID,proto3" json:"CourseID,omitempty"` + Version string `protobuf:"bytes,4,opt,name=Version,proto3" json:"Version,omitempty"` +} + +func (x *StreamEnded) Reset() { + *x = StreamEnded{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[23] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StreamEnded) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamEnded) ProtoMessage() {} + +func (x *StreamEnded) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[23] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamEnded.ProtoReflect.Descriptor instead. +func (*StreamEnded) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{23} +} + +func (x *StreamEnded) GetRunnerID() string { + if x != nil { + return x.RunnerID + } + return "" +} + +func (x *StreamEnded) GetStreamID() uint32 { + if x != nil { + return x.StreamID + } + return 0 +} + +func (x *StreamEnded) GetCourseID() uint32 { + if x != nil { + return x.CourseID + } + return 0 +} + +func (x *StreamEnded) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +type ThumbnailsFinished struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RunnerID string `protobuf:"bytes,1,opt,name=RunnerID,proto3" json:"RunnerID,omitempty"` + StreamID uint32 `protobuf:"varint,2,opt,name=StreamID,proto3" json:"StreamID,omitempty"` + FilePath string `protobuf:"bytes,3,opt,name=FilePath,proto3" json:"FilePath,omitempty"` + Interval uint32 `protobuf:"varint,4,opt,name=Interval,proto3" json:"Interval,omitempty"` + SourceType string `protobuf:"bytes,5,opt,name=SourceType,proto3" json:"SourceType,omitempty"` + LargeThumbnailPath string `protobuf:"bytes,6,opt,name=LargeThumbnailPath,proto3" json:"LargeThumbnailPath,omitempty"` +} + +func (x *ThumbnailsFinished) Reset() { + *x = ThumbnailsFinished{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ThumbnailsFinished) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ThumbnailsFinished) ProtoMessage() {} + +func (x *ThumbnailsFinished) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[24] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ThumbnailsFinished.ProtoReflect.Descriptor instead. +func (*ThumbnailsFinished) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{24} +} + +func (x *ThumbnailsFinished) GetRunnerID() string { + if x != nil { + return x.RunnerID + } + return "" +} + +func (x *ThumbnailsFinished) GetStreamID() uint32 { + if x != nil { + return x.StreamID + } + return 0 +} + +func (x *ThumbnailsFinished) GetFilePath() string { + if x != nil { + return x.FilePath + } + return "" +} + +func (x *ThumbnailsFinished) GetInterval() uint32 { + if x != nil { + return x.Interval + } + return 0 +} + +func (x *ThumbnailsFinished) GetSourceType() string { + if x != nil { + return x.SourceType + } + return "" +} + +func (x *ThumbnailsFinished) GetLargeThumbnailPath() string { + if x != nil { + return x.LargeThumbnailPath + } + return "" +} + +type TranscodingFailureNotification struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RunnerID string `protobuf:"bytes,1,opt,name=RunnerID,proto3" json:"RunnerID,omitempty"` + StreamID uint32 `protobuf:"varint,2,opt,name=StreamID,proto3" json:"StreamID,omitempty"` + Version string `protobuf:"bytes,3,opt,name=Version,proto3" json:"Version,omitempty"` + FilePath string `protobuf:"bytes,4,opt,name=FilePath,proto3" json:"FilePath,omitempty"` + Logs string `protobuf:"bytes,5,opt,name=Logs,proto3" json:"Logs,omitempty"` + ExitCode int64 `protobuf:"varint,6,opt,name=ExitCode,proto3" json:"ExitCode,omitempty"` +} + +func (x *TranscodingFailureNotification) Reset() { + *x = TranscodingFailureNotification{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TranscodingFailureNotification) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TranscodingFailureNotification) ProtoMessage() {} + +func (x *TranscodingFailureNotification) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[25] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TranscodingFailureNotification.ProtoReflect.Descriptor instead. +func (*TranscodingFailureNotification) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{25} +} + +func (x *TranscodingFailureNotification) GetRunnerID() string { + if x != nil { + return x.RunnerID + } + return "" +} + +func (x *TranscodingFailureNotification) GetStreamID() uint32 { + if x != nil { + return x.StreamID + } + return 0 +} + +func (x *TranscodingFailureNotification) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *TranscodingFailureNotification) GetFilePath() string { + if x != nil { + return x.FilePath + } + return "" +} + +func (x *TranscodingFailureNotification) GetLogs() string { + if x != nil { + return x.Logs + } + return "" +} + +func (x *TranscodingFailureNotification) GetExitCode() int64 { + if x != nil { + return x.ExitCode + } + return 0 +} + +type RegisterRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` + Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` +} + +func (x *RegisterRequest) Reset() { + *x = RegisterRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterRequest) ProtoMessage() {} + +func (x *RegisterRequest) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[26] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterRequest.ProtoReflect.Descriptor instead. +func (*RegisterRequest) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{26} +} + +func (x *RegisterRequest) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *RegisterRequest) GetPort() int32 { + if x != nil { + return x.Port + } + return 0 +} + +type RegisterResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` +} + +func (x *RegisterResponse) Reset() { + *x = RegisterResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterResponse) ProtoMessage() {} + +func (x *RegisterResponse) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[27] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegisterResponse.ProtoReflect.Descriptor instead. +func (*RegisterResponse) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{27} +} + +func (x *RegisterResponse) GetID() string { + if x != nil { + return x.ID + } + return "" +} + +type HeartbeatRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Hostname string `protobuf:"bytes,1,opt,name=hostname,proto3" json:"hostname,omitempty"` + Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` + LastSeen *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=LastSeen,proto3" json:"LastSeen,omitempty"` + Status string `protobuf:"bytes,4,opt,name=Status,proto3" json:"Status,omitempty"` + Workload uint32 `protobuf:"varint,5,opt,name=Workload,proto3" json:"Workload,omitempty"` + CPU string `protobuf:"bytes,6,opt,name=CPU,proto3" json:"CPU,omitempty"` + Memory string `protobuf:"bytes,7,opt,name=Memory,proto3" json:"Memory,omitempty"` + Disk string `protobuf:"bytes,8,opt,name=Disk,proto3" json:"Disk,omitempty"` + Uptime string `protobuf:"bytes,9,opt,name=Uptime,proto3" json:"Uptime,omitempty"` + Version string `protobuf:"bytes,10,opt,name=Version,proto3" json:"Version,omitempty"` + CurrentAction string `protobuf:"bytes,11,opt,name=CurrentAction,proto3" json:"CurrentAction,omitempty"` +} + +func (x *HeartbeatRequest) Reset() { + *x = HeartbeatRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HeartbeatRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HeartbeatRequest) ProtoMessage() {} + +func (x *HeartbeatRequest) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[28] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HeartbeatRequest.ProtoReflect.Descriptor instead. +func (*HeartbeatRequest) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{28} +} + +func (x *HeartbeatRequest) GetHostname() string { + if x != nil { + return x.Hostname + } + return "" +} + +func (x *HeartbeatRequest) GetPort() int32 { + if x != nil { + return x.Port + } + return 0 +} + +func (x *HeartbeatRequest) GetLastSeen() *timestamppb.Timestamp { + if x != nil { + return x.LastSeen + } + return nil +} + +func (x *HeartbeatRequest) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *HeartbeatRequest) GetWorkload() uint32 { + if x != nil { + return x.Workload + } + return 0 +} + +func (x *HeartbeatRequest) GetCPU() string { + if x != nil { + return x.CPU + } + return "" +} + +func (x *HeartbeatRequest) GetMemory() string { + if x != nil { + return x.Memory + } + return "" +} + +func (x *HeartbeatRequest) GetDisk() string { + if x != nil { + return x.Disk + } + return "" +} + +func (x *HeartbeatRequest) GetUptime() string { + if x != nil { + return x.Uptime + } + return "" +} + +func (x *HeartbeatRequest) GetVersion() string { + if x != nil { + return x.Version + } + return "" +} + +func (x *HeartbeatRequest) GetCurrentAction() string { + if x != nil { + return x.CurrentAction + } + return "" +} + +type HeartbeatResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Ok bool `protobuf:"varint,1,opt,name=ok,proto3" json:"ok,omitempty"` +} + +func (x *HeartbeatResponse) Reset() { + *x = HeartbeatResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *HeartbeatResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*HeartbeatResponse) ProtoMessage() {} + +func (x *HeartbeatResponse) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[29] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use HeartbeatResponse.ProtoReflect.Descriptor instead. +func (*HeartbeatResponse) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{29} +} + +func (x *HeartbeatResponse) GetOk() bool { + if x != nil { + return x.Ok + } + return false +} + +type SelfStreamRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + StreamKey string `protobuf:"bytes,1,opt,name=streamKey,proto3" json:"streamKey,omitempty"` +} + +func (x *SelfStreamRequest) Reset() { + *x = SelfStreamRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SelfStreamRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SelfStreamRequest) ProtoMessage() {} + +func (x *SelfStreamRequest) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[30] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SelfStreamRequest.ProtoReflect.Descriptor instead. +func (*SelfStreamRequest) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{30} +} + +func (x *SelfStreamRequest) GetStreamKey() string { + if x != nil { + return x.StreamKey + } + return "" +} + +type SelfStreamResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Stream uint64 `protobuf:"varint,1,opt,name=stream,proto3" json:"stream,omitempty"` + Course uint64 `protobuf:"varint,2,opt,name=course,proto3" json:"course,omitempty"` + CourseYear uint64 `protobuf:"varint,3,opt,name=courseYear,proto3" json:"courseYear,omitempty"` + StreamStart *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=streamStart,proto3" json:"streamStart,omitempty"` + StreamEnd *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=streamEnd,proto3" json:"streamEnd,omitempty"` + UploadVoD bool `protobuf:"varint,6,opt,name=uploadVoD,proto3" json:"uploadVoD,omitempty"` + IngestServer string `protobuf:"bytes,7,opt,name=ingestServer,proto3" json:"ingestServer,omitempty"` + StreamName string `protobuf:"bytes,8,opt,name=streamName,proto3" json:"streamName,omitempty"` + OutURL string `protobuf:"bytes,9,opt,name=outURL,proto3" json:"outURL,omitempty"` +} + +func (x *SelfStreamResponse) Reset() { + *x = SelfStreamResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_runner_proto_msgTypes[31] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SelfStreamResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SelfStreamResponse) ProtoMessage() {} + +func (x *SelfStreamResponse) ProtoReflect() protoreflect.Message { + mi := &file_runner_proto_msgTypes[31] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SelfStreamResponse.ProtoReflect.Descriptor instead. +func (*SelfStreamResponse) Descriptor() ([]byte, []int) { + return file_runner_proto_rawDescGZIP(), []int{31} +} + +func (x *SelfStreamResponse) GetStream() uint64 { + if x != nil { + return x.Stream + } + return 0 +} + +func (x *SelfStreamResponse) GetCourse() uint64 { + if x != nil { + return x.Course + } + return 0 +} + +func (x *SelfStreamResponse) GetCourseYear() uint64 { + if x != nil { + return x.CourseYear + } + return 0 +} + +func (x *SelfStreamResponse) GetStreamStart() *timestamppb.Timestamp { + if x != nil { + return x.StreamStart + } + return nil +} + +func (x *SelfStreamResponse) GetStreamEnd() *timestamppb.Timestamp { + if x != nil { + return x.StreamEnd + } + return nil +} + +func (x *SelfStreamResponse) GetUploadVoD() bool { + if x != nil { + return x.UploadVoD + } + return false +} + +func (x *SelfStreamResponse) GetIngestServer() string { + if x != nil { + return x.IngestServer + } + return "" +} + +func (x *SelfStreamResponse) GetStreamName() string { + if x != nil { + return x.StreamName + } + return "" +} + +func (x *SelfStreamResponse) GetOutURL() string { + if x != nil { + return x.OutURL + } + return "" +} + +var File_runner_proto protoreflect.FileDescriptor + +var file_runner_proto_rawDesc = []byte{ + 0x0a, 0x0c, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x93, 0x02, 0x0a, 0x0d, 0x55, 0x70, + 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x75, 0x6e, 0x6e, 0x65, + 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x52, 0x75, 0x6e, 0x6e, 0x65, + 0x72, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x48, 0x4c, 0x53, 0x55, 0x72, 0x6c, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x48, 0x4c, 0x53, 0x55, 0x72, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x53, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x43, + 0x6f, 0x75, 0x72, 0x73, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x43, + 0x6f, 0x75, 0x72, 0x73, 0x65, 0x59, 0x65, 0x61, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x0a, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x59, 0x65, 0x61, 0x72, 0x12, 0x2e, 0x0a, 0x12, 0x43, + 0x6f, 0x75, 0x72, 0x73, 0x65, 0x54, 0x65, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x72, + 0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x54, + 0x65, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x72, 0x6d, 0x12, 0x22, 0x0a, 0x0c, 0x54, + 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x55, 0x72, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x55, 0x72, 0x6c, 0x22, + 0x4a, 0x0a, 0x0e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1c, 0x0a, + 0x09, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x65, 0x79, 0x22, 0xf2, 0x01, 0x0a, 0x10, + 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, + 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x48, 0x4c, 0x53, 0x55, + 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x48, 0x4c, 0x53, 0x55, 0x72, 0x6c, + 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x1e, 0x0a, 0x0a, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x1e, 0x0a, 0x0a, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x59, 0x65, 0x61, 0x72, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x59, 0x65, 0x61, 0x72, + 0x12, 0x2e, 0x0a, 0x12, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x54, 0x65, 0x61, 0x63, 0x68, 0x69, + 0x6e, 0x67, 0x54, 0x65, 0x72, 0x6d, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x43, 0x6f, + 0x75, 0x72, 0x73, 0x65, 0x54, 0x65, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x72, 0x6d, + 0x22, 0x53, 0x0a, 0x11, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x55, 0x72, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, + 0x69, 0x6c, 0x55, 0x72, 0x6c, 0x22, 0xc6, 0x01, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, + 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, + 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x75, 0x6e, 0x6e, + 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x52, 0x75, 0x6e, 0x6e, + 0x65, 0x72, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x44, 0x61, 0x74, 0x61, 0x55, 0x52, 0x4c, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x44, 0x61, 0x74, 0x61, 0x55, 0x52, 0x4c, 0x12, 0x1e, + 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x57, + 0x0a, 0x13, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x44, 0x12, 0x24, 0x0a, 0x0d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, + 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, + 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x49, 0x44, 0x22, 0x64, 0x0a, 0x12, 0x4c, 0x69, 0x76, 0x65, 0x50, + 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x75, 0x6e, + 0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x52, 0x75, 0x6e, + 0x6e, 0x65, 0x72, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x48, 0x4c, 0x53, 0x55, 0x72, 0x6c, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x48, 0x4c, 0x53, 0x55, 0x72, 0x6c, 0x22, 0x4f, 0x0a, + 0x13, 0x4c, 0x69, 0x76, 0x65, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, + 0x12, 0x1c, 0x0a, 0x09, 0x4c, 0x69, 0x76, 0x65, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x09, 0x4c, 0x69, 0x76, 0x65, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x22, 0x4b, + 0x0a, 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, + 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x50, 0x61, 0x74, 0x68, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x50, 0x61, 0x74, 0x68, 0x22, 0x50, 0x0a, 0x1c, 0x47, + 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6d, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x50, 0x61, 0x74, 0x68, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x50, 0x61, 0x74, 0x68, 0x73, 0x22, 0x53, 0x0a, + 0x07, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x48, 0x6f, 0x75, 0x72, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x05, 0x48, 0x6f, 0x75, 0x72, 0x73, 0x12, 0x18, + 0x0a, 0x07, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x07, 0x4d, 0x69, 0x6e, 0x75, 0x74, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x65, 0x63, 0x6f, + 0x6e, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x53, 0x65, 0x63, 0x6f, 0x6e, + 0x64, 0x73, 0x22, 0xfa, 0x01, 0x0a, 0x1b, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, + 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x20, + 0x0a, 0x0b, 0x50, 0x6c, 0x61, 0x79, 0x6c, 0x69, 0x73, 0x74, 0x55, 0x52, 0x4c, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x50, 0x6c, 0x61, 0x79, 0x6c, 0x69, 0x73, 0x74, 0x55, 0x52, 0x4c, + 0x12, 0x1e, 0x0a, 0x0a, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x1e, 0x0a, 0x0a, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x59, 0x65, 0x61, 0x72, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x59, 0x65, 0x61, 0x72, + 0x12, 0x2e, 0x0a, 0x12, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x54, 0x65, 0x61, 0x63, 0x68, 0x69, + 0x6e, 0x67, 0x54, 0x65, 0x72, 0x6d, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x43, 0x6f, + 0x75, 0x72, 0x73, 0x65, 0x54, 0x65, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x67, 0x54, 0x65, 0x72, 0x6d, + 0x12, 0x2d, 0x0a, 0x08, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, + 0x18, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x22, 0xbb, 0x01, 0x0a, 0x0d, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, + 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x06, 0x63, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x2c, 0x0a, 0x03, 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x03, 0x65, 0x6e, 0x64, 0x12, + 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x2c, 0x0a, 0x0e, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x22, 0x48, 0x0a, 0x10, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, + 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x6b, 0x65, 0x65, 0x70, 0x56, 0x6f, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x6b, 0x65, 0x65, 0x70, 0x56, 0x6f, 0x64, 0x22, + 0x13, 0x0a, 0x11, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5c, 0x0a, 0x0e, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, + 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, + 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, + 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x44, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x22, 0x56, 0x0a, 0x1a, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, + 0x46, 0x6f, 0x72, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, + 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x09, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x4b, 0x65, 0x79, 0x22, 0xaf, 0x02, 0x0a, 0x1b, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x46, 0x6f, 0x72, 0x55, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x43, 0x6f, + 0x75, 0x72, 0x73, 0x65, 0x53, 0x6c, 0x75, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x53, 0x6c, 0x75, 0x67, 0x12, 0x1e, 0x0a, 0x0a, 0x43, 0x6f, + 0x75, 0x72, 0x73, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, + 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x43, 0x6f, + 0x75, 0x72, 0x73, 0x65, 0x59, 0x65, 0x61, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, + 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x59, 0x65, 0x61, 0x72, 0x12, 0x3c, 0x0a, 0x0b, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0b, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x45, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, + 0x6e, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x1c, + 0x0a, 0x09, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x56, 0x69, 0x64, 0x65, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x22, 0xa7, 0x01, 0x0a, + 0x11, 0x56, 0x6f, 0x44, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, + 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1a, + 0x0a, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x48, 0x4c, + 0x53, 0x55, 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x48, 0x4c, 0x53, 0x55, + 0x72, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, + 0x70, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x55, + 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, + 0x61, 0x69, 0x6c, 0x55, 0x72, 0x6c, 0x22, 0x7c, 0x0a, 0x0e, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, + 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x75, 0x6e, 0x6e, + 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x52, 0x75, 0x6e, 0x6e, + 0x65, 0x72, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, + 0x12, 0x1a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0d, + 0x42, 0x02, 0x10, 0x01, 0x52, 0x06, 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x12, 0x16, 0x0a, 0x04, + 0x65, 0x6e, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0d, 0x42, 0x02, 0x10, 0x01, 0x52, 0x04, + 0x65, 0x6e, 0x64, 0x73, 0x22, 0xb5, 0x01, 0x0a, 0x0d, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x1a, + 0x0a, 0x08, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x08, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x48, 0x4c, 0x53, 0x55, 0x72, 0x6c, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x48, 0x4c, 0x53, 0x55, 0x72, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, + 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x22, 0x7b, 0x0a, 0x0b, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x52, + 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x52, + 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x49, 0x44, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x43, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x49, 0x44, 0x12, + 0x18, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xd4, 0x01, 0x0a, 0x12, 0x54, 0x68, + 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, + 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, + 0x50, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, + 0x50, 0x61, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, + 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x2e, 0x0a, 0x12, 0x4c, 0x61, 0x72, 0x67, 0x65, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, + 0x69, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x4c, 0x61, + 0x72, 0x67, 0x65, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x50, 0x61, 0x74, 0x68, + 0x22, 0xbe, 0x01, 0x0a, 0x1e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, + 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x12, + 0x1a, 0x0a, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x08, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, 0x56, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, + 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x46, 0x69, 0x6c, 0x65, 0x50, 0x61, 0x74, + 0x68, 0x12, 0x12, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x45, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x45, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, + 0x65, 0x22, 0x41, 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, + 0x70, 0x6f, 0x72, 0x74, 0x22, 0x22, 0x0a, 0x10, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x49, 0x44, 0x22, 0xc4, 0x02, 0x0a, 0x10, 0x48, 0x65, 0x61, + 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x36, 0x0a, + 0x08, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x08, 0x4c, 0x61, 0x73, + 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, + 0x08, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, + 0x08, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x43, 0x50, 0x55, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x43, 0x50, 0x55, 0x12, 0x16, 0x0a, 0x06, 0x4d, + 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x4d, 0x65, 0x6d, + 0x6f, 0x72, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x69, 0x73, 0x6b, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x44, 0x69, 0x73, 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x70, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x55, 0x70, 0x74, 0x69, 0x6d, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x0a, 0x0d, 0x43, 0x75, 0x72, + 0x72, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0x23, 0x0a, 0x11, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x02, 0x6f, 0x6b, 0x22, 0x31, 0x0a, 0x11, 0x53, 0x65, 0x6c, 0x66, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x4b, 0x65, 0x79, 0x22, 0xd6, 0x02, 0x0a, 0x12, 0x53, 0x65, 0x6c, 0x66, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, + 0x0a, 0x06, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, + 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x75, 0x72, 0x73, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x63, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x12, 0x1e, + 0x0a, 0x0a, 0x63, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x59, 0x65, 0x61, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0a, 0x63, 0x6f, 0x75, 0x72, 0x73, 0x65, 0x59, 0x65, 0x61, 0x72, 0x12, 0x3c, + 0x0a, 0x0b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x0b, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x38, 0x0a, 0x09, + 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x73, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x45, 0x6e, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x56, 0x6f, 0x44, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x75, 0x70, 0x6c, 0x6f, 0x61, + 0x64, 0x56, 0x6f, 0x44, 0x12, 0x22, 0x0a, 0x0c, 0x69, 0x6e, 0x67, 0x65, 0x73, 0x74, 0x53, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x69, 0x6e, 0x67, 0x65, + 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x73, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x55, + 0x52, 0x4c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x55, 0x52, 0x4c, + 0x32, 0x82, 0x05, 0x0a, 0x08, 0x54, 0x6f, 0x52, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x44, 0x0a, + 0x0d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x17, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x10, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, + 0x72, 0x65, 0x61, 0x6d, 0x45, 0x6e, 0x64, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x54, 0x0a, 0x13, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x4c, 0x69, + 0x76, 0x65, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x76, 0x65, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x4c, 0x69, 0x76, 0x65, 0x50, 0x72, 0x65, 0x76, 0x69, 0x65, 0x77, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x52, 0x0a, 0x15, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6d, 0x61, 0x67, 0x65, + 0x73, 0x12, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x47, 0x65, 0x6e, + 0x65, 0x72, 0x61, 0x74, 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6d, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x00, 0x12, 0x4d, 0x0a, 0x12, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6d, 0x61, + 0x67, 0x65, 0x12, 0x23, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x53, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6d, 0x61, 0x67, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x00, 0x12, 0x53, 0x0a, 0x12, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, + 0x67, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x4d, 0x0a, 0x10, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x68, 0x75, 0x6d, 0x62, + 0x6e, 0x61, 0x69, 0x6c, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x68, 0x75, 0x6d, + 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x44, 0x0a, 0x0d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x70, 0x6c, 0x6f, + 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x32, 0xd8, 0x06, 0x0a, 0x0a, 0x46, 0x72, 0x6f, 0x6d, 0x52, 0x75, + 0x6e, 0x6e, 0x65, 0x72, 0x12, 0x43, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x12, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x09, 0x48, 0x65, 0x61, + 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x48, 0x65, + 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x50, 0x0a, 0x11, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x65, 0x6c, 0x66, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x53, 0x65, 0x6c, 0x66, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, + 0x65, 0x6c, 0x66, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x17, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x56, 0x6f, 0x44, + 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x1b, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x6f, 0x44, 0x55, 0x70, 0x6c, + 0x6f, 0x61, 0x64, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x1a, 0x10, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x00, 0x12, + 0x44, 0x0a, 0x14, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, + 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x53, 0x69, 0x6c, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, + 0x73, 0x1a, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x13, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x17, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x74, + 0x61, 0x72, 0x74, 0x65, 0x64, 0x1a, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x11, 0x4e, 0x6f, 0x74, + 0x69, 0x66, 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x6e, 0x64, 0x65, 0x64, 0x12, 0x15, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x45, 0x6e, 0x64, 0x65, 0x64, 0x1a, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x18, 0x4e, 0x6f, 0x74, + 0x69, 0x66, 0x79, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x46, 0x69, 0x6e, + 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x54, 0x68, 0x75, 0x6d, 0x62, 0x6e, 0x61, 0x69, 0x6c, 0x73, 0x46, 0x69, 0x6e, 0x69, 0x73, + 0x68, 0x65, 0x64, 0x1a, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x18, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x79, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x46, 0x61, 0x69, 0x6c, + 0x75, 0x72, 0x65, 0x12, 0x28, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, + 0x65, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x10, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, + 0x00, 0x12, 0x67, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x6e, + 0x66, 0x6f, 0x46, 0x6f, 0x72, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x24, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x66, + 0x6f, 0x46, 0x6f, 0x72, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x49, 0x6e, 0x66, 0x6f, 0x46, 0x6f, 0x72, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x44, 0x0a, 0x14, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, + 0x65, 0x64, 0x12, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x1a, 0x10, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x00, + 0x42, 0x11, 0x5a, 0x0f, 0x72, 0x75, 0x6e, 0x6e, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_runner_proto_rawDescOnce sync.Once + file_runner_proto_rawDescData = file_runner_proto_rawDesc +) + +func file_runner_proto_rawDescGZIP() []byte { + file_runner_proto_rawDescOnce.Do(func() { + file_runner_proto_rawDescData = protoimpl.X.CompressGZIP(file_runner_proto_rawDescData) + }) + return file_runner_proto_rawDescData +} + +var file_runner_proto_msgTypes = make([]protoimpl.MessageInfo, 32) +var file_runner_proto_goTypes = []interface{}{ + (*UploadRequest)(nil), // 0: protobuf.UploadRequest + (*UploadResponse)(nil), // 1: protobuf.UploadResponse + (*ThumbnailRequest)(nil), // 2: protobuf.ThumbnailRequest + (*ThumbnailResponse)(nil), // 3: protobuf.ThumbnailResponse + (*TranscodingRequest)(nil), // 4: protobuf.TranscodingRequest + (*TranscodingResponse)(nil), // 5: protobuf.TranscodingResponse + (*LivePreviewRequest)(nil), // 6: protobuf.LivePreviewRequest + (*LivePreviewResponse)(nil), // 7: protobuf.LivePreviewResponse + (*DeleteSectionImageRequest)(nil), // 8: protobuf.DeleteSectionImageRequest + (*GenerateSectionImageResponse)(nil), // 9: protobuf.GenerateSectionImageResponse + (*Section)(nil), // 10: protobuf.Section + (*GenerateSectionImageRequest)(nil), // 11: protobuf.GenerateSectionImageRequest + (*Status)(nil), // 12: protobuf.Status + (*StreamRequest)(nil), // 13: protobuf.StreamRequest + (*StreamResponse)(nil), // 14: protobuf.StreamResponse + (*StreamEndRequest)(nil), // 15: protobuf.StreamEndRequest + (*StreamEndResponse)(nil), // 16: protobuf.StreamEndResponse + (*ActionFinished)(nil), // 17: protobuf.ActionFinished + (*StreamInfoForUploadRequest)(nil), // 18: protobuf.StreamInfoForUploadRequest + (*StreamInfoForUploadResponse)(nil), // 19: protobuf.StreamInfoForUploadResponse + (*VoDUploadFinished)(nil), // 20: protobuf.VoDUploadFinished + (*SilenceResults)(nil), // 21: protobuf.SilenceResults + (*StreamStarted)(nil), // 22: protobuf.StreamStarted + (*StreamEnded)(nil), // 23: protobuf.StreamEnded + (*ThumbnailsFinished)(nil), // 24: protobuf.ThumbnailsFinished + (*TranscodingFailureNotification)(nil), // 25: protobuf.TranscodingFailureNotification + (*RegisterRequest)(nil), // 26: protobuf.RegisterRequest + (*RegisterResponse)(nil), // 27: protobuf.RegisterResponse + (*HeartbeatRequest)(nil), // 28: protobuf.HeartbeatRequest + (*HeartbeatResponse)(nil), // 29: protobuf.HeartbeatResponse + (*SelfStreamRequest)(nil), // 30: protobuf.SelfStreamRequest + (*SelfStreamResponse)(nil), // 31: protobuf.SelfStreamResponse + (*timestamppb.Timestamp)(nil), // 32: google.protobuf.Timestamp +} +var file_runner_proto_depIdxs = []int32{ + 10, // 0: protobuf.GenerateSectionImageRequest.Sections:type_name -> protobuf.Section + 32, // 1: protobuf.StreamRequest.end:type_name -> google.protobuf.Timestamp + 32, // 2: protobuf.StreamInfoForUploadResponse.StreamStart:type_name -> google.protobuf.Timestamp + 32, // 3: protobuf.StreamInfoForUploadResponse.StreamEnd:type_name -> google.protobuf.Timestamp + 32, // 4: protobuf.HeartbeatRequest.LastSeen:type_name -> google.protobuf.Timestamp + 32, // 5: protobuf.SelfStreamResponse.streamStart:type_name -> google.protobuf.Timestamp + 32, // 6: protobuf.SelfStreamResponse.streamEnd:type_name -> google.protobuf.Timestamp + 13, // 7: protobuf.ToRunner.RequestStream:input_type -> protobuf.StreamRequest + 15, // 8: protobuf.ToRunner.RequestStreamEnd:input_type -> protobuf.StreamEndRequest + 6, // 9: protobuf.ToRunner.GenerateLivePreview:input_type -> protobuf.LivePreviewRequest + 11, // 10: protobuf.ToRunner.GenerateSectionImages:input_type -> protobuf.GenerateSectionImageRequest + 8, // 11: protobuf.ToRunner.DeleteSectionImage:input_type -> protobuf.DeleteSectionImageRequest + 4, // 12: protobuf.ToRunner.RequestTranscoding:input_type -> protobuf.TranscodingRequest + 2, // 13: protobuf.ToRunner.RequestThumbnail:input_type -> protobuf.ThumbnailRequest + 0, // 14: protobuf.ToRunner.RequestUpload:input_type -> protobuf.UploadRequest + 26, // 15: protobuf.FromRunner.Register:input_type -> protobuf.RegisterRequest + 28, // 16: protobuf.FromRunner.Heartbeat:input_type -> protobuf.HeartbeatRequest + 30, // 17: protobuf.FromRunner.RequestSelfStream:input_type -> protobuf.SelfStreamRequest + 20, // 18: protobuf.FromRunner.NotifyVoDUploadFinished:input_type -> protobuf.VoDUploadFinished + 21, // 19: protobuf.FromRunner.NotifySilenceResults:input_type -> protobuf.SilenceResults + 22, // 20: protobuf.FromRunner.NotifyStreamStarted:input_type -> protobuf.StreamStarted + 23, // 21: protobuf.FromRunner.NotifyStreamEnded:input_type -> protobuf.StreamEnded + 24, // 22: protobuf.FromRunner.NotifyThumbnailsFinished:input_type -> protobuf.ThumbnailsFinished + 25, // 23: protobuf.FromRunner.NotifyTranscodingFailure:input_type -> protobuf.TranscodingFailureNotification + 18, // 24: protobuf.FromRunner.GetStreamInfoForUpload:input_type -> protobuf.StreamInfoForUploadRequest + 17, // 25: protobuf.FromRunner.NotifyActionFinished:input_type -> protobuf.ActionFinished + 14, // 26: protobuf.ToRunner.RequestStream:output_type -> protobuf.StreamResponse + 16, // 27: protobuf.ToRunner.RequestStreamEnd:output_type -> protobuf.StreamEndResponse + 7, // 28: protobuf.ToRunner.GenerateLivePreview:output_type -> protobuf.LivePreviewResponse + 12, // 29: protobuf.ToRunner.GenerateSectionImages:output_type -> protobuf.Status + 12, // 30: protobuf.ToRunner.DeleteSectionImage:output_type -> protobuf.Status + 5, // 31: protobuf.ToRunner.RequestTranscoding:output_type -> protobuf.TranscodingResponse + 3, // 32: protobuf.ToRunner.RequestThumbnail:output_type -> protobuf.ThumbnailResponse + 1, // 33: protobuf.ToRunner.RequestUpload:output_type -> protobuf.UploadResponse + 27, // 34: protobuf.FromRunner.Register:output_type -> protobuf.RegisterResponse + 29, // 35: protobuf.FromRunner.Heartbeat:output_type -> protobuf.HeartbeatResponse + 31, // 36: protobuf.FromRunner.RequestSelfStream:output_type -> protobuf.SelfStreamResponse + 12, // 37: protobuf.FromRunner.NotifyVoDUploadFinished:output_type -> protobuf.Status + 12, // 38: protobuf.FromRunner.NotifySilenceResults:output_type -> protobuf.Status + 12, // 39: protobuf.FromRunner.NotifyStreamStarted:output_type -> protobuf.Status + 12, // 40: protobuf.FromRunner.NotifyStreamEnded:output_type -> protobuf.Status + 12, // 41: protobuf.FromRunner.NotifyThumbnailsFinished:output_type -> protobuf.Status + 12, // 42: protobuf.FromRunner.NotifyTranscodingFailure:output_type -> protobuf.Status + 19, // 43: protobuf.FromRunner.GetStreamInfoForUpload:output_type -> protobuf.StreamInfoForUploadResponse + 12, // 44: protobuf.FromRunner.NotifyActionFinished:output_type -> protobuf.Status + 26, // [26:45] is the sub-list for method output_type + 7, // [7:26] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name +} + +func init() { file_runner_proto_init() } +func file_runner_proto_init() { + if File_runner_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_runner_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UploadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*UploadResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ThumbnailRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ThumbnailResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TranscodingRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TranscodingResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LivePreviewRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LivePreviewResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteSectionImageRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenerateSectionImageResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Section); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GenerateSectionImageRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Status); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StreamRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StreamResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StreamEndRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StreamEndResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ActionFinished); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StreamInfoForUploadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StreamInfoForUploadResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VoDUploadFinished); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SilenceResults); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StreamStarted); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StreamEnded); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ThumbnailsFinished); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TranscodingFailureNotification); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HeartbeatRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[29].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*HeartbeatResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[30].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SelfStreamRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_runner_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SelfStreamResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_runner_proto_rawDesc, + NumEnums: 0, + NumMessages: 32, + NumExtensions: 0, + NumServices: 2, + }, + GoTypes: file_runner_proto_goTypes, + DependencyIndexes: file_runner_proto_depIdxs, + MessageInfos: file_runner_proto_msgTypes, + }.Build() + File_runner_proto = out.File + file_runner_proto_rawDesc = nil + file_runner_proto_goTypes = nil + file_runner_proto_depIdxs = nil +} diff --git a/runner/protobuf/runner_grpc.pb.go b/runner/protobuf/runner_grpc.pb.go new file mode 100644 index 000000000..ecc84a41c --- /dev/null +++ b/runner/protobuf/runner_grpc.pb.go @@ -0,0 +1,807 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.21.12 +// source: runner.proto + +package protobuf + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// ToRunnerClient is the client API for ToRunner service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ToRunnerClient interface { + // Requests a stream from a lecture hall + RequestStream(ctx context.Context, in *StreamRequest, opts ...grpc.CallOption) (*StreamResponse, error) + RequestStreamEnd(ctx context.Context, in *StreamEndRequest, opts ...grpc.CallOption) (*StreamEndResponse, error) + GenerateLivePreview(ctx context.Context, in *LivePreviewRequest, opts ...grpc.CallOption) (*LivePreviewResponse, error) + GenerateSectionImages(ctx context.Context, in *GenerateSectionImageRequest, opts ...grpc.CallOption) (*Status, error) + DeleteSectionImage(ctx context.Context, in *DeleteSectionImageRequest, opts ...grpc.CallOption) (*Status, error) + RequestTranscoding(ctx context.Context, in *TranscodingRequest, opts ...grpc.CallOption) (*TranscodingResponse, error) + RequestThumbnail(ctx context.Context, in *ThumbnailRequest, opts ...grpc.CallOption) (*ThumbnailResponse, error) + RequestUpload(ctx context.Context, in *UploadRequest, opts ...grpc.CallOption) (*UploadResponse, error) +} + +type toRunnerClient struct { + cc grpc.ClientConnInterface +} + +func NewToRunnerClient(cc grpc.ClientConnInterface) ToRunnerClient { + return &toRunnerClient{cc} +} + +func (c *toRunnerClient) RequestStream(ctx context.Context, in *StreamRequest, opts ...grpc.CallOption) (*StreamResponse, error) { + out := new(StreamResponse) + err := c.cc.Invoke(ctx, "/protobuf.ToRunner/RequestStream", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *toRunnerClient) RequestStreamEnd(ctx context.Context, in *StreamEndRequest, opts ...grpc.CallOption) (*StreamEndResponse, error) { + out := new(StreamEndResponse) + err := c.cc.Invoke(ctx, "/protobuf.ToRunner/RequestStreamEnd", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *toRunnerClient) GenerateLivePreview(ctx context.Context, in *LivePreviewRequest, opts ...grpc.CallOption) (*LivePreviewResponse, error) { + out := new(LivePreviewResponse) + err := c.cc.Invoke(ctx, "/protobuf.ToRunner/GenerateLivePreview", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *toRunnerClient) GenerateSectionImages(ctx context.Context, in *GenerateSectionImageRequest, opts ...grpc.CallOption) (*Status, error) { + out := new(Status) + err := c.cc.Invoke(ctx, "/protobuf.ToRunner/GenerateSectionImages", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *toRunnerClient) DeleteSectionImage(ctx context.Context, in *DeleteSectionImageRequest, opts ...grpc.CallOption) (*Status, error) { + out := new(Status) + err := c.cc.Invoke(ctx, "/protobuf.ToRunner/DeleteSectionImage", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *toRunnerClient) RequestTranscoding(ctx context.Context, in *TranscodingRequest, opts ...grpc.CallOption) (*TranscodingResponse, error) { + out := new(TranscodingResponse) + err := c.cc.Invoke(ctx, "/protobuf.ToRunner/RequestTranscoding", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *toRunnerClient) RequestThumbnail(ctx context.Context, in *ThumbnailRequest, opts ...grpc.CallOption) (*ThumbnailResponse, error) { + out := new(ThumbnailResponse) + err := c.cc.Invoke(ctx, "/protobuf.ToRunner/RequestThumbnail", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *toRunnerClient) RequestUpload(ctx context.Context, in *UploadRequest, opts ...grpc.CallOption) (*UploadResponse, error) { + out := new(UploadResponse) + err := c.cc.Invoke(ctx, "/protobuf.ToRunner/RequestUpload", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ToRunnerServer is the server API for ToRunner service. +// All implementations must embed UnimplementedToRunnerServer +// for forward compatibility +type ToRunnerServer interface { + // Requests a stream from a lecture hall + RequestStream(context.Context, *StreamRequest) (*StreamResponse, error) + RequestStreamEnd(context.Context, *StreamEndRequest) (*StreamEndResponse, error) + GenerateLivePreview(context.Context, *LivePreviewRequest) (*LivePreviewResponse, error) + GenerateSectionImages(context.Context, *GenerateSectionImageRequest) (*Status, error) + DeleteSectionImage(context.Context, *DeleteSectionImageRequest) (*Status, error) + RequestTranscoding(context.Context, *TranscodingRequest) (*TranscodingResponse, error) + RequestThumbnail(context.Context, *ThumbnailRequest) (*ThumbnailResponse, error) + RequestUpload(context.Context, *UploadRequest) (*UploadResponse, error) + mustEmbedUnimplementedToRunnerServer() +} + +// UnimplementedToRunnerServer must be embedded to have forward compatible implementations. +type UnimplementedToRunnerServer struct { +} + +func (UnimplementedToRunnerServer) RequestStream(context.Context, *StreamRequest) (*StreamResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RequestStream not implemented") +} +func (UnimplementedToRunnerServer) RequestStreamEnd(context.Context, *StreamEndRequest) (*StreamEndResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RequestStreamEnd not implemented") +} +func (UnimplementedToRunnerServer) GenerateLivePreview(context.Context, *LivePreviewRequest) (*LivePreviewResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GenerateLivePreview not implemented") +} +func (UnimplementedToRunnerServer) GenerateSectionImages(context.Context, *GenerateSectionImageRequest) (*Status, error) { + return nil, status.Errorf(codes.Unimplemented, "method GenerateSectionImages not implemented") +} +func (UnimplementedToRunnerServer) DeleteSectionImage(context.Context, *DeleteSectionImageRequest) (*Status, error) { + return nil, status.Errorf(codes.Unimplemented, "method DeleteSectionImage not implemented") +} +func (UnimplementedToRunnerServer) RequestTranscoding(context.Context, *TranscodingRequest) (*TranscodingResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RequestTranscoding not implemented") +} +func (UnimplementedToRunnerServer) RequestThumbnail(context.Context, *ThumbnailRequest) (*ThumbnailResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RequestThumbnail not implemented") +} +func (UnimplementedToRunnerServer) RequestUpload(context.Context, *UploadRequest) (*UploadResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RequestUpload not implemented") +} +func (UnimplementedToRunnerServer) mustEmbedUnimplementedToRunnerServer() {} + +// UnsafeToRunnerServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ToRunnerServer will +// result in compilation errors. +type UnsafeToRunnerServer interface { + mustEmbedUnimplementedToRunnerServer() +} + +func RegisterToRunnerServer(s grpc.ServiceRegistrar, srv ToRunnerServer) { + s.RegisterService(&ToRunner_ServiceDesc, srv) +} + +func _ToRunner_RequestStream_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StreamRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ToRunnerServer).RequestStream(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.ToRunner/RequestStream", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ToRunnerServer).RequestStream(ctx, req.(*StreamRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ToRunner_RequestStreamEnd_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StreamEndRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ToRunnerServer).RequestStreamEnd(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.ToRunner/RequestStreamEnd", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ToRunnerServer).RequestStreamEnd(ctx, req.(*StreamEndRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ToRunner_GenerateLivePreview_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LivePreviewRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ToRunnerServer).GenerateLivePreview(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.ToRunner/GenerateLivePreview", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ToRunnerServer).GenerateLivePreview(ctx, req.(*LivePreviewRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ToRunner_GenerateSectionImages_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GenerateSectionImageRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ToRunnerServer).GenerateSectionImages(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.ToRunner/GenerateSectionImages", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ToRunnerServer).GenerateSectionImages(ctx, req.(*GenerateSectionImageRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ToRunner_DeleteSectionImage_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteSectionImageRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ToRunnerServer).DeleteSectionImage(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.ToRunner/DeleteSectionImage", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ToRunnerServer).DeleteSectionImage(ctx, req.(*DeleteSectionImageRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ToRunner_RequestTranscoding_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TranscodingRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ToRunnerServer).RequestTranscoding(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.ToRunner/RequestTranscoding", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ToRunnerServer).RequestTranscoding(ctx, req.(*TranscodingRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ToRunner_RequestThumbnail_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ThumbnailRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ToRunnerServer).RequestThumbnail(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.ToRunner/RequestThumbnail", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ToRunnerServer).RequestThumbnail(ctx, req.(*ThumbnailRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ToRunner_RequestUpload_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UploadRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ToRunnerServer).RequestUpload(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.ToRunner/RequestUpload", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ToRunnerServer).RequestUpload(ctx, req.(*UploadRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// ToRunner_ServiceDesc is the grpc.ServiceDesc for ToRunner service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ToRunner_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "protobuf.ToRunner", + HandlerType: (*ToRunnerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "RequestStream", + Handler: _ToRunner_RequestStream_Handler, + }, + { + MethodName: "RequestStreamEnd", + Handler: _ToRunner_RequestStreamEnd_Handler, + }, + { + MethodName: "GenerateLivePreview", + Handler: _ToRunner_GenerateLivePreview_Handler, + }, + { + MethodName: "GenerateSectionImages", + Handler: _ToRunner_GenerateSectionImages_Handler, + }, + { + MethodName: "DeleteSectionImage", + Handler: _ToRunner_DeleteSectionImage_Handler, + }, + { + MethodName: "RequestTranscoding", + Handler: _ToRunner_RequestTranscoding_Handler, + }, + { + MethodName: "RequestThumbnail", + Handler: _ToRunner_RequestThumbnail_Handler, + }, + { + MethodName: "RequestUpload", + Handler: _ToRunner_RequestUpload_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "runner.proto", +} + +// FromRunnerClient is the client API for FromRunner service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type FromRunnerClient interface { + // Register is a request to the server to join the runners pool. + Register(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (*RegisterResponse, error) + Heartbeat(ctx context.Context, in *HeartbeatRequest, opts ...grpc.CallOption) (*HeartbeatResponse, error) + RequestSelfStream(ctx context.Context, in *SelfStreamRequest, opts ...grpc.CallOption) (*SelfStreamResponse, error) + NotifyVoDUploadFinished(ctx context.Context, in *VoDUploadFinished, opts ...grpc.CallOption) (*Status, error) + NotifySilenceResults(ctx context.Context, in *SilenceResults, opts ...grpc.CallOption) (*Status, error) + NotifyStreamStarted(ctx context.Context, in *StreamStarted, opts ...grpc.CallOption) (*Status, error) + NotifyStreamEnded(ctx context.Context, in *StreamEnded, opts ...grpc.CallOption) (*Status, error) + NotifyThumbnailsFinished(ctx context.Context, in *ThumbnailsFinished, opts ...grpc.CallOption) (*Status, error) + NotifyTranscodingFailure(ctx context.Context, in *TranscodingFailureNotification, opts ...grpc.CallOption) (*Status, error) + GetStreamInfoForUpload(ctx context.Context, in *StreamInfoForUploadRequest, opts ...grpc.CallOption) (*StreamInfoForUploadResponse, error) + NotifyActionFinished(ctx context.Context, in *ActionFinished, opts ...grpc.CallOption) (*Status, error) +} + +type fromRunnerClient struct { + cc grpc.ClientConnInterface +} + +func NewFromRunnerClient(cc grpc.ClientConnInterface) FromRunnerClient { + return &fromRunnerClient{cc} +} + +func (c *fromRunnerClient) Register(ctx context.Context, in *RegisterRequest, opts ...grpc.CallOption) (*RegisterResponse, error) { + out := new(RegisterResponse) + err := c.cc.Invoke(ctx, "/protobuf.FromRunner/Register", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fromRunnerClient) Heartbeat(ctx context.Context, in *HeartbeatRequest, opts ...grpc.CallOption) (*HeartbeatResponse, error) { + out := new(HeartbeatResponse) + err := c.cc.Invoke(ctx, "/protobuf.FromRunner/Heartbeat", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fromRunnerClient) RequestSelfStream(ctx context.Context, in *SelfStreamRequest, opts ...grpc.CallOption) (*SelfStreamResponse, error) { + out := new(SelfStreamResponse) + err := c.cc.Invoke(ctx, "/protobuf.FromRunner/RequestSelfStream", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fromRunnerClient) NotifyVoDUploadFinished(ctx context.Context, in *VoDUploadFinished, opts ...grpc.CallOption) (*Status, error) { + out := new(Status) + err := c.cc.Invoke(ctx, "/protobuf.FromRunner/NotifyVoDUploadFinished", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fromRunnerClient) NotifySilenceResults(ctx context.Context, in *SilenceResults, opts ...grpc.CallOption) (*Status, error) { + out := new(Status) + err := c.cc.Invoke(ctx, "/protobuf.FromRunner/NotifySilenceResults", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fromRunnerClient) NotifyStreamStarted(ctx context.Context, in *StreamStarted, opts ...grpc.CallOption) (*Status, error) { + out := new(Status) + err := c.cc.Invoke(ctx, "/protobuf.FromRunner/NotifyStreamStarted", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fromRunnerClient) NotifyStreamEnded(ctx context.Context, in *StreamEnded, opts ...grpc.CallOption) (*Status, error) { + out := new(Status) + err := c.cc.Invoke(ctx, "/protobuf.FromRunner/NotifyStreamEnded", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fromRunnerClient) NotifyThumbnailsFinished(ctx context.Context, in *ThumbnailsFinished, opts ...grpc.CallOption) (*Status, error) { + out := new(Status) + err := c.cc.Invoke(ctx, "/protobuf.FromRunner/NotifyThumbnailsFinished", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fromRunnerClient) NotifyTranscodingFailure(ctx context.Context, in *TranscodingFailureNotification, opts ...grpc.CallOption) (*Status, error) { + out := new(Status) + err := c.cc.Invoke(ctx, "/protobuf.FromRunner/NotifyTranscodingFailure", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fromRunnerClient) GetStreamInfoForUpload(ctx context.Context, in *StreamInfoForUploadRequest, opts ...grpc.CallOption) (*StreamInfoForUploadResponse, error) { + out := new(StreamInfoForUploadResponse) + err := c.cc.Invoke(ctx, "/protobuf.FromRunner/GetStreamInfoForUpload", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fromRunnerClient) NotifyActionFinished(ctx context.Context, in *ActionFinished, opts ...grpc.CallOption) (*Status, error) { + out := new(Status) + err := c.cc.Invoke(ctx, "/protobuf.FromRunner/NotifyActionFinished", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// FromRunnerServer is the server API for FromRunner service. +// All implementations must embed UnimplementedFromRunnerServer +// for forward compatibility +type FromRunnerServer interface { + // Register is a request to the server to join the runners pool. + Register(context.Context, *RegisterRequest) (*RegisterResponse, error) + Heartbeat(context.Context, *HeartbeatRequest) (*HeartbeatResponse, error) + RequestSelfStream(context.Context, *SelfStreamRequest) (*SelfStreamResponse, error) + NotifyVoDUploadFinished(context.Context, *VoDUploadFinished) (*Status, error) + NotifySilenceResults(context.Context, *SilenceResults) (*Status, error) + NotifyStreamStarted(context.Context, *StreamStarted) (*Status, error) + NotifyStreamEnded(context.Context, *StreamEnded) (*Status, error) + NotifyThumbnailsFinished(context.Context, *ThumbnailsFinished) (*Status, error) + NotifyTranscodingFailure(context.Context, *TranscodingFailureNotification) (*Status, error) + GetStreamInfoForUpload(context.Context, *StreamInfoForUploadRequest) (*StreamInfoForUploadResponse, error) + NotifyActionFinished(context.Context, *ActionFinished) (*Status, error) + mustEmbedUnimplementedFromRunnerServer() +} + +// UnimplementedFromRunnerServer must be embedded to have forward compatible implementations. +type UnimplementedFromRunnerServer struct { +} + +func (UnimplementedFromRunnerServer) Register(context.Context, *RegisterRequest) (*RegisterResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Register not implemented") +} +func (UnimplementedFromRunnerServer) Heartbeat(context.Context, *HeartbeatRequest) (*HeartbeatResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Heartbeat not implemented") +} +func (UnimplementedFromRunnerServer) RequestSelfStream(context.Context, *SelfStreamRequest) (*SelfStreamResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RequestSelfStream not implemented") +} +func (UnimplementedFromRunnerServer) NotifyVoDUploadFinished(context.Context, *VoDUploadFinished) (*Status, error) { + return nil, status.Errorf(codes.Unimplemented, "method NotifyVoDUploadFinished not implemented") +} +func (UnimplementedFromRunnerServer) NotifySilenceResults(context.Context, *SilenceResults) (*Status, error) { + return nil, status.Errorf(codes.Unimplemented, "method NotifySilenceResults not implemented") +} +func (UnimplementedFromRunnerServer) NotifyStreamStarted(context.Context, *StreamStarted) (*Status, error) { + return nil, status.Errorf(codes.Unimplemented, "method NotifyStreamStarted not implemented") +} +func (UnimplementedFromRunnerServer) NotifyStreamEnded(context.Context, *StreamEnded) (*Status, error) { + return nil, status.Errorf(codes.Unimplemented, "method NotifyStreamEnded not implemented") +} +func (UnimplementedFromRunnerServer) NotifyThumbnailsFinished(context.Context, *ThumbnailsFinished) (*Status, error) { + return nil, status.Errorf(codes.Unimplemented, "method NotifyThumbnailsFinished not implemented") +} +func (UnimplementedFromRunnerServer) NotifyTranscodingFailure(context.Context, *TranscodingFailureNotification) (*Status, error) { + return nil, status.Errorf(codes.Unimplemented, "method NotifyTranscodingFailure not implemented") +} +func (UnimplementedFromRunnerServer) GetStreamInfoForUpload(context.Context, *StreamInfoForUploadRequest) (*StreamInfoForUploadResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetStreamInfoForUpload not implemented") +} +func (UnimplementedFromRunnerServer) NotifyActionFinished(context.Context, *ActionFinished) (*Status, error) { + return nil, status.Errorf(codes.Unimplemented, "method NotifyActionFinished not implemented") +} +func (UnimplementedFromRunnerServer) mustEmbedUnimplementedFromRunnerServer() {} + +// UnsafeFromRunnerServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to FromRunnerServer will +// result in compilation errors. +type UnsafeFromRunnerServer interface { + mustEmbedUnimplementedFromRunnerServer() +} + +func RegisterFromRunnerServer(s grpc.ServiceRegistrar, srv FromRunnerServer) { + s.RegisterService(&FromRunner_ServiceDesc, srv) +} + +func _FromRunner_Register_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RegisterRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FromRunnerServer).Register(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.FromRunner/Register", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FromRunnerServer).Register(ctx, req.(*RegisterRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FromRunner_Heartbeat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HeartbeatRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FromRunnerServer).Heartbeat(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.FromRunner/Heartbeat", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FromRunnerServer).Heartbeat(ctx, req.(*HeartbeatRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FromRunner_RequestSelfStream_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SelfStreamRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FromRunnerServer).RequestSelfStream(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.FromRunner/RequestSelfStream", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FromRunnerServer).RequestSelfStream(ctx, req.(*SelfStreamRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FromRunner_NotifyVoDUploadFinished_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(VoDUploadFinished) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FromRunnerServer).NotifyVoDUploadFinished(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.FromRunner/NotifyVoDUploadFinished", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FromRunnerServer).NotifyVoDUploadFinished(ctx, req.(*VoDUploadFinished)) + } + return interceptor(ctx, in, info, handler) +} + +func _FromRunner_NotifySilenceResults_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SilenceResults) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FromRunnerServer).NotifySilenceResults(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.FromRunner/NotifySilenceResults", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FromRunnerServer).NotifySilenceResults(ctx, req.(*SilenceResults)) + } + return interceptor(ctx, in, info, handler) +} + +func _FromRunner_NotifyStreamStarted_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StreamStarted) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FromRunnerServer).NotifyStreamStarted(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.FromRunner/NotifyStreamStarted", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FromRunnerServer).NotifyStreamStarted(ctx, req.(*StreamStarted)) + } + return interceptor(ctx, in, info, handler) +} + +func _FromRunner_NotifyStreamEnded_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StreamEnded) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FromRunnerServer).NotifyStreamEnded(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.FromRunner/NotifyStreamEnded", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FromRunnerServer).NotifyStreamEnded(ctx, req.(*StreamEnded)) + } + return interceptor(ctx, in, info, handler) +} + +func _FromRunner_NotifyThumbnailsFinished_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ThumbnailsFinished) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FromRunnerServer).NotifyThumbnailsFinished(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.FromRunner/NotifyThumbnailsFinished", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FromRunnerServer).NotifyThumbnailsFinished(ctx, req.(*ThumbnailsFinished)) + } + return interceptor(ctx, in, info, handler) +} + +func _FromRunner_NotifyTranscodingFailure_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TranscodingFailureNotification) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FromRunnerServer).NotifyTranscodingFailure(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.FromRunner/NotifyTranscodingFailure", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FromRunnerServer).NotifyTranscodingFailure(ctx, req.(*TranscodingFailureNotification)) + } + return interceptor(ctx, in, info, handler) +} + +func _FromRunner_GetStreamInfoForUpload_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(StreamInfoForUploadRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FromRunnerServer).GetStreamInfoForUpload(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.FromRunner/GetStreamInfoForUpload", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FromRunnerServer).GetStreamInfoForUpload(ctx, req.(*StreamInfoForUploadRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FromRunner_NotifyActionFinished_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ActionFinished) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FromRunnerServer).NotifyActionFinished(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/protobuf.FromRunner/NotifyActionFinished", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FromRunnerServer).NotifyActionFinished(ctx, req.(*ActionFinished)) + } + return interceptor(ctx, in, info, handler) +} + +// FromRunner_ServiceDesc is the grpc.ServiceDesc for FromRunner service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var FromRunner_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "protobuf.FromRunner", + HandlerType: (*FromRunnerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Register", + Handler: _FromRunner_Register_Handler, + }, + { + MethodName: "Heartbeat", + Handler: _FromRunner_Heartbeat_Handler, + }, + { + MethodName: "RequestSelfStream", + Handler: _FromRunner_RequestSelfStream_Handler, + }, + { + MethodName: "NotifyVoDUploadFinished", + Handler: _FromRunner_NotifyVoDUploadFinished_Handler, + }, + { + MethodName: "NotifySilenceResults", + Handler: _FromRunner_NotifySilenceResults_Handler, + }, + { + MethodName: "NotifyStreamStarted", + Handler: _FromRunner_NotifyStreamStarted_Handler, + }, + { + MethodName: "NotifyStreamEnded", + Handler: _FromRunner_NotifyStreamEnded_Handler, + }, + { + MethodName: "NotifyThumbnailsFinished", + Handler: _FromRunner_NotifyThumbnailsFinished_Handler, + }, + { + MethodName: "NotifyTranscodingFailure", + Handler: _FromRunner_NotifyTranscodingFailure_Handler, + }, + { + MethodName: "GetStreamInfoForUpload", + Handler: _FromRunner_GetStreamInfoForUpload_Handler, + }, + { + MethodName: "NotifyActionFinished", + Handler: _FromRunner_NotifyActionFinished_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "runner.proto", +} diff --git a/runner/runner.go b/runner/runner.go new file mode 100644 index 000000000..5f6d6b6ac --- /dev/null +++ b/runner/runner.go @@ -0,0 +1,184 @@ +package runner + +import ( + "context" + "fmt" + "github.com/caarlos0/env" + "github.com/tum-dev/gocast/runner/actions" + "github.com/tum-dev/gocast/runner/config" + "github.com/tum-dev/gocast/runner/pkg/logging" + "github.com/tum-dev/gocast/runner/pkg/netutil" + "github.com/tum-dev/gocast/runner/protobuf" + "github.com/tum-dev/gocast/runner/vmstat" + "google.golang.org/grpc" + "google.golang.org/grpc/keepalive" + "google.golang.org/grpc/reflection" + "log/slog" + "net" + "os" + "time" +) + +type envConfig struct { + LogFmt string `env:"LOG_FMT" envDefault:"txt"` + LogLevel string `env:"LOG_LEVEL" envDefault:"debug"` + Port int `env:"PORT" envDefault:"0"` + StoragePath string `env:"STORAGE_PATH" envDefault:"storage/mass"` + SegmentPath string `env:"SEGMENT_PATH" envDefault:"storage/live"` + RecPath string `env:"REC_PATH" envDefault:"storage/rec"` + GocastServer string `env:"GOCAST_SERVER" envDefault:"localhost:50056"` + Hostname string `env:"REALHOST" envDefault:"localhost"` + Version string `env:"VERSION" envDefault:"dev"` +} + +type Runner struct { + cfg envConfig + log *slog.Logger + cmd config.CmdList + + draining bool + ActionCount chan int + activeActions map[string][]*actions.Action + + actions actions.ActionProvider + hlsServer *HLSServer + + stats *vmstat.VmStat + + StartTime time.Time + protobuf.UnimplementedToRunnerServer +} + +func NewRunner(v string) *Runner { + log := logging.GetLogger(v) + var cfg envConfig + if err := env.Parse(&cfg); err != nil { + log.Error("error parsing envConfig", "error", err) + } + log.Info("envConfig loaded", "envConfig", cfg) + + cmd := config.NewCmd(log) + log.Info("loading cmd.yaml", "cmd", cmd) + + vmstats := vmstat.New() + + start := time.Now() + return &Runner{ + log: log, + ActionCount: make(chan int), + activeActions: make(map[string][]*actions.Action), + draining: false, + cfg: cfg, + cmd: *cmd, + actions: actions.ActionProvider{ + Log: log, + Cmd: *cmd, + SegmentDir: cfg.SegmentPath, + RecDir: cfg.RecPath, + MassDir: cfg.StoragePath, + }, + hlsServer: NewHLSServer(cfg.SegmentPath, log.WithGroup("HLSServer")), + stats: vmstats, + StartTime: start, + } +} + +func (r *Runner) Run() { + r.log.Info("Running!") + r.actions.Server = r + if r.cfg.Port == 0 { + r.log.Info("Getting free port") + p, err := netutil.GetFreePort() + if err != nil { + r.log.Error("Failed to get free port", "error", err) + os.Exit(1) + } + r.cfg.Port = p + } + r.log.Info("using port", "port", r.cfg.Port) + + go r.InitApiGrpc() + go func() { + err := r.hlsServer.Start() + if err != nil { + + } + }() + + r.RegisterWithGocast(5) + r.log.Info("successfully connected to gocast") +} + +func (r *Runner) Drain() { + r.log.Info("Runner set to drain.") + r.draining = true +} + +func (r *Runner) InitApiGrpc() { + r.log.Info("Starting gRPC server", "port", r.cfg.Port) + lis, err := net.Listen("tcp", fmt.Sprintf(":%d", r.cfg.Port)) + if err != nil { + r.log.Error("failed to listen", "error", err) + os.Exit(1) + } + grpcServer := grpc.NewServer(grpc.KeepaliveParams(keepalive.ServerParameters{ + MaxConnectionIdle: time.Minute, + MaxConnectionAge: time.Minute, + MaxConnectionAgeGrace: time.Second * 5, + Time: time.Minute * 10, + Timeout: time.Second * 20, + }), logging.GetGrpcLogInterceptor(r.log)) + protobuf.RegisterToRunnerServer(grpcServer, r) + + reflection.Register(grpcServer) + if err := grpcServer.Serve(lis); err != nil { + r.log.Error("failed to serve", "error", err) + os.Exit(1) + } + +} + +func (r *Runner) RunAction(ctx context.Context, a []*actions.Action) string { + r.ActionCount <- len(r.activeActions) + ActionID := ctx.Value("actionID").(string) + r.activeActions[ActionID] = a + go func() { + for _, action := range a { + if action.Canceled { + r.log.Info("skipping action because it was canceled", "action", action.Type) + continue + } + // create new context to make each action cancelable individually + actionContext, cancel := context.WithCancelCause(ctx) + action.Cancel = cancel + r.log.Info("running action", "action", action.Type) + c, err := action.ActionFn(actionContext, r.log.With("action", action.Type)) + if err != nil { + // ensure context is canceled even on error + r.log.Error("action failed", "error", err, "action", action.Type) + action.Cancel(err) + return + } + // pass context to next action without cancel + ctx = context.WithoutCancel(c) + + action.Cancel(nil) + } + }() + + return ActionID +} + +// enrichLogger adds StreamID, CourseID, Version to the logger if present in the context +func enrichLogger(log *slog.Logger, ctx context.Context) *slog.Logger { + if streamID, ok := ctx.Value("stream").(uint64); ok { + log = log.With("streamID", streamID) + } + if courseID, ok := ctx.Value("course").(uint64); ok { + log = log.With("courseID", courseID) + } + if version, ok := ctx.Value("version").(string); ok { + log = log.With("version", version) + } + return log +} diff --git a/runner/runner.proto b/runner/runner.proto new file mode 100644 index 000000000..6c60412de --- /dev/null +++ b/runner/runner.proto @@ -0,0 +1,255 @@ +syntax = "proto3"; +package protobuf; +option go_package = "runner/protobuf"; + +import "google/protobuf/timestamp.proto"; + +service ToRunner { + // Requests a stream from a lecture hall + rpc RequestStream (StreamRequest) returns (StreamResponse) {} + rpc RequestStreamEnd (StreamEndRequest) returns (StreamEndResponse) {} + rpc GenerateLivePreview (LivePreviewRequest) returns (LivePreviewResponse){} + rpc GenerateSectionImages (GenerateSectionImageRequest) returns (Status) {} + rpc DeleteSectionImage (DeleteSectionImageRequest) returns (Status) {} + rpc RequestTranscoding(TranscodingRequest) returns (TranscodingResponse) {} + rpc RequestThumbnail(ThumbnailRequest) returns (ThumbnailResponse) {} + rpc RequestUpload(UploadRequest) returns (UploadResponse) {} +} + +message UploadRequest{ + string ActionID = 1; + string RunnerID = 2; + string HLSUrl = 3; + string SourceType = 4; + string CourseName = 5; + uint32 CourseYear = 6; + string CourseTeachingTerm = 7; + string ThumbnailUrl = 8; +} + +message UploadResponse{ + string ActionID = 1; + string UploadKey = 2; +} + +message ThumbnailRequest{ + string ActionID = 1; + string RunnerID = 2; + string HLSUrl = 3; + string SourceType = 4; + string CourseName = 5; + uint32 CourseYear = 6; + string CourseTeachingTerm = 7; +} + +message ThumbnailResponse{ + string ActionID = 1; + string ThumbnailUrl = 2; +} + +message TranscodingRequest{ + string ActionID = 1; + string RunnerID = 2; + string SourceType = 3; + string CourseName = 4; + string DataURL = 5; + string streamName = 6; +} + +message TranscodingResponse{ + string ActionID = 1; + string TranscodingID = 2; + +} + +message LivePreviewRequest{ + string ActionID = 1; + string RunnerID = 2; + string HLSUrl = 3; +} + +message LivePreviewResponse{ + string ActionID = 1; + bytes LiveThumb = 2; +} + +message DeleteSectionImageRequest { + string ActionID = 1; + string Path = 2; +} + +message GenerateSectionImageResponse { + string ActionID = 1; + repeated string Paths = 2; +} + +message Section { + uint32 Hours = 1; + uint32 Minutes = 2; + uint32 Seconds = 3; +} + +message GenerateSectionImageRequest { + string ActionID = 1; + string PlaylistURL = 2; + string CourseName = 3; + uint32 CourseYear = 4; + string CourseTeachingTerm = 5; + repeated Section Sections = 6; +} + +message Status{ + bool ok = 1; +} + +message StreamRequest { + string ActionID = 1; + uint64 stream = 2; + uint64 course = 3; + string version = 4; + google.protobuf.Timestamp end = 5; + string source = 6; +} + + +message StreamResponse { + string ActionID = 1; +} + +message StreamEndRequest { + string ActionID = 1; + bool keepVod = 2; +} + +message StreamEndResponse {} + +// FromRunner service defines communication from runners to gocast +service FromRunner { + // Register is a request to the server to join the runners pool. + rpc Register (RegisterRequest) returns (RegisterResponse) {} + rpc Heartbeat (HeartbeatRequest) returns (HeartbeatResponse) {} + rpc RequestSelfStream (SelfStreamRequest) returns (SelfStreamResponse) {} + rpc NotifyVoDUploadFinished (VoDUploadFinished) returns (Status) {} + rpc NotifySilenceResults (SilenceResults) returns (Status) {} + rpc NotifyStreamStarted (StreamStarted) returns (Status) {} + rpc NotifyStreamEnded (StreamEnded) returns (Status) {} + rpc NotifyThumbnailsFinished (ThumbnailsFinished) returns (Status) {} + rpc NotifyTranscodingFailure (TranscodingFailureNotification) returns (Status) {} + rpc GetStreamInfoForUpload (StreamInfoForUploadRequest) returns (StreamInfoForUploadResponse) {} + rpc NotifyActionFinished (ActionFinished) returns (Status) {} +} + +message ActionFinished{ + string RunnerID = 1; + string ActionID = 2; + string type = 3; +} + +message StreamInfoForUploadRequest{ + string RunnerID = 1; + uint32 UploadKey = 2; +} + +message StreamInfoForUploadResponse{ + string CourseSlug = 1; + string CourseTerm = 2; + uint32 CourseYear = 3; + google.protobuf.Timestamp StreamStart = 4; + google.protobuf.Timestamp StreamEnd = 5; + uint32 StreamID = 6; + string VideoType = 7; +} + +message VoDUploadFinished{ + string RunnerID = 1; + uint32 StreamID = 2; + string HLSUrl = 3; + string SourceType = 4; + string ThumbnailUrl = 5; +} + +message SilenceResults{ + string RunnerID = 1; + uint32 StreamID = 2; + repeated uint32 starts = 3 [packed = true]; + repeated uint32 ends = 4 [packed = true]; +} + +message StreamStarted { + string Hostname = 1; + uint32 StreamID = 2; + uint32 CourseID = 3; + string Version = 4; + string HLSUrl = 5; + string SourceType = 6; +} + +message StreamEnded{ + string RunnerID = 1; + uint32 StreamID = 2; + uint32 CourseID = 3; + string Version = 4; +} + +message ThumbnailsFinished{ + string RunnerID = 1; + uint32 StreamID = 2; + string FilePath = 3; + uint32 Interval = 4; + string SourceType = 5; + + string LargeThumbnailPath = 6; +} + +message TranscodingFailureNotification{ + string RunnerID = 1; + uint32 StreamID = 2; + string Version = 3; + string FilePath = 4; + string Logs = 5; + int64 ExitCode = 6; +} + +message RegisterRequest { + string hostname = 1; + int32 port = 2; +} + +message RegisterResponse { + string ID = 1; +} + +message HeartbeatRequest { + string hostname = 1; + int32 port = 2; + google.protobuf.Timestamp LastSeen = 3; + string Status = 4; + uint32 Workload = 5; + string CPU = 6; + string Memory = 7; + string Disk = 8; + string Uptime = 9; + string Version = 10; + string CurrentAction = 11; +} + +message HeartbeatResponse { + bool ok = 1; +} + +message SelfStreamRequest { + string streamKey = 1; +} + +message SelfStreamResponse { + uint64 stream = 1; + uint64 course = 2; + uint64 courseYear = 3; + google.protobuf.Timestamp streamStart = 4; + google.protobuf.Timestamp streamEnd = 5; + bool uploadVoD = 6; + string ingestServer = 7; + string streamName = 8; + string outURL = 9; +} + diff --git a/runner/vmstat/vmstat.go b/runner/vmstat/vmstat.go new file mode 100644 index 000000000..c683e3f57 --- /dev/null +++ b/runner/vmstat/vmstat.go @@ -0,0 +1,101 @@ +package vmstat + +import ( + "context" + "fmt" + "github.com/icza/gox/fmtx" + "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v3/disk" + "github.com/shirou/gopsutil/v3/mem" + "golang.org/x/sync/errgroup" + "time" +) + +type VmStat struct { + diskPath string + + CpuPercent float64 + + MemTotal uint64 + MemAvailable uint64 + + DiskTotal uint64 + DiskPercent float64 + DiskUsed uint64 +} + +var ( + ErrCPUInvalid = fmt.Errorf("len (cpu.Percent) != 1") +) + +func New() *VmStat { + return &VmStat{diskPath: "/"} +} + +func NewWithPath(path string) *VmStat { + return &VmStat{diskPath: path} +} + +func (s *VmStat) Update() error { + g, _ := errgroup.WithContext(context.Background()) + g.Go(s.getCpu) + g.Go(s.getMem) + g.Go(s.getDisk) + return g.Wait() +} + +func (s *VmStat) getMem() error { + memory, err := mem.VirtualMemory() + if err != nil { + return err + } + s.MemAvailable = memory.Available + s.MemTotal = memory.Total + return nil +} + +func (s *VmStat) getCpu() error { + percent, err := cpu.Percent(time.Second*5, false) + if err != nil { + return err + } + if len(percent) != 1 { + return ErrCPUInvalid + } + s.CpuPercent = percent[0] + return nil +} + +func (s *VmStat) getDisk() error { + usage, err := disk.Usage("/") + if err != nil { + return err + } + s.DiskUsed = usage.Used + s.DiskTotal = usage.Total + s.DiskPercent = usage.UsedPercent + return nil +} + +func (s *VmStat) GetCpuStr() string { + return fmt.Sprintf("%.f%%", s.CpuPercent) +} + +func (s *VmStat) GetMemStr() string { + if s.MemAvailable == 0 || s.MemTotal == 0 { + return "unknown" + } + return fmt.Sprintf("%sM/%sM (%.f%%)", + fmtx.FormatInt(int64(s.MemAvailable/1e+6), 3, '.'), + fmtx.FormatInt(int64(s.MemTotal/1e+6), 3, '.'), + (1-(float64(s.MemAvailable)/float64(s.MemTotal)))*100) +} + +func (s *VmStat) GetDiskStr() string { + return fmt.Sprintf("%dG/%dG (%.f%%)", s.DiskUsed/1e+9, s.DiskTotal/1e+9, s.DiskPercent) +} + +func (s *VmStat) String() string { + return fmt.Sprintf("CPU: [%s], Mem: [%s], Disk: [%s]", + s.GetCpuStr(), s.GetMemStr(), s.GetDiskStr()) +} diff --git a/tools/config.go b/tools/config.go index d439f32d4..9fa652c2e 100644 --- a/tools/config.go +++ b/tools/config.go @@ -51,7 +51,12 @@ func initConfig() { if err != nil { panic(fmt.Errorf("fatal error config file: %v", err)) } - + if Cfg.Edge.Domain == "" { + logger.Error("No domain for edge found, can't proxy streams") + } + if Cfg.Edge.Port == 0 { + logger.Error("No port for edge found, can't proxy streams") + } // set defaults if Cfg.WorkerToken == "" { Cfg.WorkerToken = uuid.NewV4().String() @@ -113,6 +118,10 @@ type Config struct { Host string `yaml:"host"` Port uint `yaml:"port"` } `yaml:"db"` + Edge struct { + Domain string `yaml:"domain"` + Port int `yaml:"port"` + } `yaml:"edge"` Campus struct { Base string `yaml:"base"` Tokens []string `yaml:"tokens"` diff --git a/web/admin.go b/web/admin.go index c6bddef7a..aa3290af3 100644 --- a/web/admin.go +++ b/web/admin.go @@ -5,9 +5,6 @@ import ( "encoding/json" "errors" "fmt" - "net/http" - "regexp" - "github.com/TUM-Dev/gocast/dao" "github.com/TUM-Dev/gocast/model" "github.com/TUM-Dev/gocast/tools" @@ -15,6 +12,8 @@ import ( "github.com/getsentry/sentry-go" "github.com/gin-gonic/gin" "gorm.io/gorm" + "net/http" + "regexp" ) // AdminPage serves all administration pages. todo: refactor into multiple methods @@ -41,6 +40,10 @@ func (r mainRoutes) AdminPage(c *gin.Context) { if err != nil { sentry.CaptureException(err) } + runners, err := r.RunnerDao.GetAll(context.Background()) + if err != nil { + sentry.CaptureException(err) + } lectureHalls := r.LectureHallsDao.GetAllLectureHalls() indexData := NewIndexData() indexData.TUMLiveContext = tumLiveContext @@ -104,6 +107,7 @@ func (r mainRoutes) AdminPage(c *gin.Context) { InfoPages: infopages, ServerNotifications: serverNotifications, Notifications: notifications, + Runners: RunnersData{Runners: runners}, }) if err != nil { logger.Error("Error executing template admin.gohtml", "err", err) @@ -122,6 +126,8 @@ func GetPageString(s string) string { return "createLectureHalls" case "/admin/workers": return "workers" + case "/admin/runners": + return "runners" case "/admin/create-course": return "createCourse" case "/admin/course-import": @@ -150,6 +156,10 @@ type WorkersData struct { Token string } +type RunnersData struct { + Runners []model.Runner +} + type TokensData struct { Tokens []dao.AllTokensDto RtmpProxyURL string @@ -341,6 +351,7 @@ type AdminPageData struct { Tokens TokensData InfoPages []model.InfoPage Notifications []model.Notification + Runners RunnersData } func (apd AdminPageData) UsersAsJson() string { diff --git a/web/assets/init-admin.js b/web/assets/init-admin.js index f4e8be806..3230042c6 100644 --- a/web/assets/init-admin.js +++ b/web/assets/init-admin.js @@ -93,7 +93,7 @@ document.addEventListener("alpine:init", () => { if (!data[fieldName]) { el.value = ""; } - el.dispatchEvent(new CustomEvent(nativeEventName, { detail: { changeSet, value: data[fieldName] } })); + el.dispatchEvent(new CustomEvent(nativeEventName, {detail: {changeSet, value: data[fieldName]}})); }; changeSet.listen(onChangeSetUpdateHandler); @@ -110,7 +110,7 @@ document.addEventListener("alpine:init", () => { const onChangeSetUpdateHandler = (data) => { el.checked = !!data[fieldName]; - el.dispatchEvent(new CustomEvent(nativeEventName, { detail: { changeSet, value: !!data[fieldName] }})); + el.dispatchEvent(new CustomEvent(nativeEventName, {detail: {changeSet, value: !!data[fieldName]}})); }; changeSet.listen(onChangeSetUpdateHandler); @@ -127,7 +127,7 @@ document.addEventListener("alpine:init", () => { const onChangeSetUpdateHandler = (data) => { el.value = `${data[fieldName]}`; - el.dispatchEvent(new CustomEvent(nativeEventName, { detail: { changeSet, value: data[fieldName] } })); + el.dispatchEvent(new CustomEvent(nativeEventName, {detail: {changeSet, value: data[fieldName]}})); }; changeSet.listen(onChangeSetUpdateHandler); @@ -145,7 +145,7 @@ document.addEventListener("alpine:init", () => { const onChangeSetUpdateHandler = (data) => { el.value = `${data[fieldName]}`; - el.dispatchEvent(new CustomEvent(nativeEventName, { detail: { changeSet, value: data[fieldName] } })); + el.dispatchEvent(new CustomEvent(nativeEventName, {detail: {changeSet, value: data[fieldName]}})); }; changeSet.listen(onChangeSetUpdateHandler); @@ -177,7 +177,7 @@ document.addEventListener("alpine:init", () => { * - "csupdate": Custom event triggered when the change set is updated. * The detail property of the event object contains the new value of the specified field. */ - Alpine.directive("change-set-listen", (el, { expression, modifiers }, { effect, evaluate, cleanup }) => { + Alpine.directive("change-set-listen", (el, {expression, modifiers}, {effect, evaluate, cleanup}) => { effect(() => { const [changeSetExpression, fieldName = null] = expression.split("."); const changeSet = evaluate(changeSetExpression); @@ -187,7 +187,7 @@ document.addEventListener("alpine:init", () => { if (modifiers.includes("text")) { el.innerText = `${value}`; } - el.dispatchEvent(new CustomEvent(nativeEventName, { detail: { changeSet, value } })); + el.dispatchEvent(new CustomEvent(nativeEventName, {detail: {changeSet, value}})); }; if (!changeSet) { @@ -205,26 +205,26 @@ document.addEventListener("alpine:init", () => { }); /** - * Alpine.js directive for executing custom logic in response to the "csupdate" event, - * which is usually triggered by changes in a "change set" object's field. - * - * Syntax: - *
- * - * Parameters: - * - expression: The JavaScript expression to be evaluated when the "csupdate" event is triggered. - * - * Modifiers: - * - "init": When provided, the directive will execute the expression during initialization (no matter if its dirty or clean). - * - "clean": When provided, the directive will only execute if changeSet is not dirty. - * - "dirty": When provided, the directive will only execute if changeSet is dirty. - * - * Example usage: - *
- *
- */ - Alpine.directive("on-change-set-update", (el, { expression, modifiers }, { evaluate, evaluateLater, cleanup }) => { + * Alpine.js directive for executing custom logic in response to the "csupdate" event, + * which is usually triggered by changes in a "change set" object's field. + * + * Syntax: + *
+ * + * Parameters: + * - expression: The JavaScript expression to be evaluated when the "csupdate" event is triggered. + * + * Modifiers: + * - "init": When provided, the directive will execute the expression during initialization (no matter if its dirty or clean). + * - "clean": When provided, the directive will only execute if changeSet is not dirty. + * - "dirty": When provided, the directive will only execute if changeSet is dirty. + * + * Example usage: + *
+ *
+ */ + Alpine.directive("on-change-set-update", (el, {expression, modifiers}, {evaluate, evaluateLater, cleanup}) => { const onUpdate = evaluateLater(expression); const onChangeSetUpdateHandler = (e) => { diff --git a/web/index.go b/web/index.go index ebbc9fa80..0bfd38a97 100644 --- a/web/index.go +++ b/web/index.go @@ -21,7 +21,7 @@ import ( var VersionTag string func (r mainRoutes) MainPage(c *gin.Context) { - tName := sentry.TransactionName("GET /") + tName := sentry.WithTransactionName("GET /") spanMain := sentry.StartSpan(c.Request.Context(), "MainPageHandler", tName) defer spanMain.Finish() diff --git a/web/router.go b/web/router.go index f4a34c23a..7f87186a7 100755 --- a/web/router.go +++ b/web/router.go @@ -125,6 +125,7 @@ func configMainRoute(router *gin.Engine) { adminGroup.GET("/admin/notifications", routes.AdminPage) adminGroup.GET("/admin/audits", routes.AdminPage) adminGroup.GET("/admin/maintenance", routes.AdminPage) + adminGroup.GET("/admin/runners", routes.AdminPage) courseAdminGroup := router.Group("/") courseAdminGroup.Use(tools.InitCourse(daoWrapper)) diff --git a/web/template/admin/admin.gohtml b/web/template/admin/admin.gohtml index 8019bf8f5..b9941b4c4 100755 --- a/web/template/admin/admin.gohtml +++ b/web/template/admin/admin.gohtml @@ -44,6 +44,13 @@ href="/admin/workers">Workers +
  • + + + Runners + +
  • Workers
  • +
  • + + + Runners + +
  • + x-init="$watch('year',(value)=>{ yearW = (value<2000) ? '' : (value%1000) + 1 })">

    Find your course from TUMOnline

    @@ -11,7 +11,8 @@
    Runners + + + + + + +
    + +
    +

    Runners

    +
    + + + + + + + + + + + + {{- /*gotype: github.com/TUM-Dev/gocast/web.RunnersData*/ -}} + {{range $runner := .Runners}} + + + + + + + + + {{end}} + +
    NameStatusUptimeActionsFunctions
    +
    {{$runner.Hostname}}@{{$runner.Version}} +
    +
    + CPU: {{$runner.CPU}} + Mem: {{$runner.Memory}} + Disk: {{$runner.Disk}} +
    +
    {{if $runner.IsAlive}} + Alive{{else}} + Dead{{end}} + {{$runner.Uptime}} + + + + + +
    +
    +
    +
    +
    + + + + + + + + + + + + +
    IDStatusStartTypeDuration
    +
    +
    +
    diff --git a/web/template/partial/course/manage/create-lecture-form.gohtml b/web/template/partial/course/manage/create-lecture-form.gohtml index 810532ce6..447f88c51 100644 --- a/web/template/partial/course/manage/create-lecture-form.gohtml +++ b/web/template/partial/course/manage/create-lecture-form.gohtml @@ -69,14 +69,16 @@
    - +
    -