From a0cf979c601b2398660afa5c78add681c9ef33ed Mon Sep 17 00:00:00 2001 From: Anbraten Date: Fri, 2 Jun 2023 07:31:13 +0200 Subject: [PATCH 01/37] fix logging --- agent/logger.go | 3 +-- agent/runner.go | 8 -------- server/logging/log.go | 1 + 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/agent/logger.go b/agent/logger.go index 8a2745dbb12..1311e4e2f4b 100644 --- a/agent/logger.go +++ b/agent/logger.go @@ -50,9 +50,8 @@ func (r *Runner) createLogger(_ context.Context, logger zerolog.Logger, uploads loglogger.Debug().Msg("log stream opened") - limitedPart := io.LimitReader(part, maxLogsUpload) logStream := rpc.NewLineWriter(r.client, work.ID, step.Alias, secrets...) - if _, err := io.Copy(logStream, limitedPart); err != nil { + if _, err := io.Copy(logStream, part); err != nil { log.Error().Err(err).Msg("copy limited logStream part") } diff --git a/agent/runner.go b/agent/runner.go index 4ef75914601..b553f0aea69 100644 --- a/agent/runner.go +++ b/agent/runner.go @@ -31,14 +31,6 @@ import ( "github.com/woodpecker-ci/woodpecker/shared/utils" ) -// TODO: Implement log streaming. -// Until now we need to limit the size of the logs and files that we upload. -// The maximum grpc payload size is 4194304. So we need to set these limits below the maximum. -const ( - maxLogsUpload = 2000000 // this is per step - maxFileUpload = 1000000 -) - type Runner struct { client rpc.Peer filter rpc.Filter diff --git a/server/logging/log.go b/server/logging/log.go index f43d156cc38..3c4c1b3422f 100644 --- a/server/logging/log.go +++ b/server/logging/log.go @@ -72,6 +72,7 @@ func (l *log) Write(_ context.Context, path string, entry *Entry) error { for sub := range s.subs { go sub.handler(entry) } + // TODO: store entry to database as well s.Unlock() return nil } From c4ddf20e2a9f42c4c2a0f73ee48030db8bc53c32 Mon Sep 17 00:00:00 2001 From: Anbraten Date: Fri, 2 Jun 2023 07:45:30 +0200 Subject: [PATCH 02/37] store to db --- server/grpc/rpc.go | 12 +++++++++--- server/logging/log.go | 1 - server/store/datastore/log.go | 31 +++++++++++++++++++++++++++++++ server/store/mocks/store.go | 16 ++++++++++++++++ server/store/store.go | 2 ++ 5 files changed, 58 insertions(+), 4 deletions(-) diff --git a/server/grpc/rpc.go b/server/grpc/rpc.go index e7c0bd08253..0bf6fe9f3b7 100644 --- a/server/grpc/rpc.go +++ b/server/grpc/rpc.go @@ -308,13 +308,19 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error { } // Log implements the rpc.Log function -func (s *RPC) Log(c context.Context, id string, line *rpc.Line) error { +func (s *RPC) Log(c context.Context, _id string, line *rpc.Line) error { entry := new(logging.Entry) entry.Data, _ = json.Marshal(line) - if err := s.logger.Write(c, id, entry); err != nil { + if err := s.logger.Write(c, _id, entry); err != nil { log.Error().Err(err).Msgf("rpc server could not write to logger") } - return nil + + id, err := strconv.ParseInt(_id, 10, 64) + if err != nil { + return err + } + + return s.store.LogAppend(id, entry) } func (s *RPC) RegisterAgent(ctx context.Context, platform, backend, version string, capacity int32) (int64, error) { diff --git a/server/logging/log.go b/server/logging/log.go index 3c4c1b3422f..f43d156cc38 100644 --- a/server/logging/log.go +++ b/server/logging/log.go @@ -72,7 +72,6 @@ func (l *log) Write(_ context.Context, path string, entry *Entry) error { for sub := range s.subs { go sub.handler(entry) } - // TODO: store entry to database as well s.Unlock() return nil } diff --git a/server/store/datastore/log.go b/server/store/datastore/log.go index dbd60cfbc34..8ea3c61d2d6 100644 --- a/server/store/datastore/log.go +++ b/server/store/datastore/log.go @@ -18,6 +18,7 @@ import ( "bytes" "io" + "github.com/woodpecker-ci/woodpecker/server/logging" "github.com/woodpecker-ci/woodpecker/server/model" ) @@ -62,3 +63,33 @@ func (s storage) LogSave(step *model.Step, reader io.Reader) error { return sess.Commit() } + +func (s storage) LogAppend(stepID int64, entry *logging.Entry) error { + sess := s.engine.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + + logs := new(model.Logs) + exist, err := sess.Where("log_step_id = ?", stepID).Get(logs) + if err != nil { + return err + } + + if exist { + data := append(logs.Data, entry.Data...) + if _, err := sess.ID(logs.ID).Cols("log_data").Update(&model.Logs{Data: data}); err != nil { + return err + } + } else { + if _, err := sess.Insert(&model.Logs{ + StepID: stepID, + Data: entry.Data, + }); err != nil { + return err + } + } + + return sess.Commit() +} diff --git a/server/store/mocks/store.go b/server/store/mocks/store.go index effaea10707..66cf84cb023 100644 --- a/server/store/mocks/store.go +++ b/server/store/mocks/store.go @@ -6,6 +6,8 @@ import ( io "io" mock "github.com/stretchr/testify/mock" + logging "github.com/woodpecker-ci/woodpecker/server/logging" + model "github.com/woodpecker-ci/woodpecker/server/model" ) @@ -1089,6 +1091,20 @@ func (_m *Store) HasRedirectionForRepo(_a0 int64, _a1 string) (bool, error) { return r0, r1 } +// LogAppend provides a mock function with given fields: stepID, entry +func (_m *Store) LogAppend(stepID int64, entry *logging.Entry) error { + ret := _m.Called(stepID, entry) + + var r0 error + if rf, ok := ret.Get(0).(func(int64, *logging.Entry) error); ok { + r0 = rf(stepID, entry) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // LogFind provides a mock function with given fields: _a0 func (_m *Store) LogFind(_a0 *model.Step) (io.ReadCloser, error) { ret := _m.Called(_a0) diff --git a/server/store/store.go b/server/store/store.go index 9b646bdcc7a..c26c82b878c 100644 --- a/server/store/store.go +++ b/server/store/store.go @@ -20,6 +20,7 @@ package store import ( "io" + "github.com/woodpecker-ci/woodpecker/server/logging" "github.com/woodpecker-ci/woodpecker/server/model" ) @@ -149,6 +150,7 @@ type Store interface { // TODO: since we do ReadAll in any case a ioReader is not the best idea // so either find a way to write log in chunks by xorm ... LogSave(*model.Step, io.Reader) error + LogAppend(stepID int64, entry *logging.Entry) error // Tasks // TaskList TODO: paginate & opt filter From 7a30a539586a7fbb6e89857142cf22d0db337563 Mon Sep 17 00:00:00 2001 From: Anbraten Date: Sun, 4 Jun 2023 08:19:30 +0200 Subject: [PATCH 03/37] update model and add migration --- server/model/log.go | 2 + .../migration/018_alter_logs_table.go | 114 ++++++++++++++++++ server/store/datastore/migration/migration.go | 1 + 3 files changed, 117 insertions(+) create mode 100644 server/store/datastore/migration/018_alter_logs_table.go diff --git a/server/model/log.go b/server/model/log.go index 836c38b6094..6081381cd5b 100644 --- a/server/model/log.go +++ b/server/model/log.go @@ -17,6 +17,8 @@ package model type Logs struct { ID int64 `xorm:"pk autoincr 'log_id'"` StepID int64 `xorm:"UNIQUE 'log_step_id'"` + Time int64 `xorm:"'log_time'"` + Line int `xorm:"'log_line'"` Data []byte `xorm:"LONGBLOB 'log_data'"` // TODO: add create timestamp } diff --git a/server/store/datastore/migration/018_alter_logs_table.go b/server/store/datastore/migration/018_alter_logs_table.go new file mode 100644 index 00000000000..c553ff25758 --- /dev/null +++ b/server/store/datastore/migration/018_alter_logs_table.go @@ -0,0 +1,114 @@ +// Copyright 2023 Woodpecker Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package migration + +import ( + "encoding/json" + + "xorm.io/xorm" + + "github.com/woodpecker-ci/woodpecker/server/model" +) + +// old: +// - log_id +// - log_step_id +// - log_data + +// new: +// - log_id +// - log_step_id +// - log_time +// - log_pos +// - log_data (raw text) +// - log_command (can be added later when we support executing command by command) + +type oldLogs018 struct { + ID int64 `xorm:"pk autoincr 'log_id'"` + StepID int64 `xorm:"UNIQUE 'log_step_id'"` + Data []byte `xorm:"LONGBLOB 'log_data'"` +} + +type oldLogEntry018 struct { + Step string `json:"step,omitempty"` + Time int64 `json:"time,omitempty"` + Type int `json:"type,omitempty"` + Pos int `json:"pos,omitempty"` + Out string `json:"out,omitempty"` +} + +var alterLogsTable = task{ + name: "alter-logs-table", + fn: func(sess *xorm.Session) error { + // make sure old logs table exists + if err := sess.Sync(new(oldLogs018)); err != nil { + return err + } + + if err := renameTable(sess, "logs", "old_logs"); err != nil { + return err + } + + if err := sess.Sync(new(model.Logs)); err != nil { + return err + } + + // TODO: copy data over from old_logs to logs + page := 0 + for { + var logs []*oldLogs018 + err := sess.Limit(10, page*10).Find(&logs) + if err != nil { + return err + } + + for _, l := range logs { + + logEntries := []*oldLogEntry018{} + if err := json.Unmarshal(l.Data, &logEntries); err != nil { + return err + } + + time := int64(0) + for _, logEntry := range logEntries { + + if logEntry.Time > time { + time = logEntry.Time + } + + log := &model.Logs{ + ID: l.ID, + StepID: l.StepID, + Data: []byte(logEntry.Out), + Line: logEntry.Pos, + Time: time, + } + + if _, err := sess.Insert(log); err != nil { + return err + } + } + } + + if len(logs) < 10 { + break + } + + page++ + } + + return sess.DropTable("old_logs") + }, +} diff --git a/server/store/datastore/migration/migration.go b/server/store/datastore/migration/migration.go index 6d51179b083..c9c7c483794 100644 --- a/server/store/datastore/migration/migration.go +++ b/server/store/datastore/migration/migration.go @@ -46,6 +46,7 @@ var migrationTasks = []*task{ &removeInactiveRepos, &dropFiles, &removeMachineCol, + &alterLogsTable, } var allBeans = []interface{}{ From bba6e02498df55253e9b7e2227191c9f4f37d395 Mon Sep 17 00:00:00 2001 From: Anbraten Date: Sun, 4 Jun 2023 10:56:07 +0200 Subject: [PATCH 04/37] refactor log code --- agent/logger.go | 3 +- agent/rpc/client_grpc.go | 15 +- pipeline/rpc/{line.go => log_entry.go} | 67 ++-- .../rpc/{line_test.go => log_entry_test.go} | 12 +- pipeline/rpc/peer.go | 2 +- pipeline/rpc/proto/woodpecker.pb.go | 295 +++++++++--------- pipeline/rpc/proto/woodpecker.proto | 12 +- pipeline/rpc/proto/woodpecker_grpc.pb.go | 80 ++--- server/api/pipeline.go | 37 +-- server/api/stream.go | 7 +- server/grpc/rpc.go | 21 +- server/grpc/server.go | 12 +- server/logging/log.go | 10 +- server/logging/log_test.go | 9 +- server/logging/logging.go | 19 +- server/model/log.go | 15 +- server/store/datastore/log.go | 68 +--- server/store/datastore/log_test.go | 80 +++-- .../migration/018_alter_logs_table.go | 4 +- server/store/datastore/migration/migration.go | 2 +- server/store/datastore/repo_test.go | 2 +- server/store/datastore/step.go | 2 +- server/store/mocks/store.go | 30 +- server/store/store.go | 11 +- web/src/lib/api/types/pipeline.ts | 9 +- 25 files changed, 363 insertions(+), 461 deletions(-) rename pipeline/rpc/{line.go => log_entry.go} (62%) rename pipeline/rpc/{line_test.go => log_entry_test.go} (85%) diff --git a/agent/logger.go b/agent/logger.go index 1311e4e2f4b..c668d245822 100644 --- a/agent/logger.go +++ b/agent/logger.go @@ -50,7 +50,8 @@ func (r *Runner) createLogger(_ context.Context, logger zerolog.Logger, uploads loglogger.Debug().Msg("log stream opened") - logStream := rpc.NewLineWriter(r.client, work.ID, step.Alias, secrets...) + // TODO: use step-id instead of work-id + logStream := rpc.NewLineWriter(r.client, work.ID, secrets...) if _, err := io.Copy(logStream, part); err != nil { log.Error().Err(err).Msg("copy limited logStream part") } diff --git a/agent/rpc/client_grpc.go b/agent/rpc/client_grpc.go index 8c42518040e..5551269ae89 100644 --- a/agent/rpc/client_grpc.go +++ b/agent/rpc/client_grpc.go @@ -17,6 +17,7 @@ package rpc import ( "context" "encoding/json" + "strconv" "strings" "time" @@ -278,14 +279,14 @@ func (c *client) Update(ctx context.Context, id string, state rpc.State) (err er } // Log writes the pipeline log entry. -func (c *client) Log(ctx context.Context, id string, line *rpc.Line) (err error) { +func (c *client) Log(ctx context.Context, logEntry *rpc.LogEntry) (err error) { req := new(proto.LogRequest) - req.Id = id - req.Line = new(proto.Line) - req.Line.Out = line.Out - req.Line.Pos = int32(line.Pos) - req.Line.Step = line.Step - req.Line.Time = line.Time + req.Id = strconv.FormatInt(logEntry.StepID, 10) + req.LogEntry = new(proto.LogEntry) + req.LogEntry.StepId = logEntry.StepID + req.LogEntry.Data = logEntry.Data + req.LogEntry.Line = int32(logEntry.Line) + req.LogEntry.Time = logEntry.Time for { _, err = c.client.Log(ctx, req) if err == nil { diff --git a/pipeline/rpc/line.go b/pipeline/rpc/log_entry.go similarity index 62% rename from pipeline/rpc/line.go rename to pipeline/rpc/log_entry.go index f30c20272c8..5b4d8bfe492 100644 --- a/pipeline/rpc/line.go +++ b/pipeline/rpc/log_entry.go @@ -36,62 +36,59 @@ const ( ) // Line is a line of console output. -type Line struct { - Step string `json:"step,omitempty"` - Time int64 `json:"time,omitempty"` - Type int `json:"type,omitempty"` - Pos int `json:"pos,omitempty"` - Out string `json:"out,omitempty"` +type LogEntry struct { + StepID int64 `json:"step_id,omitempty"` + Time int64 `json:"time,omitempty"` + Type int `json:"type,omitempty"` + Line int `json:"line,omitempty"` + Data string `json:"data,omitempty"` } -func (l *Line) String() string { +func (l *LogEntry) String() string { switch l.Type { case LineExitCode: - return fmt.Sprintf("[%s] exit code %s", l.Step, l.Out) + return fmt.Sprintf("[%d] exit code %s", l.StepID, l.Data) default: - return fmt.Sprintf("[%s:L%v:%vs] %s", l.Step, l.Pos, l.Time, l.Out) + return fmt.Sprintf("[%d:L%v:%vs] %s", l.StepID, l.Line, l.Time, l.Data) } } // LineWriter sends logs to the client. type LineWriter struct { - peer Peer - id string - name string - num int - now time.Time - rep *strings.Replacer - lines []*Line + peer Peer + stepID int64 + num int + now time.Time + rep *strings.Replacer + lines []*LogEntry } // NewLineWriter returns a new line reader. -func NewLineWriter(peer Peer, id, name string, secret ...string) *LineWriter { +func NewLineWriter(peer Peer, stepID int64, secret ...string) *LineWriter { return &LineWriter{ - peer: peer, - id: id, - name: name, - now: time.Now().UTC(), - rep: shared.NewSecretsReplacer(secret), - lines: nil, + peer: peer, + stepID: stepID, + now: time.Now().UTC(), + rep: shared.NewSecretsReplacer(secret), + lines: nil, } } func (w *LineWriter) Write(p []byte) (n int, err error) { - out := string(p) + data := string(p) if w.rep != nil { - out = w.rep.Replace(out) + data = w.rep.Replace(data) } - log.Trace().Str("name", w.name).Str("ID", w.id).Msgf("grpc write line: %s", out) + log.Trace().Int64("step-id", w.stepID).Msgf("grpc write line: %s", data) - line := &Line{ - Out: out, - Step: w.name, - Pos: w.num, - Time: int64(time.Since(w.now).Seconds()), - Type: LineStdout, + line := &LogEntry{ + Data: data, + StepID: w.stepID, + Time: int64(time.Since(w.now).Seconds()), + Type: LineStdout, } - if err := w.peer.Log(context.Background(), w.id, line); err != nil { - log.Error().Err(err).Msgf("fail to write pipeline log to peer '%s'", w.id) + if err := w.peer.Log(context.Background(), line); err != nil { + log.Error().Err(err).Msgf("fail to write pipeline log to peer '%d'", w.stepID) } w.num++ @@ -111,7 +108,7 @@ func (w *LineWriter) Write(p []byte) (n int, err error) { } // Lines returns the line history -func (w *LineWriter) Lines() []*Line { +func (w *LineWriter) Lines() []*LogEntry { return w.lines } diff --git a/pipeline/rpc/line_test.go b/pipeline/rpc/log_entry_test.go similarity index 85% rename from pipeline/rpc/line_test.go rename to pipeline/rpc/log_entry_test.go index 1469b5d9c0d..6fd1bc0cedc 100644 --- a/pipeline/rpc/line_test.go +++ b/pipeline/rpc/log_entry_test.go @@ -18,12 +18,12 @@ import ( "testing" ) -func TestLine(t *testing.T) { - line := Line{ - Step: "redis", - Time: 60, - Pos: 1, - Out: "starting redis server", +func TestLogEntry(t *testing.T) { + line := LogEntry{ + StepID: int64(123), + Time: 60, + Line: 1, + Data: "starting redis server", } got, want := line.String(), "[redis:L1:60s] starting redis server" if got != want { diff --git a/pipeline/rpc/peer.go b/pipeline/rpc/peer.go index 7c37d956588..69fea8d67d2 100644 --- a/pipeline/rpc/peer.go +++ b/pipeline/rpc/peer.go @@ -74,7 +74,7 @@ type Peer interface { Update(c context.Context, id string, state State) error // Log writes the pipeline log entry. - Log(c context.Context, id string, line *Line) error + Log(c context.Context, logEntry *LogEntry) error // RegisterAgent register our agent to the server RegisterAgent(ctx context.Context, platform, backend, version string, capacity int) (int64, error) diff --git a/pipeline/rpc/proto/woodpecker.pb.go b/pipeline/rpc/proto/woodpecker.pb.go index 3af0d865865..193784fdfb5 100644 --- a/pipeline/rpc/proto/woodpecker.pb.go +++ b/pipeline/rpc/proto/woodpecker.pb.go @@ -15,8 +15,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 -// protoc v3.21.12 +// protoc-gen-go v1.28.0 +// protoc v3.12.4 // source: woodpecker.proto package proto @@ -122,19 +122,19 @@ func (x *State) GetError() string { return "" } -type Line struct { +type LogEntry struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Step string `protobuf:"bytes,1,opt,name=step,proto3" json:"step,omitempty"` - Time int64 `protobuf:"varint,2,opt,name=time,proto3" json:"time,omitempty"` - Pos int32 `protobuf:"varint,3,opt,name=pos,proto3" json:"pos,omitempty"` - Out string `protobuf:"bytes,4,opt,name=out,proto3" json:"out,omitempty"` + StepId int64 `protobuf:"varint,1,opt,name=step_id,json=stepId,proto3" json:"step_id,omitempty"` + Time int64 `protobuf:"varint,2,opt,name=time,proto3" json:"time,omitempty"` + Line int32 `protobuf:"varint,3,opt,name=line,proto3" json:"line,omitempty"` + Data string `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` } -func (x *Line) Reset() { - *x = Line{} +func (x *LogEntry) Reset() { + *x = LogEntry{} if protoimpl.UnsafeEnabled { mi := &file_woodpecker_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -142,13 +142,13 @@ func (x *Line) Reset() { } } -func (x *Line) String() string { +func (x *LogEntry) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Line) ProtoMessage() {} +func (*LogEntry) ProtoMessage() {} -func (x *Line) ProtoReflect() protoreflect.Message { +func (x *LogEntry) ProtoReflect() protoreflect.Message { mi := &file_woodpecker_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -160,35 +160,35 @@ func (x *Line) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Line.ProtoReflect.Descriptor instead. -func (*Line) Descriptor() ([]byte, []int) { +// Deprecated: Use LogEntry.ProtoReflect.Descriptor instead. +func (*LogEntry) Descriptor() ([]byte, []int) { return file_woodpecker_proto_rawDescGZIP(), []int{1} } -func (x *Line) GetStep() string { +func (x *LogEntry) GetStepId() int64 { if x != nil { - return x.Step + return x.StepId } - return "" + return 0 } -func (x *Line) GetTime() int64 { +func (x *LogEntry) GetTime() int64 { if x != nil { return x.Time } return 0 } -func (x *Line) GetPos() int32 { +func (x *LogEntry) GetLine() int32 { if x != nil { - return x.Pos + return x.Line } return 0 } -func (x *Line) GetOut() string { +func (x *LogEntry) GetData() string { if x != nil { - return x.Out + return x.Data } return "" } @@ -614,8 +614,8 @@ type LogRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Line *Line `protobuf:"bytes,2,opt,name=line,proto3" json:"line,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + LogEntry *LogEntry `protobuf:"bytes,2,opt,name=logEntry,proto3" json:"logEntry,omitempty"` } func (x *LogRequest) Reset() { @@ -657,9 +657,9 @@ func (x *LogRequest) GetId() string { return "" } -func (x *LogRequest) GetLine() *Line { +func (x *LogRequest) GetLogEntry() *LogEntry { if x != nil { - return x.Line + return x.LogEntry } return nil } @@ -1101,126 +1101,127 @@ var file_woodpecker_proto_rawDesc = []byte{ 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x52, 0x0a, 0x04, 0x4c, 0x69, 0x6e, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x73, 0x74, 0x65, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x73, 0x74, 0x65, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x6f, 0x73, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x6f, 0x73, 0x12, 0x10, 0x0a, 0x03, 0x6f, 0x75, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6f, 0x75, 0x74, 0x22, 0x76, 0x0a, 0x06, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, - 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4e, 0x0a, 0x08, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x34, 0x0a, 0x0b, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x41, 0x0a, 0x0b, 0x49, 0x6e, + 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x5f, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x74, 0x65, 0x70, 0x49, 0x64, 0x12, 0x12, 0x0a, + 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x76, 0x0a, 0x06, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, + 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, + 0x01, 0x22, 0x4e, 0x0a, 0x08, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, + 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, + 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x22, 0x34, 0x0a, 0x0b, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x25, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, + 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x41, 0x0a, 0x0b, 0x49, 0x6e, 0x69, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x1d, 0x0a, 0x0b, 0x57, 0x61, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x1d, 0x0a, - 0x0b, 0x57, 0x61, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x41, 0x0a, 0x0b, - 0x44, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, - 0x1f, 0x0a, 0x0d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x41, 0x0a, 0x0b, 0x44, 0x6f, 0x6e, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x1f, 0x0a, 0x0d, + 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x43, 0x0a, + 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x22, 0x49, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x22, 0x43, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x22, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x3d, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x69, 0x6e, 0x65, 0x52, 0x04, - 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x2d, 0x0a, - 0x13, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x82, 0x01, 0x0a, - 0x14, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, - 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, - 0x6d, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, - 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 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, 0x22, 0x5b, 0x0a, 0x0f, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x67, 0x72, 0x70, 0x63, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, - 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3b, - 0x0a, 0x0c, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, - 0x0a, 0x08, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, - 0x65, 0x52, 0x08, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x32, 0x0a, 0x15, 0x52, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, - 0x49, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, - 0x0a, 0x0b, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, - 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x64, 0x0a, 0x0c, 0x41, 0x75, - 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, - 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x32, 0x8a, 0x04, 0x0a, 0x0a, 0x57, 0x6f, 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x12, - 0x31, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x31, 0x0a, 0x04, 0x4e, 0x65, 0x78, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x12, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, - 0x00, 0x12, 0x2a, 0x0a, 0x04, 0x57, 0x61, 0x69, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2a, 0x0a, - 0x04, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x6f, - 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x06, 0x45, 0x78, 0x74, - 0x65, 0x6e, 0x64, 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x78, 0x74, 0x65, - 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x06, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x28, 0x0a, 0x03, 0x4c, 0x6f, 0x67, - 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x0d, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, - 0x67, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x12, 0x3a, 0x0a, 0x0c, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, - 0x68, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x32, 0x43, 0x0a, - 0x0e, 0x57, 0x6f, 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x12, - 0x31, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x77, 0x6f, 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x2d, 0x63, 0x69, 0x2f, 0x77, - 0x6f, 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x2f, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, - 0x6e, 0x65, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x12, 0x2b, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x07, 0x0a, + 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x2d, 0x0a, 0x13, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, + 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, + 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x63, 0x61, + 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, + 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, 0x22, 0x5b, 0x0a, 0x0f, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, + 0x0c, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x0b, 0x67, 0x72, 0x70, 0x63, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3b, 0x0a, 0x0c, 0x4e, 0x65, 0x78, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x70, 0x69, 0x70, 0x65, 0x6c, + 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x08, 0x70, 0x69, 0x70, 0x65, + 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x32, 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x19, 0x0a, + 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x49, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x67, 0x65, 0x6e, 0x74, + 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x67, + 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, + 0x74, 0x49, 0x64, 0x22, 0x64, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x61, + 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, + 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0x8a, 0x04, 0x0a, 0x0a, 0x57, 0x6f, + 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, + 0x79, 0x1a, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x04, 0x4e, + 0x65, 0x78, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x65, 0x78, 0x74, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2a, + 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, + 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x04, 0x57, 0x61, + 0x69, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x04, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x12, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x06, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x12, 0x14, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x22, 0x00, 0x12, 0x28, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x0d, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, + 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0c, 0x52, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x32, 0x43, 0x0a, 0x0e, 0x57, 0x6f, 0x6f, 0x64, 0x70, 0x65, + 0x63, 0x6b, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x12, 0x31, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, + 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74, + 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x38, 0x5a, 0x36, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x77, 0x6f, 0x6f, 0x64, 0x70, 0x65, + 0x63, 0x6b, 0x65, 0x72, 0x2d, 0x63, 0x69, 0x2f, 0x77, 0x6f, 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, + 0x65, 0x72, 0x2f, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2f, 0x72, 0x70, 0x63, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1238,7 +1239,7 @@ func file_woodpecker_proto_rawDescGZIP() []byte { var file_woodpecker_proto_msgTypes = make([]protoimpl.MessageInfo, 20) var file_woodpecker_proto_goTypes = []interface{}{ (*State)(nil), // 0: proto.State - (*Line)(nil), // 1: proto.Line + (*LogEntry)(nil), // 1: proto.LogEntry (*Filter)(nil), // 2: proto.Filter (*Pipeline)(nil), // 3: proto.Pipeline (*NextRequest)(nil), // 4: proto.NextRequest @@ -1264,7 +1265,7 @@ var file_woodpecker_proto_depIdxs = []int32{ 0, // 2: proto.InitRequest.state:type_name -> proto.State 0, // 3: proto.DoneRequest.state:type_name -> proto.State 0, // 4: proto.UpdateRequest.state:type_name -> proto.State - 1, // 5: proto.LogRequest.line:type_name -> proto.Line + 1, // 5: proto.LogRequest.logEntry:type_name -> proto.LogEntry 3, // 6: proto.NextResponse.pipeline:type_name -> proto.Pipeline 11, // 7: proto.Woodpecker.Version:input_type -> proto.Empty 4, // 8: proto.Woodpecker.Next:input_type -> proto.NextRequest @@ -1314,7 +1315,7 @@ func file_woodpecker_proto_init() { } } file_woodpecker_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Line); i { + switch v := v.(*LogEntry); i { case 0: return &v.state case 1: diff --git a/pipeline/rpc/proto/woodpecker.proto b/pipeline/rpc/proto/woodpecker.proto index 0a14918df77..9c888231b8e 100644 --- a/pipeline/rpc/proto/woodpecker.proto +++ b/pipeline/rpc/proto/woodpecker.proto @@ -49,11 +49,11 @@ message State { string error = 6; } -message Line { - string step = 1; +message LogEntry { + int64 step_id = 1; int64 time = 2; - int32 pos = 3; - string out = 4; + int32 line = 3; + string data = 4; } message Filter { @@ -98,8 +98,8 @@ message UpdateRequest { } message LogRequest { - string id = 1; - Line line = 2; + string id = 1; + LogEntry logEntry = 2; } message Empty { diff --git a/pipeline/rpc/proto/woodpecker_grpc.pb.go b/pipeline/rpc/proto/woodpecker_grpc.pb.go index 44c2814fa61..3d2473519f0 100644 --- a/pipeline/rpc/proto/woodpecker_grpc.pb.go +++ b/pipeline/rpc/proto/woodpecker_grpc.pb.go @@ -1,22 +1,7 @@ -// Copyright 2021 Woodpecker Authors -// Copyright 2011 Drone.IO Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.3.0 -// - protoc v3.21.12 +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.12.4 // source: woodpecker.proto package proto @@ -33,19 +18,6 @@ import ( // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 -const ( - Woodpecker_Version_FullMethodName = "/proto.Woodpecker/Version" - Woodpecker_Next_FullMethodName = "/proto.Woodpecker/Next" - Woodpecker_Init_FullMethodName = "/proto.Woodpecker/Init" - Woodpecker_Wait_FullMethodName = "/proto.Woodpecker/Wait" - Woodpecker_Done_FullMethodName = "/proto.Woodpecker/Done" - Woodpecker_Extend_FullMethodName = "/proto.Woodpecker/Extend" - Woodpecker_Update_FullMethodName = "/proto.Woodpecker/Update" - Woodpecker_Log_FullMethodName = "/proto.Woodpecker/Log" - Woodpecker_RegisterAgent_FullMethodName = "/proto.Woodpecker/RegisterAgent" - Woodpecker_ReportHealth_FullMethodName = "/proto.Woodpecker/ReportHealth" -) - // WoodpeckerClient is the client API for Woodpecker 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. @@ -72,7 +44,7 @@ func NewWoodpeckerClient(cc grpc.ClientConnInterface) WoodpeckerClient { func (c *woodpeckerClient) Version(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VersionResponse, error) { out := new(VersionResponse) - err := c.cc.Invoke(ctx, Woodpecker_Version_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/proto.Woodpecker/Version", in, out, opts...) if err != nil { return nil, err } @@ -81,7 +53,7 @@ func (c *woodpeckerClient) Version(ctx context.Context, in *Empty, opts ...grpc. func (c *woodpeckerClient) Next(ctx context.Context, in *NextRequest, opts ...grpc.CallOption) (*NextResponse, error) { out := new(NextResponse) - err := c.cc.Invoke(ctx, Woodpecker_Next_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/proto.Woodpecker/Next", in, out, opts...) if err != nil { return nil, err } @@ -90,7 +62,7 @@ func (c *woodpeckerClient) Next(ctx context.Context, in *NextRequest, opts ...gr func (c *woodpeckerClient) Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := c.cc.Invoke(ctx, Woodpecker_Init_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/proto.Woodpecker/Init", in, out, opts...) if err != nil { return nil, err } @@ -99,7 +71,7 @@ func (c *woodpeckerClient) Init(ctx context.Context, in *InitRequest, opts ...gr func (c *woodpeckerClient) Wait(ctx context.Context, in *WaitRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := c.cc.Invoke(ctx, Woodpecker_Wait_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/proto.Woodpecker/Wait", in, out, opts...) if err != nil { return nil, err } @@ -108,7 +80,7 @@ func (c *woodpeckerClient) Wait(ctx context.Context, in *WaitRequest, opts ...gr func (c *woodpeckerClient) Done(ctx context.Context, in *DoneRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := c.cc.Invoke(ctx, Woodpecker_Done_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/proto.Woodpecker/Done", in, out, opts...) if err != nil { return nil, err } @@ -117,7 +89,7 @@ func (c *woodpeckerClient) Done(ctx context.Context, in *DoneRequest, opts ...gr func (c *woodpeckerClient) Extend(ctx context.Context, in *ExtendRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := c.cc.Invoke(ctx, Woodpecker_Extend_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/proto.Woodpecker/Extend", in, out, opts...) if err != nil { return nil, err } @@ -126,7 +98,7 @@ func (c *woodpeckerClient) Extend(ctx context.Context, in *ExtendRequest, opts . func (c *woodpeckerClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := c.cc.Invoke(ctx, Woodpecker_Update_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/proto.Woodpecker/Update", in, out, opts...) if err != nil { return nil, err } @@ -135,7 +107,7 @@ func (c *woodpeckerClient) Update(ctx context.Context, in *UpdateRequest, opts . func (c *woodpeckerClient) Log(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := c.cc.Invoke(ctx, Woodpecker_Log_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/proto.Woodpecker/Log", in, out, opts...) if err != nil { return nil, err } @@ -144,7 +116,7 @@ func (c *woodpeckerClient) Log(ctx context.Context, in *LogRequest, opts ...grpc func (c *woodpeckerClient) RegisterAgent(ctx context.Context, in *RegisterAgentRequest, opts ...grpc.CallOption) (*RegisterAgentResponse, error) { out := new(RegisterAgentResponse) - err := c.cc.Invoke(ctx, Woodpecker_RegisterAgent_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/proto.Woodpecker/RegisterAgent", in, out, opts...) if err != nil { return nil, err } @@ -153,7 +125,7 @@ func (c *woodpeckerClient) RegisterAgent(ctx context.Context, in *RegisterAgentR func (c *woodpeckerClient) ReportHealth(ctx context.Context, in *ReportHealthRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := c.cc.Invoke(ctx, Woodpecker_ReportHealth_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/proto.Woodpecker/ReportHealth", in, out, opts...) if err != nil { return nil, err } @@ -234,7 +206,7 @@ func _Woodpecker_Version_Handler(srv interface{}, ctx context.Context, dec func( } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Woodpecker_Version_FullMethodName, + FullMethod: "/proto.Woodpecker/Version", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Version(ctx, req.(*Empty)) @@ -252,7 +224,7 @@ func _Woodpecker_Next_Handler(srv interface{}, ctx context.Context, dec func(int } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Woodpecker_Next_FullMethodName, + FullMethod: "/proto.Woodpecker/Next", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Next(ctx, req.(*NextRequest)) @@ -270,7 +242,7 @@ func _Woodpecker_Init_Handler(srv interface{}, ctx context.Context, dec func(int } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Woodpecker_Init_FullMethodName, + FullMethod: "/proto.Woodpecker/Init", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Init(ctx, req.(*InitRequest)) @@ -288,7 +260,7 @@ func _Woodpecker_Wait_Handler(srv interface{}, ctx context.Context, dec func(int } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Woodpecker_Wait_FullMethodName, + FullMethod: "/proto.Woodpecker/Wait", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Wait(ctx, req.(*WaitRequest)) @@ -306,7 +278,7 @@ func _Woodpecker_Done_Handler(srv interface{}, ctx context.Context, dec func(int } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Woodpecker_Done_FullMethodName, + FullMethod: "/proto.Woodpecker/Done", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Done(ctx, req.(*DoneRequest)) @@ -324,7 +296,7 @@ func _Woodpecker_Extend_Handler(srv interface{}, ctx context.Context, dec func(i } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Woodpecker_Extend_FullMethodName, + FullMethod: "/proto.Woodpecker/Extend", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Extend(ctx, req.(*ExtendRequest)) @@ -342,7 +314,7 @@ func _Woodpecker_Update_Handler(srv interface{}, ctx context.Context, dec func(i } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Woodpecker_Update_FullMethodName, + FullMethod: "/proto.Woodpecker/Update", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Update(ctx, req.(*UpdateRequest)) @@ -360,7 +332,7 @@ func _Woodpecker_Log_Handler(srv interface{}, ctx context.Context, dec func(inte } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Woodpecker_Log_FullMethodName, + FullMethod: "/proto.Woodpecker/Log", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Log(ctx, req.(*LogRequest)) @@ -378,7 +350,7 @@ func _Woodpecker_RegisterAgent_Handler(srv interface{}, ctx context.Context, dec } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Woodpecker_RegisterAgent_FullMethodName, + FullMethod: "/proto.Woodpecker/RegisterAgent", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).RegisterAgent(ctx, req.(*RegisterAgentRequest)) @@ -396,7 +368,7 @@ func _Woodpecker_ReportHealth_Handler(srv interface{}, ctx context.Context, dec } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: Woodpecker_ReportHealth_FullMethodName, + FullMethod: "/proto.Woodpecker/ReportHealth", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).ReportHealth(ctx, req.(*ReportHealthRequest)) @@ -456,10 +428,6 @@ var Woodpecker_ServiceDesc = grpc.ServiceDesc{ Metadata: "woodpecker.proto", } -const ( - WoodpeckerAuth_Auth_FullMethodName = "/proto.WoodpeckerAuth/Auth" -) - // WoodpeckerAuthClient is the client API for WoodpeckerAuth 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. @@ -477,7 +445,7 @@ func NewWoodpeckerAuthClient(cc grpc.ClientConnInterface) WoodpeckerAuthClient { func (c *woodpeckerAuthClient) Auth(ctx context.Context, in *AuthRequest, opts ...grpc.CallOption) (*AuthResponse, error) { out := new(AuthResponse) - err := c.cc.Invoke(ctx, WoodpeckerAuth_Auth_FullMethodName, in, out, opts...) + err := c.cc.Invoke(ctx, "/proto.WoodpeckerAuth/Auth", in, out, opts...) if err != nil { return nil, err } @@ -522,7 +490,7 @@ func _WoodpeckerAuth_Auth_Handler(srv interface{}, ctx context.Context, dec func } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: WoodpeckerAuth_Auth_FullMethodName, + FullMethod: "/proto.WoodpeckerAuth/Auth", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerAuthServer).Auth(ctx, req.(*AuthRequest)) diff --git a/server/api/pipeline.go b/server/api/pipeline.go index bd39af5971c..bd68f0bfe71 100644 --- a/server/api/pipeline.go +++ b/server/api/pipeline.go @@ -19,17 +19,13 @@ package api import ( - "bytes" "encoding/json" "errors" - "fmt" - "io" "net/http" "strconv" "time" "github.com/gin-gonic/gin" - "github.com/rs/zerolog/log" "github.com/woodpecker-ci/woodpecker/server" "github.com/woodpecker-ci/woodpecker/server/model" @@ -172,24 +168,20 @@ func GetPipelineLogs(c *gin.Context) { return } + // TODO: get & use step id directly step, err := _store.StepChild(pl, ppid, name) if err != nil { handleDbGetError(c, err) return } - rc, err := _store.LogFind(step) + logs, err := _store.LogFind(step) if err != nil { handleDbGetError(c, err) return } - defer rc.Close() - - c.Header("Content-Type", "application/json") - if _, err := io.Copy(c.Writer, rc); err != nil { - log.Error().Err(err).Msg("could not copy log to http response") - } + c.JSON(http.StatusOK, logs) } func GetStepLogs(c *gin.Context) { @@ -213,18 +205,13 @@ func GetStepLogs(c *gin.Context) { return } - rc, err := _store.LogFind(step) + logs, err := _store.LogFind(step) if err != nil { handleDbGetError(c, err) return } - defer rc.Close() - - c.Header("Content-Type", "application/json") - if _, err := io.Copy(c.Writer, rc); err != nil { - log.Error().Err(err).Msg("could not copy log to http response") - } + c.JSON(http.StatusOK, logs) } func GetPipelineConfig(c *gin.Context) { @@ -400,7 +387,6 @@ func DeletePipelineLogs(c *gin.Context) { _store := store.FromContext(c) repo := session.Repo(c) - user := session.User(c) num, _ := strconv.ParseInt(c.Params.ByName("number"), 10, 64) pl, err := _store.GetPipelineNumber(repo, num) @@ -422,9 +408,8 @@ func DeletePipelineLogs(c *gin.Context) { } for _, step := range steps { - t := time.Now().UTC() - buf := bytes.NewBufferString(fmt.Sprintf(deleteStr, step.Name, user.Login, t.Format(time.UnixDate))) - lerr := _store.LogSave(step, buf) + logs := make([]*model.LogEntry, 0) + lerr := _store.LogSave(step, logs) if lerr != nil { err = lerr } @@ -436,11 +421,3 @@ func DeletePipelineLogs(c *gin.Context) { c.String(http.StatusNoContent, "") } - -var deleteStr = `[ - { - "step": %q, - "pos": 0, - "out": "logs purged by %s on %s\n" - } -]` diff --git a/server/api/stream.go b/server/api/stream.go index d965f52a582..75053b6697a 100644 --- a/server/api/stream.go +++ b/server/api/stream.go @@ -27,7 +27,6 @@ import ( "github.com/rs/zerolog/log" "github.com/woodpecker-ci/woodpecker/server" - "github.com/woodpecker-ci/woodpecker/server/logging" "github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/pubsub" "github.com/woodpecker-ci/woodpecker/server/router/middleware/session" @@ -141,8 +140,8 @@ func LogStreamSSE(c *gin.Context) { repo := session.Repo(c) _store := store.FromContext(c) - // // parse the pipeline number and step sequence number from - // // the request parameter. + // parse the pipeline number and step sequence number from + // the request parameter. pipelinen, _ := strconv.ParseInt(c.Param("pipeline"), 10, 64) stepn, _ := strconv.Atoi(c.Param("number")) @@ -179,7 +178,7 @@ func LogStreamSSE(c *gin.Context) { go func() { // TODO remove global variable - err := server.Config.Services.Logs.Tail(ctx, fmt.Sprint(step.ID), func(entries ...*logging.Entry) { + err := server.Config.Services.Logs.Tail(ctx, fmt.Sprint(step.ID), func(entries ...*model.LogEntry) { defer func() { obj := recover() // fix #2480 // TODO: check if it's still needed log.Trace().Msgf("pubsub subscribe recover return: %v", obj) diff --git a/server/grpc/rpc.go b/server/grpc/rpc.go index 0bf6fe9f3b7..b72028dee89 100644 --- a/server/grpc/rpc.go +++ b/server/grpc/rpc.go @@ -308,19 +308,14 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error { } // Log implements the rpc.Log function -func (s *RPC) Log(c context.Context, _id string, line *rpc.Line) error { - entry := new(logging.Entry) - entry.Data, _ = json.Marshal(line) - if err := s.logger.Write(c, _id, entry); err != nil { - log.Error().Err(err).Msgf("rpc server could not write to logger") - } - - id, err := strconv.ParseInt(_id, 10, 64) - if err != nil { - return err - } - - return s.store.LogAppend(id, entry) +func (s *RPC) Log(c context.Context, _logEntry *rpc.LogEntry) error { + logEntry := &model.LogEntry{ + StepID: _logEntry.StepID, + Time: _logEntry.Time, + Line: _logEntry.Line, + Data: []byte(_logEntry.Data), + } + return s.store.LogAppend(logEntry) } func (s *RPC) RegisterAgent(ctx context.Context, platform, backend, version string, capacity int32) (int64, error) { diff --git a/server/grpc/server.go b/server/grpc/server.go index f6f57fef207..83ba0819d91 100644 --- a/server/grpc/server.go +++ b/server/grpc/server.go @@ -145,14 +145,14 @@ func (s *WoodpeckerServer) Extend(c context.Context, req *proto.ExtendRequest) ( } func (s *WoodpeckerServer) Log(c context.Context, req *proto.LogRequest) (*proto.Empty, error) { - line := &rpc.Line{ - Out: req.GetLine().GetOut(), - Pos: int(req.GetLine().GetPos()), - Time: req.GetLine().GetTime(), - Step: req.GetLine().GetStep(), + logEntry := &rpc.LogEntry{ + Data: req.GetLogEntry().GetData(), + Line: int(req.GetLogEntry().GetLine()), + Time: req.GetLogEntry().GetTime(), + StepID: req.GetLogEntry().GetStepId(), } res := new(proto.Empty) - err := s.peer.Log(c, req.GetId(), line) + err := s.peer.Log(c, logEntry) return res, err } diff --git a/server/logging/log.go b/server/logging/log.go index f43d156cc38..df587eebcea 100644 --- a/server/logging/log.go +++ b/server/logging/log.go @@ -4,6 +4,8 @@ import ( "context" "io" "sync" + + "github.com/woodpecker-ci/woodpecker/server/model" ) // TODO (bradrydzewski) writing to subscribers is currently a blocking @@ -28,7 +30,7 @@ type stream struct { sync.Mutex path string - list []*Entry + list []*model.LogEntry subs map[*subscriber]struct{} done chan struct{} } @@ -60,7 +62,7 @@ func (l *log) Open(_ context.Context, path string) error { return nil } -func (l *log) Write(_ context.Context, path string, entry *Entry) error { +func (l *log) Write(_ context.Context, path string, logEntry *model.LogEntry) error { l.Lock() s, ok := l.streams[path] l.Unlock() @@ -68,9 +70,9 @@ func (l *log) Write(_ context.Context, path string, entry *Entry) error { return ErrNotFound } s.Lock() - s.list = append(s.list, entry) + s.list = append(s.list, logEntry) for sub := range s.subs { - go sub.handler(entry) + go sub.handler(logEntry) } s.Unlock() return nil diff --git a/server/logging/log_test.go b/server/logging/log_test.go index 206b4e1e05f..3a1f1d194bb 100644 --- a/server/logging/log_test.go +++ b/server/logging/log_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/woodpecker-ci/woodpecker/server/model" ) func TestLogging(t *testing.T) { @@ -14,7 +15,7 @@ func TestLogging(t *testing.T) { wg sync.WaitGroup testPath = "test" - testEntry = &Entry{ + testEntry = &model.LogEntry{ Data: []byte("test"), } ) @@ -26,10 +27,10 @@ func TestLogging(t *testing.T) { logger := New() assert.NoError(t, logger.Open(ctx, testPath)) go func() { - assert.NoError(t, logger.Tail(ctx, testPath, func(entry ...*Entry) { wg.Done() })) + assert.NoError(t, logger.Tail(ctx, testPath, func(entry ...*model.LogEntry) { wg.Done() })) }() go func() { - assert.NoError(t, logger.Tail(ctx, testPath, func(entry ...*Entry) { wg.Done() })) + assert.NoError(t, logger.Tail(ctx, testPath, func(entry ...*model.LogEntry) { wg.Done() })) }() <-time.After(500 * time.Millisecond) @@ -44,7 +45,7 @@ func TestLogging(t *testing.T) { wg.Add(1) go func() { - assert.NoError(t, logger.Tail(ctx, testPath, func(entry ...*Entry) { wg.Done() })) + assert.NoError(t, logger.Tail(ctx, testPath, func(entry ...*model.LogEntry) { wg.Done() })) }() <-time.After(500 * time.Millisecond) diff --git a/server/logging/logging.go b/server/logging/logging.go index 8fa1ff24e0f..ffea703b460 100644 --- a/server/logging/logging.go +++ b/server/logging/logging.go @@ -4,26 +4,15 @@ import ( "context" "errors" "io" + + "github.com/woodpecker-ci/woodpecker/server/model" ) // ErrNotFound is returned when the log does not exist. var ErrNotFound = errors.New("stream: not found") -// Entry defines a log entry. -type Entry struct { - // ID identifies this message. - ID string `json:"id,omitempty"` - - // Data is the actual data in the entry. - Data []byte `json:"data"` - - // Tags represents the key-value pairs the - // entry is tagged with. - Tags map[string]string `json:"tags,omitempty"` -} - // Handler defines a callback function for handling log entries. -type Handler func(...*Entry) +type Handler func(...*model.LogEntry) // Log defines a log multiplexer. type Log interface { @@ -31,7 +20,7 @@ type Log interface { Open(c context.Context, path string) error // Write writes the entry to the log. - Write(c context.Context, path string, entry *Entry) error + Write(c context.Context, path string, entry *model.LogEntry) error // Tail tails the log. Tail(c context.Context, path string, handler Handler) error diff --git a/server/model/log.go b/server/model/log.go index 6081381cd5b..a2fcb103b6a 100644 --- a/server/model/log.go +++ b/server/model/log.go @@ -14,11 +14,12 @@ package model -type Logs struct { - ID int64 `xorm:"pk autoincr 'log_id'"` - StepID int64 `xorm:"UNIQUE 'log_step_id'"` - Time int64 `xorm:"'log_time'"` - Line int `xorm:"'log_line'"` - Data []byte `xorm:"LONGBLOB 'log_data'"` - // TODO: add create timestamp +type LogEntry struct { + ID int64 `json:"id" xorm:"pk autoincr 'log_id'"` + StepID int64 `json:"step_id" xorm:"UNIQUE 'log_step_id'"` + Time int64 `json:"time" xorm:"'log_time'"` + Line int `json:"line" xorm:"'log_line'"` + Data []byte `json:"data" xorm:"LONGBLOB 'log_data'"` + Created int64 `json:"-" xorm:"created"` + // TODO: should we add type from pipeline/rpc/line.go? } diff --git a/server/store/datastore/log.go b/server/store/datastore/log.go index 8ea3c61d2d6..b91caccdf95 100644 --- a/server/store/datastore/log.go +++ b/server/store/datastore/log.go @@ -15,48 +15,23 @@ package datastore import ( - "bytes" - "io" - - "github.com/woodpecker-ci/woodpecker/server/logging" "github.com/woodpecker-ci/woodpecker/server/model" ) -func (s storage) LogFind(step *model.Step) (io.ReadCloser, error) { - logs := &model.Logs{ - StepID: step.ID, - } - if err := wrapGet(s.engine.Get(logs)); err != nil { - return nil, err - } - buf := bytes.NewBuffer(logs.Data) - return io.NopCloser(buf), nil +func (s storage) LogFind(step *model.Step) ([]*model.LogEntry, error) { + var logEntries []*model.LogEntry + return logEntries, s.engine.Find(&logEntries) } -func (s storage) LogSave(step *model.Step, reader io.Reader) error { - data, _ := io.ReadAll(reader) - +func (s storage) LogSave(step *model.Step, logEntries []*model.LogEntry) error { sess := s.engine.NewSession() defer sess.Close() if err := sess.Begin(); err != nil { return err } - logs := new(model.Logs) - exist, err := sess.Where("log_step_id = ?", step.ID).Get(logs) - if err != nil { - return err - } - - if exist { - if _, err := sess.ID(logs.ID).Cols("log_data").Update(&model.Logs{Data: data}); err != nil { - return err - } - } else { - if _, err := sess.Insert(&model.Logs{ - StepID: step.ID, - Data: data, - }); err != nil { + for _, logEntry := range logEntries { + if _, err := sess.Insert(logEntry); err != nil { return err } } @@ -64,32 +39,7 @@ func (s storage) LogSave(step *model.Step, reader io.Reader) error { return sess.Commit() } -func (s storage) LogAppend(stepID int64, entry *logging.Entry) error { - sess := s.engine.NewSession() - defer sess.Close() - if err := sess.Begin(); err != nil { - return err - } - - logs := new(model.Logs) - exist, err := sess.Where("log_step_id = ?", stepID).Get(logs) - if err != nil { - return err - } - - if exist { - data := append(logs.Data, entry.Data...) - if _, err := sess.ID(logs.ID).Cols("log_data").Update(&model.Logs{Data: data}); err != nil { - return err - } - } else { - if _, err := sess.Insert(&model.Logs{ - StepID: stepID, - Data: entry.Data, - }); err != nil { - return err - } - } - - return sess.Commit() +func (s storage) LogAppend(logEntry *model.LogEntry) error { + _, err := s.engine.Insert(logEntry) + return err } diff --git a/server/store/datastore/log_test.go b/server/store/datastore/log_test.go index d62b2062038..fba04c2771e 100644 --- a/server/store/datastore/log_test.go +++ b/server/store/datastore/log_test.go @@ -15,64 +15,92 @@ package datastore import ( - "bytes" - "io" "testing" "github.com/woodpecker-ci/woodpecker/server/model" ) func TestLogCreateFind(t *testing.T) { - store, closer := newTestStore(t, new(model.Step), new(model.Logs)) + store, closer := newTestStore(t, new(model.Step), new(model.LogEntry)) defer closer() step := model.Step{ ID: 1, } - buf := bytes.NewBufferString("echo hi") - err := store.LogSave(&step, buf) + + logEntries := []*model.LogEntry{ + { + StepID: step.ID, + Data: []byte("hello"), + Line: 1, + Time: 0, + }, + { + StepID: step.ID, + Data: []byte("world"), + Line: 2, + Time: 10, + }, + } + + err := store.LogSave(&step, logEntries) if err != nil { t.Errorf("Unexpected error: log create: %s", err) } - rc, err := store.LogFind(&step) + _logEntries, err := store.LogFind(&step) if err != nil { t.Errorf("Unexpected error: log create: %s", err) } - defer rc.Close() - out, _ := io.ReadAll(rc) - if got, want := string(out), "echo hi"; got != want { - t.Errorf("Want log data %s, got %s", want, got) + if got, want := len(_logEntries), len(logEntries); got != want { + t.Errorf("Want %d log entries, got %d", want, got) } } -func TestLogUpdate(t *testing.T) { - store, closer := newTestStore(t, new(model.Step), new(model.Logs)) +func TestLogAppend(t *testing.T) { + store, closer := newTestStore(t, new(model.Step), new(model.LogEntry)) defer closer() step := model.Step{ ID: 1, } - buf1 := bytes.NewBufferString("echo hi") - buf2 := bytes.NewBufferString("echo allo?") - err1 := store.LogSave(&step, buf1) - err2 := store.LogSave(&step, buf2) - if err1 != nil { - t.Errorf("Unexpected error: log create: %s", err1) + logEntries := []*model.LogEntry{ + { + StepID: step.ID, + Data: []byte("hello"), + Line: 1, + Time: 0, + }, + { + StepID: step.ID, + Data: []byte("world"), + Line: 2, + Time: 10, + }, + } + + if err := store.LogSave(&step, logEntries); err != nil { + t.Errorf("Unexpected error: log create: %s", err) + } + + logEntry := &model.LogEntry{ + StepID: step.ID, + Data: []byte("allo?"), + Line: 3, + Time: 20, } - if err2 != nil { - t.Errorf("Unexpected error: log update: %s", err2) + + if err := store.LogAppend(logEntry); err != nil { + t.Errorf("Unexpected error: log append: %s", err) } - rc, err := store.LogFind(&step) + _logEntries, err := store.LogFind(&step) if err != nil { - t.Errorf("Unexpected error: log create: %s", err) + t.Errorf("Unexpected error: log find: %s", err) } - defer rc.Close() - out, _ := io.ReadAll(rc) - if got, want := string(out), "echo allo?"; got != want { - t.Errorf("Want log data %s, got %s", want, got) + if got, want := len(_logEntries), len(logEntries)+1; got != want { + t.Errorf("Want %d log entries, got %d", want, got) } } diff --git a/server/store/datastore/migration/018_alter_logs_table.go b/server/store/datastore/migration/018_alter_logs_table.go index c553ff25758..8526f7229f3 100644 --- a/server/store/datastore/migration/018_alter_logs_table.go +++ b/server/store/datastore/migration/018_alter_logs_table.go @@ -61,7 +61,7 @@ var alterLogsTable = task{ return err } - if err := sess.Sync(new(model.Logs)); err != nil { + if err := sess.Sync(new(model.LogEntry)); err != nil { return err } @@ -88,7 +88,7 @@ var alterLogsTable = task{ time = logEntry.Time } - log := &model.Logs{ + log := &model.LogEntry{ ID: l.ID, StepID: l.StepID, Data: []byte(logEntry.Out), diff --git a/server/store/datastore/migration/migration.go b/server/store/datastore/migration/migration.go index c9c7c483794..e673a9e5a8a 100644 --- a/server/store/datastore/migration/migration.go +++ b/server/store/datastore/migration/migration.go @@ -54,7 +54,7 @@ var allBeans = []interface{}{ new(model.Pipeline), new(model.PipelineConfig), new(model.Config), - new(model.Logs), + new(model.LogEntry), new(model.Perm), new(model.Step), new(model.Registry), diff --git a/server/store/datastore/repo_test.go b/server/store/datastore/repo_test.go index 813382e54f8..8a0895abf98 100644 --- a/server/store/datastore/repo_test.go +++ b/server/store/datastore/repo_test.go @@ -299,7 +299,7 @@ func TestRepoCrud(t *testing.T) { new(model.Perm), new(model.Pipeline), new(model.PipelineConfig), - new(model.Logs), + new(model.LogEntry), new(model.Step), new(model.Secret), new(model.Registry), diff --git a/server/store/datastore/step.go b/server/store/datastore/step.go index 6e948d5014a..27b0266e9a1 100644 --- a/server/store/datastore/step.go +++ b/server/store/datastore/step.go @@ -87,7 +87,7 @@ func (s storage) StepClear(pipeline *model.Pipeline) error { } func deleteStep(sess *xorm.Session, stepID int64) error { - if _, err := sess.Where("log_step_id = ?", stepID).Delete(new(model.Logs)); err != nil { + if _, err := sess.Where("log_step_id = ?", stepID).Delete(new(model.LogEntry)); err != nil { return err } _, err := sess.ID(stepID).Delete(new(model.Step)) diff --git a/server/store/mocks/store.go b/server/store/mocks/store.go index 66cf84cb023..26c3316051f 100644 --- a/server/store/mocks/store.go +++ b/server/store/mocks/store.go @@ -1,13 +1,9 @@ -// Code generated by mockery v2.28.1. DO NOT EDIT. +// Code generated by mockery v2.28.2. DO NOT EDIT. package mocks import ( - io "io" - mock "github.com/stretchr/testify/mock" - logging "github.com/woodpecker-ci/woodpecker/server/logging" - model "github.com/woodpecker-ci/woodpecker/server/model" ) @@ -1091,13 +1087,13 @@ func (_m *Store) HasRedirectionForRepo(_a0 int64, _a1 string) (bool, error) { return r0, r1 } -// LogAppend provides a mock function with given fields: stepID, entry -func (_m *Store) LogAppend(stepID int64, entry *logging.Entry) error { - ret := _m.Called(stepID, entry) +// LogAppend provides a mock function with given fields: logEntry +func (_m *Store) LogAppend(logEntry *model.LogEntry) error { + ret := _m.Called(logEntry) var r0 error - if rf, ok := ret.Get(0).(func(int64, *logging.Entry) error); ok { - r0 = rf(stepID, entry) + if rf, ok := ret.Get(0).(func(*model.LogEntry) error); ok { + r0 = rf(logEntry) } else { r0 = ret.Error(0) } @@ -1106,19 +1102,19 @@ func (_m *Store) LogAppend(stepID int64, entry *logging.Entry) error { } // LogFind provides a mock function with given fields: _a0 -func (_m *Store) LogFind(_a0 *model.Step) (io.ReadCloser, error) { +func (_m *Store) LogFind(_a0 *model.Step) ([]*model.LogEntry, error) { ret := _m.Called(_a0) - var r0 io.ReadCloser + var r0 []*model.LogEntry var r1 error - if rf, ok := ret.Get(0).(func(*model.Step) (io.ReadCloser, error)); ok { + if rf, ok := ret.Get(0).(func(*model.Step) ([]*model.LogEntry, error)); ok { return rf(_a0) } - if rf, ok := ret.Get(0).(func(*model.Step) io.ReadCloser); ok { + if rf, ok := ret.Get(0).(func(*model.Step) []*model.LogEntry); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(io.ReadCloser) + r0 = ret.Get(0).([]*model.LogEntry) } } @@ -1132,11 +1128,11 @@ func (_m *Store) LogFind(_a0 *model.Step) (io.ReadCloser, error) { } // LogSave provides a mock function with given fields: _a0, _a1 -func (_m *Store) LogSave(_a0 *model.Step, _a1 io.Reader) error { +func (_m *Store) LogSave(_a0 *model.Step, _a1 []*model.LogEntry) error { ret := _m.Called(_a0, _a1) var r0 error - if rf, ok := ret.Get(0).(func(*model.Step, io.Reader) error); ok { + if rf, ok := ret.Get(0).(func(*model.Step, []*model.LogEntry) error); ok { r0 = rf(_a0, _a1) } else { r0 = ret.Error(0) diff --git a/server/store/store.go b/server/store/store.go index c26c82b878c..b5000de5653 100644 --- a/server/store/store.go +++ b/server/store/store.go @@ -18,9 +18,6 @@ package store //go:generate mockery --name Store --output mocks --case underscore import ( - "io" - - "github.com/woodpecker-ci/woodpecker/server/logging" "github.com/woodpecker-ci/woodpecker/server/model" ) @@ -146,11 +143,9 @@ type Store interface { StepClear(*model.Pipeline) error // Logs - LogFind(*model.Step) (io.ReadCloser, error) - // TODO: since we do ReadAll in any case a ioReader is not the best idea - // so either find a way to write log in chunks by xorm ... - LogSave(*model.Step, io.Reader) error - LogAppend(stepID int64, entry *logging.Entry) error + LogFind(*model.Step) ([]*model.LogEntry, error) + LogSave(*model.Step, []*model.LogEntry) error + LogAppend(logEntry *model.LogEntry) error // Tasks // TaskList TODO: paginate & opt filter diff --git a/web/src/lib/api/types/pipeline.ts b/web/src/lib/api/types/pipeline.ts index 052595fa909..348358e67b6 100644 --- a/web/src/lib/api/types/pipeline.ts +++ b/web/src/lib/api/types/pipeline.ts @@ -118,10 +118,11 @@ export type PipelineStep = { }; export type PipelineLog = { - step: string; - pos: number; - out: string; - time?: number; + id: number; + step_id: number; + time: number; + line: number; + data: string; }; export type PipelineFeed = Pipeline & { From d35daa7d28b83a39d632f5f25a9b2b3d6807553d Mon Sep 17 00:00:00 2001 From: Anbraten Date: Sun, 4 Jun 2023 11:00:17 +0200 Subject: [PATCH 05/37] update ui --- .../components/repo/pipeline/PipelineLog.vue | 399 ++++++++---------- 1 file changed, 184 insertions(+), 215 deletions(-) diff --git a/web/src/components/repo/pipeline/PipelineLog.vue b/web/src/components/repo/pipeline/PipelineLog.vue index 4b48522c6b3..727ed1215e8 100644 --- a/web/src/components/repo/pipeline/PipelineLog.vue +++ b/web/src/components/repo/pipeline/PipelineLog.vue @@ -64,13 +64,13 @@ - From 8a101decc1a077dc2297a41bd85ebe5143d19d7f Mon Sep 17 00:00:00 2001 From: Anbraten Date: Sun, 4 Jun 2023 11:06:37 +0200 Subject: [PATCH 06/37] adjust api endpoints and ui --- server/api/pipeline.go | 10 +++------- server/api/stream.go | 14 ++------------ server/router/api.go | 5 ++--- web/src/components/repo/pipeline/PipelineLog.vue | 4 ++-- web/src/lib/api/index.ts | 4 ++-- 5 files changed, 11 insertions(+), 26 deletions(-) diff --git a/server/api/pipeline.go b/server/api/pipeline.go index d043de6bf0d..f6cbb94bdb7 100644 --- a/server/api/pipeline.go +++ b/server/api/pipeline.go @@ -245,20 +245,16 @@ func GetPipelineLogs(c *gin.Context) { // @Param pid path int true "the pipeline id" func GetStepLogs(c *gin.Context) { _store := store.FromContext(c) - repo := session.Repo(c) // parse the pipeline number and step sequence number from // the request parameter. - num, _ := strconv.ParseInt(c.Params.ByName("number"), 10, 64) - pid, _ := strconv.Atoi(c.Params.ByName("pid")) - - pl, err := _store.GetPipelineNumber(repo, num) + stepID, err := strconv.ParseInt(c.Params.ByName("stepId"), 10, 64) if err != nil { - handleDbGetError(c, err) + _ = c.AbortWithError(http.StatusBadRequest, err) return } - step, err := _store.StepFind(pl, pid) + step, err := _store.StepLoad(stepID) if err != nil { handleDbGetError(c, err) return diff --git a/server/api/stream.go b/server/api/stream.go index 75053b6697a..66e55cbc579 100644 --- a/server/api/stream.go +++ b/server/api/stream.go @@ -137,21 +137,11 @@ func LogStreamSSE(c *gin.Context) { logWriteStringErr(io.WriteString(rw, ": ping\n\n")) flusher.Flush() - repo := session.Repo(c) _store := store.FromContext(c) - // parse the pipeline number and step sequence number from - // the request parameter. - pipelinen, _ := strconv.ParseInt(c.Param("pipeline"), 10, 64) - stepn, _ := strconv.Atoi(c.Param("number")) + stepID, _ := strconv.ParseInt(c.Param("stepId"), 10, 64) - pipeline, err := _store.GetPipelineNumber(repo, pipelinen) - if err != nil { - log.Debug().Msgf("stream cannot get pipeline number: %v", err) - logWriteStringErr(io.WriteString(rw, "event: error\ndata: pipeline not found\n\n")) - return - } - step, err := _store.StepFind(pipeline, stepn) + step, err := _store.StepLoad(stepID) if err != nil { log.Debug().Msgf("stream cannot get step number: %v", err) logWriteStringErr(io.WriteString(rw, "event: error\ndata: process not found\n\n")) diff --git a/server/router/api.go b/server/router/api.go index 150de3ffb7f..278cb57edd0 100644 --- a/server/router/api.go +++ b/server/router/api.go @@ -89,8 +89,7 @@ func apiRoutes(e *gin.Engine) { repo.POST("/pipelines/:number/approve", session.MustPush, api.PostApproval) repo.POST("/pipelines/:number/decline", session.MustPush, api.PostDecline) - repo.GET("/logs/:number/:pid", api.GetStepLogs) - repo.GET("/logs/:number/:pid/:step", api.GetPipelineLogs) + repo.GET("/logs/:number/:stepId", api.GetStepLogs) // requires push permissions repo.DELETE("/logs/:number", session.MustPush, api.DeletePipelineLogs) @@ -204,7 +203,7 @@ func apiRoutes(e *gin.Engine) { sse := e.Group("/stream") { sse.GET("/events", api.EventStreamSSE) - sse.GET("/logs/:owner/:name/:pipeline/:number", + sse.GET("/logs/:owner/:name/:pipeline/:stepId", session.SetRepo(), session.SetPerm(), session.MustPull, diff --git a/web/src/components/repo/pipeline/PipelineLog.vue b/web/src/components/repo/pipeline/PipelineLog.vue index 727ed1215e8..5a2b5b78429 100644 --- a/web/src/components/repo/pipeline/PipelineLog.vue +++ b/web/src/components/repo/pipeline/PipelineLog.vue @@ -190,7 +190,7 @@ async function download() { let logs; try { downloadInProgress.value = true; - logs = await apiClient.getLogs(repo.value.owner, repo.value.name, pipeline.value.number, step.value.pid); + logs = await apiClient.getLogs(repo.value.owner, repo.value.name, pipeline.value.number, step.value.id); } catch (e) { notifications.notifyError(e, i18n.t('repo.pipeline.log_download_error')); return; @@ -239,7 +239,7 @@ async function loadLogs() { } if (isStepFinished(step.value)) { - const logs = await apiClient.getLogs(repo.value.owner, repo.value.name, pipeline.value.number, step.value.pid); + const logs = await apiClient.getLogs(repo.value.owner, repo.value.name, pipeline.value.number, step.value.id); logs?.forEach((line) => writeLog({ index: line.line, text: line.data, time: line.time })); flushLogs(false); } diff --git a/web/src/lib/api/index.ts b/web/src/lib/api/index.ts index 88c2da845e3..cec83b30f40 100644 --- a/web/src/lib/api/index.ts +++ b/web/src/lib/api/index.ts @@ -129,8 +129,8 @@ export default class WoodpeckerClient extends ApiClient { return this._post(`/api/repos/${owner}/${repo}/pipelines/${pipeline}?${query}`) as Promise; } - getLogs(owner: string, repo: string, pipeline: number, step: number): Promise { - return this._get(`/api/repos/${owner}/${repo}/logs/${pipeline}/${step}`) as Promise; + getLogs(owner: string, repo: string, pipeline: number, stepId: number): Promise { + return this._get(`/api/repos/${owner}/${repo}/logs/${pipeline}/${stepId}`) as Promise; } getSecretList(owner: string, repo: string, page: number): Promise { From 474724d8e6d48d8ae14519778f29011b3f105652 Mon Sep 17 00:00:00 2001 From: Anbraten Date: Sun, 4 Jun 2023 11:31:39 +0200 Subject: [PATCH 07/37] rename --- cli/exec/line.go | 50 ++++++++++------------------- pipeline/rpc/log_entry.go | 14 ++++---- pipeline/rpc/proto/woodpecker.proto | 3 +- 3 files changed, 26 insertions(+), 41 deletions(-) diff --git a/cli/exec/line.go b/cli/exec/line.go index 3ba05b8be42..ae4acd8caf9 100644 --- a/cli/exec/line.go +++ b/cli/exec/line.go @@ -19,39 +19,23 @@ import ( "os" "strings" "time" -) -// Identifies the type of line in the logs. -const ( - LineStdout int = iota - LineStderr - LineExitCode - LineMetadata - LineProgress + "github.com/woodpecker-ci/woodpecker/pipeline/rpc" ) -// Line is a line of console output. -type Line struct { - Step string `json:"step,omitempty"` - Time int64 `json:"time,omitempty"` - Type int `json:"type,omitempty"` - Pos int `json:"pos,omitempty"` - Out string `json:"out,omitempty"` -} - // LineWriter sends logs to the client. type LineWriter struct { - name string - num int - now time.Time - rep *strings.Replacer - lines []*Line + stepID int64 + num int + now time.Time + rep *strings.Replacer + lines []*rpc.LogEntry } // NewLineWriter returns a new line reader. -func NewLineWriter(name string) *LineWriter { +func NewLineWriter(stepID int64) *LineWriter { w := new(LineWriter) - w.name = name + w.stepID = stepID w.num = 0 w.now = time.Now().UTC() @@ -59,20 +43,20 @@ func NewLineWriter(name string) *LineWriter { } func (w *LineWriter) Write(p []byte) (n int, err error) { - out := string(p) + data := string(p) if w.rep != nil { - out = w.rep.Replace(out) + data = w.rep.Replace(data) } - line := &Line{ - Out: out, - Step: w.name, - Pos: w.num, - Time: int64(time.Since(w.now).Seconds()), - Type: LineStdout, + line := &rpc.LogEntry{ + Data: data, + StepID: w.stepID, + Line: w.num, + Time: int64(time.Since(w.now).Seconds()), + Type: rpc.LogEntryStdout, } - fmt.Fprintf(os.Stderr, "[%s:L%d:%ds] %s", w.name, w.num, int64(time.Since(w.now).Seconds()), out) + fmt.Fprintf(os.Stderr, "[%d:L%d:%ds] %s", w.stepID, w.num, int64(time.Since(w.now).Seconds()), data) w.num++ diff --git a/pipeline/rpc/log_entry.go b/pipeline/rpc/log_entry.go index 5b4d8bfe492..c7ab8408f67 100644 --- a/pipeline/rpc/log_entry.go +++ b/pipeline/rpc/log_entry.go @@ -28,11 +28,11 @@ import ( // Identifies the type of line in the logs. const ( - LineStdout int = iota - LineStderr - LineExitCode - LineMetadata - LineProgress + LogEntryStdout int = iota + LogEntryStderr + LogEntryExitCode + LogEntryMetadata + LogEntryProgress ) // Line is a line of console output. @@ -46,7 +46,7 @@ type LogEntry struct { func (l *LogEntry) String() string { switch l.Type { - case LineExitCode: + case LogEntryExitCode: return fmt.Sprintf("[%d] exit code %s", l.StepID, l.Data) default: return fmt.Sprintf("[%d:L%v:%vs] %s", l.StepID, l.Line, l.Time, l.Data) @@ -85,7 +85,7 @@ func (w *LineWriter) Write(p []byte) (n int, err error) { Data: data, StepID: w.stepID, Time: int64(time.Since(w.now).Seconds()), - Type: LineStdout, + Type: LogEntryStdout, } if err := w.peer.Log(context.Background(), line); err != nil { log.Error().Err(err).Msgf("fail to write pipeline log to peer '%d'", w.stepID) diff --git a/pipeline/rpc/proto/woodpecker.proto b/pipeline/rpc/proto/woodpecker.proto index 9c888231b8e..4dbf0080be4 100644 --- a/pipeline/rpc/proto/woodpecker.proto +++ b/pipeline/rpc/proto/woodpecker.proto @@ -53,7 +53,8 @@ message LogEntry { int64 step_id = 1; int64 time = 2; int32 line = 3; - string data = 4; + int32 type = 4; // 0 = stdout, 1 = stderr, 2 = exit-code, 3 = metadata, 4 = progress + string data = 5; } message Filter { From dd3e1fdb90d79f4d1c28e57d9614c6f16d3d0f1a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 4 Jun 2023 20:14:03 +0200 Subject: [PATCH 08/37] finish store & bump proto version ... --- pipeline/rpc/proto/version.go | 2 +- pipeline/rpc/proto/woodpecker.pb.go | 246 +++++++++--------- pipeline/rpc/proto/woodpecker_grpc.pb.go | 80 ++++-- server/api/pipeline.go | 6 +- server/model/log.go | 6 +- server/store/datastore/log.go | 12 +- server/store/datastore/log_test.go | 38 ++- .../migration/018_alter_logs_table.go | 4 + server/store/mocks/store.go | 14 + server/store/store.go | 1 + 10 files changed, 235 insertions(+), 174 deletions(-) diff --git a/pipeline/rpc/proto/version.go b/pipeline/rpc/proto/version.go index 70e92d80e21..3a1b1b168a3 100644 --- a/pipeline/rpc/proto/version.go +++ b/pipeline/rpc/proto/version.go @@ -16,4 +16,4 @@ package proto // Version is the version of the woodpecker.proto file, // !IMPORTANT! increased by 1 each time it get changed !IMPORTANT! -const Version int32 = 2 +const Version int32 = 3 diff --git a/pipeline/rpc/proto/woodpecker.pb.go b/pipeline/rpc/proto/woodpecker.pb.go index 193784fdfb5..3ade4f19159 100644 --- a/pipeline/rpc/proto/woodpecker.pb.go +++ b/pipeline/rpc/proto/woodpecker.pb.go @@ -15,8 +15,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.0 -// protoc v3.12.4 +// protoc-gen-go v1.30.0 +// protoc v3.21.12 // source: woodpecker.proto package proto @@ -130,7 +130,8 @@ type LogEntry struct { StepId int64 `protobuf:"varint,1,opt,name=step_id,json=stepId,proto3" json:"step_id,omitempty"` Time int64 `protobuf:"varint,2,opt,name=time,proto3" json:"time,omitempty"` Line int32 `protobuf:"varint,3,opt,name=line,proto3" json:"line,omitempty"` - Data string `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` + Type int32 `protobuf:"varint,4,opt,name=type,proto3" json:"type,omitempty"` // 0 = stdout, 1 = stderr, 2 = exit-code, 3 = metadata, 4 = progress + Data string `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` } func (x *LogEntry) Reset() { @@ -186,6 +187,13 @@ func (x *LogEntry) GetLine() int32 { return 0 } +func (x *LogEntry) GetType() int32 { + if x != nil { + return x.Type + } + return 0 +} + func (x *LogEntry) GetData() string { if x != nil { return x.Data @@ -1101,127 +1109,129 @@ var file_woodpecker_proto_rawDesc = []byte{ 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x5f, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, + 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x73, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x74, 0x65, 0x70, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x76, 0x0a, 0x06, 0x46, 0x69, 0x6c, - 0x74, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, - 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x22, 0x4e, 0x0a, 0x08, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, - 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, - 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x22, 0x34, 0x0a, 0x0b, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x25, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, - 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x41, 0x0a, 0x0b, 0x49, 0x6e, 0x69, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x1d, 0x0a, 0x0b, 0x57, 0x61, - 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x41, 0x0a, 0x0b, 0x44, 0x6f, 0x6e, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x1f, 0x0a, 0x0d, - 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x43, 0x0a, - 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, - 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, - 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x22, 0x49, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x2b, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x07, 0x0a, - 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x2d, 0x0a, 0x13, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, - 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, - 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x63, 0x61, - 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, - 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, 0x22, 0x5b, 0x0a, 0x0f, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, - 0x0c, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0b, 0x67, 0x72, 0x70, 0x63, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, - 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3b, 0x0a, 0x0c, 0x4e, 0x65, 0x78, 0x74, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x70, 0x69, 0x70, 0x65, 0x6c, - 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x08, 0x70, 0x69, 0x70, 0x65, - 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x32, 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x19, 0x0a, - 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x49, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x67, - 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, - 0x74, 0x49, 0x64, 0x22, 0x64, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0x8a, 0x04, 0x0a, 0x0a, 0x57, 0x6f, - 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x1a, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x04, 0x4e, - 0x65, 0x78, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x65, 0x78, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2a, - 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, - 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x04, 0x57, 0x61, - 0x69, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x52, + 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x76, 0x0a, + 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, + 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4e, 0x0a, 0x08, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, + 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x34, 0x0a, 0x0b, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x41, 0x0a, 0x0b, 0x49, + 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x1d, + 0x0a, 0x0b, 0x57, 0x61, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, + 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x41, 0x0a, + 0x0b, 0x44, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x22, 0x1f, 0x0a, 0x0d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, + 0x64, 0x22, 0x43, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, + 0x69, 0x64, 0x12, 0x22, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, + 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x49, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, + 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x2d, 0x0a, 0x13, 0x52, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x14, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x1a, + 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, + 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x63, + 0x6b, 0x65, 0x6e, 0x64, 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, 0x22, 0x5b, + 0x0a, 0x0f, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x67, 0x72, 0x70, 0x63, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x76, + 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3b, 0x0a, 0x0c, 0x4e, + 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x70, + 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x08, + 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x32, 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x49, 0x0a, 0x0b, + 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x61, + 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0a, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x19, 0x0a, 0x08, + 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, + 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x64, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, + 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0x8a, 0x04, + 0x0a, 0x0a, 0x57, 0x6f, 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x07, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, + 0x31, 0x0a, 0x04, 0x4e, 0x65, 0x78, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, + 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2a, + 0x0a, 0x04, 0x57, 0x61, 0x69, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, + 0x61, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x04, 0x44, 0x6f, + 0x6e, 0x65, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x6f, 0x6e, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, + 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x06, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, + 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x04, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x12, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x06, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x12, 0x14, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, - 0x22, 0x00, 0x12, 0x28, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x0d, - 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, - 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0c, 0x52, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, + 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x32, 0x43, 0x0a, 0x0e, 0x57, 0x6f, 0x6f, 0x64, 0x70, 0x65, - 0x63, 0x6b, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x12, 0x31, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, - 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74, - 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x38, 0x5a, 0x36, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x77, 0x6f, 0x6f, 0x64, 0x70, 0x65, - 0x63, 0x6b, 0x65, 0x72, 0x2d, 0x63, 0x69, 0x2f, 0x77, 0x6f, 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, - 0x65, 0x72, 0x2f, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2f, 0x72, 0x70, 0x63, 0x2f, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x28, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x11, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, + 0x12, 0x4c, 0x0a, 0x0d, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, + 0x74, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, + 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, + 0x0a, 0x0c, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x1a, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, + 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x32, 0x43, 0x0a, 0x0e, 0x57, 0x6f, + 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x12, 0x31, 0x0a, 0x04, + 0x41, 0x75, 0x74, 0x68, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74, + 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, + 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x77, 0x6f, + 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x2d, 0x63, 0x69, 0x2f, 0x77, 0x6f, 0x6f, 0x64, + 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x2f, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2f, + 0x72, 0x70, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( diff --git a/pipeline/rpc/proto/woodpecker_grpc.pb.go b/pipeline/rpc/proto/woodpecker_grpc.pb.go index 3d2473519f0..44c2814fa61 100644 --- a/pipeline/rpc/proto/woodpecker_grpc.pb.go +++ b/pipeline/rpc/proto/woodpecker_grpc.pb.go @@ -1,7 +1,22 @@ +// Copyright 2021 Woodpecker Authors +// Copyright 2011 Drone.IO Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: -// - protoc-gen-go-grpc v1.2.0 -// - protoc v3.12.4 +// - protoc-gen-go-grpc v1.3.0 +// - protoc v3.21.12 // source: woodpecker.proto package proto @@ -18,6 +33,19 @@ import ( // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 +const ( + Woodpecker_Version_FullMethodName = "/proto.Woodpecker/Version" + Woodpecker_Next_FullMethodName = "/proto.Woodpecker/Next" + Woodpecker_Init_FullMethodName = "/proto.Woodpecker/Init" + Woodpecker_Wait_FullMethodName = "/proto.Woodpecker/Wait" + Woodpecker_Done_FullMethodName = "/proto.Woodpecker/Done" + Woodpecker_Extend_FullMethodName = "/proto.Woodpecker/Extend" + Woodpecker_Update_FullMethodName = "/proto.Woodpecker/Update" + Woodpecker_Log_FullMethodName = "/proto.Woodpecker/Log" + Woodpecker_RegisterAgent_FullMethodName = "/proto.Woodpecker/RegisterAgent" + Woodpecker_ReportHealth_FullMethodName = "/proto.Woodpecker/ReportHealth" +) + // WoodpeckerClient is the client API for Woodpecker 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. @@ -44,7 +72,7 @@ func NewWoodpeckerClient(cc grpc.ClientConnInterface) WoodpeckerClient { func (c *woodpeckerClient) Version(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VersionResponse, error) { out := new(VersionResponse) - err := c.cc.Invoke(ctx, "/proto.Woodpecker/Version", in, out, opts...) + err := c.cc.Invoke(ctx, Woodpecker_Version_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -53,7 +81,7 @@ func (c *woodpeckerClient) Version(ctx context.Context, in *Empty, opts ...grpc. func (c *woodpeckerClient) Next(ctx context.Context, in *NextRequest, opts ...grpc.CallOption) (*NextResponse, error) { out := new(NextResponse) - err := c.cc.Invoke(ctx, "/proto.Woodpecker/Next", in, out, opts...) + err := c.cc.Invoke(ctx, Woodpecker_Next_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -62,7 +90,7 @@ func (c *woodpeckerClient) Next(ctx context.Context, in *NextRequest, opts ...gr func (c *woodpeckerClient) Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := c.cc.Invoke(ctx, "/proto.Woodpecker/Init", in, out, opts...) + err := c.cc.Invoke(ctx, Woodpecker_Init_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -71,7 +99,7 @@ func (c *woodpeckerClient) Init(ctx context.Context, in *InitRequest, opts ...gr func (c *woodpeckerClient) Wait(ctx context.Context, in *WaitRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := c.cc.Invoke(ctx, "/proto.Woodpecker/Wait", in, out, opts...) + err := c.cc.Invoke(ctx, Woodpecker_Wait_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -80,7 +108,7 @@ func (c *woodpeckerClient) Wait(ctx context.Context, in *WaitRequest, opts ...gr func (c *woodpeckerClient) Done(ctx context.Context, in *DoneRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := c.cc.Invoke(ctx, "/proto.Woodpecker/Done", in, out, opts...) + err := c.cc.Invoke(ctx, Woodpecker_Done_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -89,7 +117,7 @@ func (c *woodpeckerClient) Done(ctx context.Context, in *DoneRequest, opts ...gr func (c *woodpeckerClient) Extend(ctx context.Context, in *ExtendRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := c.cc.Invoke(ctx, "/proto.Woodpecker/Extend", in, out, opts...) + err := c.cc.Invoke(ctx, Woodpecker_Extend_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -98,7 +126,7 @@ func (c *woodpeckerClient) Extend(ctx context.Context, in *ExtendRequest, opts . func (c *woodpeckerClient) Update(ctx context.Context, in *UpdateRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := c.cc.Invoke(ctx, "/proto.Woodpecker/Update", in, out, opts...) + err := c.cc.Invoke(ctx, Woodpecker_Update_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -107,7 +135,7 @@ func (c *woodpeckerClient) Update(ctx context.Context, in *UpdateRequest, opts . func (c *woodpeckerClient) Log(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := c.cc.Invoke(ctx, "/proto.Woodpecker/Log", in, out, opts...) + err := c.cc.Invoke(ctx, Woodpecker_Log_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -116,7 +144,7 @@ func (c *woodpeckerClient) Log(ctx context.Context, in *LogRequest, opts ...grpc func (c *woodpeckerClient) RegisterAgent(ctx context.Context, in *RegisterAgentRequest, opts ...grpc.CallOption) (*RegisterAgentResponse, error) { out := new(RegisterAgentResponse) - err := c.cc.Invoke(ctx, "/proto.Woodpecker/RegisterAgent", in, out, opts...) + err := c.cc.Invoke(ctx, Woodpecker_RegisterAgent_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -125,7 +153,7 @@ func (c *woodpeckerClient) RegisterAgent(ctx context.Context, in *RegisterAgentR func (c *woodpeckerClient) ReportHealth(ctx context.Context, in *ReportHealthRequest, opts ...grpc.CallOption) (*Empty, error) { out := new(Empty) - err := c.cc.Invoke(ctx, "/proto.Woodpecker/ReportHealth", in, out, opts...) + err := c.cc.Invoke(ctx, Woodpecker_ReportHealth_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -206,7 +234,7 @@ func _Woodpecker_Version_Handler(srv interface{}, ctx context.Context, dec func( } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.Woodpecker/Version", + FullMethod: Woodpecker_Version_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Version(ctx, req.(*Empty)) @@ -224,7 +252,7 @@ func _Woodpecker_Next_Handler(srv interface{}, ctx context.Context, dec func(int } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.Woodpecker/Next", + FullMethod: Woodpecker_Next_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Next(ctx, req.(*NextRequest)) @@ -242,7 +270,7 @@ func _Woodpecker_Init_Handler(srv interface{}, ctx context.Context, dec func(int } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.Woodpecker/Init", + FullMethod: Woodpecker_Init_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Init(ctx, req.(*InitRequest)) @@ -260,7 +288,7 @@ func _Woodpecker_Wait_Handler(srv interface{}, ctx context.Context, dec func(int } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.Woodpecker/Wait", + FullMethod: Woodpecker_Wait_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Wait(ctx, req.(*WaitRequest)) @@ -278,7 +306,7 @@ func _Woodpecker_Done_Handler(srv interface{}, ctx context.Context, dec func(int } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.Woodpecker/Done", + FullMethod: Woodpecker_Done_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Done(ctx, req.(*DoneRequest)) @@ -296,7 +324,7 @@ func _Woodpecker_Extend_Handler(srv interface{}, ctx context.Context, dec func(i } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.Woodpecker/Extend", + FullMethod: Woodpecker_Extend_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Extend(ctx, req.(*ExtendRequest)) @@ -314,7 +342,7 @@ func _Woodpecker_Update_Handler(srv interface{}, ctx context.Context, dec func(i } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.Woodpecker/Update", + FullMethod: Woodpecker_Update_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Update(ctx, req.(*UpdateRequest)) @@ -332,7 +360,7 @@ func _Woodpecker_Log_Handler(srv interface{}, ctx context.Context, dec func(inte } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.Woodpecker/Log", + FullMethod: Woodpecker_Log_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).Log(ctx, req.(*LogRequest)) @@ -350,7 +378,7 @@ func _Woodpecker_RegisterAgent_Handler(srv interface{}, ctx context.Context, dec } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.Woodpecker/RegisterAgent", + FullMethod: Woodpecker_RegisterAgent_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).RegisterAgent(ctx, req.(*RegisterAgentRequest)) @@ -368,7 +396,7 @@ func _Woodpecker_ReportHealth_Handler(srv interface{}, ctx context.Context, dec } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.Woodpecker/ReportHealth", + FullMethod: Woodpecker_ReportHealth_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerServer).ReportHealth(ctx, req.(*ReportHealthRequest)) @@ -428,6 +456,10 @@ var Woodpecker_ServiceDesc = grpc.ServiceDesc{ Metadata: "woodpecker.proto", } +const ( + WoodpeckerAuth_Auth_FullMethodName = "/proto.WoodpeckerAuth/Auth" +) + // WoodpeckerAuthClient is the client API for WoodpeckerAuth 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. @@ -445,7 +477,7 @@ func NewWoodpeckerAuthClient(cc grpc.ClientConnInterface) WoodpeckerAuthClient { func (c *woodpeckerAuthClient) Auth(ctx context.Context, in *AuthRequest, opts ...grpc.CallOption) (*AuthResponse, error) { out := new(AuthResponse) - err := c.cc.Invoke(ctx, "/proto.WoodpeckerAuth/Auth", in, out, opts...) + err := c.cc.Invoke(ctx, WoodpeckerAuth_Auth_FullMethodName, in, out, opts...) if err != nil { return nil, err } @@ -490,7 +522,7 @@ func _WoodpeckerAuth_Auth_Handler(srv interface{}, ctx context.Context, dec func } info := &grpc.UnaryServerInfo{ Server: srv, - FullMethod: "/proto.WoodpeckerAuth/Auth", + FullMethod: WoodpeckerAuth_Auth_FullMethodName, } handler := func(ctx context.Context, req interface{}) (interface{}, error) { return srv.(WoodpeckerAuthServer).Auth(ctx, req.(*AuthRequest)) diff --git a/server/api/pipeline.go b/server/api/pipeline.go index f6cbb94bdb7..045b92cd577 100644 --- a/server/api/pipeline.go +++ b/server/api/pipeline.go @@ -536,10 +536,8 @@ func DeletePipelineLogs(c *gin.Context) { } for _, step := range steps { - logs := make([]*model.LogEntry, 0) - lerr := _store.LogSave(step, logs) - if lerr != nil { - err = lerr + if lErr := _store.LogDelete(step); err != nil { + err = errors.Join(err, lErr) } } if err != nil { diff --git a/server/model/log.go b/server/model/log.go index a2fcb103b6a..71f5611cb58 100644 --- a/server/model/log.go +++ b/server/model/log.go @@ -16,10 +16,10 @@ package model type LogEntry struct { ID int64 `json:"id" xorm:"pk autoincr 'log_id'"` - StepID int64 `json:"step_id" xorm:"UNIQUE 'log_step_id'"` + StepID int64 `json:"step_id" xorm:"UNIQUE(log) 'log_step_id'"` Time int64 `json:"time" xorm:"'log_time'"` - Line int `json:"line" xorm:"'log_line'"` + Line int `json:"line" xorm:"UNIQUE(log) 'log_line'"` Data []byte `json:"data" xorm:"LONGBLOB 'log_data'"` Created int64 `json:"-" xorm:"created"` - // TODO: should we add type from pipeline/rpc/line.go? + // TODO: should we add type from pipeline/rpc/line.go ? } diff --git a/server/store/datastore/log.go b/server/store/datastore/log.go index b91caccdf95..53ed8a06cce 100644 --- a/server/store/datastore/log.go +++ b/server/store/datastore/log.go @@ -15,12 +15,14 @@ package datastore import ( + "fmt" + "github.com/woodpecker-ci/woodpecker/server/model" ) func (s storage) LogFind(step *model.Step) ([]*model.LogEntry, error) { var logEntries []*model.LogEntry - return logEntries, s.engine.Find(&logEntries) + return logEntries, s.engine.Asc("log_line").Where("log_step_id = ?", step.ID).Find(&logEntries) } func (s storage) LogSave(step *model.Step, logEntries []*model.LogEntry) error { @@ -31,6 +33,9 @@ func (s storage) LogSave(step *model.Step, logEntries []*model.LogEntry) error { } for _, logEntry := range logEntries { + if logEntry.StepID != step.ID { + return fmt.Errorf("got a log-entry with step id '%d' but require '%d'", logEntry.StepID, step.ID) + } if _, err := sess.Insert(logEntry); err != nil { return err } @@ -43,3 +48,8 @@ func (s storage) LogAppend(logEntry *model.LogEntry) error { _, err := s.engine.Insert(logEntry) return err } + +func (s storage) LogDelete(step *model.Step) error { + _, err := s.engine.Where("log_step_id = ?", step.ID).Delete(new(model.LogEntry)) + return err +} diff --git a/server/store/datastore/log_test.go b/server/store/datastore/log_test.go index fba04c2771e..edb793b4914 100644 --- a/server/store/datastore/log_test.go +++ b/server/store/datastore/log_test.go @@ -17,6 +17,7 @@ package datastore import ( "testing" + "github.com/stretchr/testify/assert" "github.com/woodpecker-ci/woodpecker/server/model" ) @@ -43,19 +44,19 @@ func TestLogCreateFind(t *testing.T) { }, } - err := store.LogSave(&step, logEntries) - if err != nil { - t.Errorf("Unexpected error: log create: %s", err) - } + // first insert should just work + assert.NoError(t, store.LogSave(&step, logEntries)) - _logEntries, err := store.LogFind(&step) - if err != nil { - t.Errorf("Unexpected error: log create: %s", err) + // reset id and check against unique constrains (stepID+lineNr) + for i := range logEntries { + logEntries[i].ID = 0 } + assert.Error(t, store.LogSave(&step, logEntries)) - if got, want := len(_logEntries), len(logEntries); got != want { - t.Errorf("Want %d log entries, got %d", want, got) - } + // we want to find our inserted logs + _logEntries, err := store.LogFind(&step) + assert.NoError(t, err) + assert.Len(t, _logEntries, len(logEntries)) } func TestLogAppend(t *testing.T) { @@ -80,9 +81,7 @@ func TestLogAppend(t *testing.T) { }, } - if err := store.LogSave(&step, logEntries); err != nil { - t.Errorf("Unexpected error: log create: %s", err) - } + assert.NoError(t, store.LogSave(&step, logEntries)) logEntry := &model.LogEntry{ StepID: step.ID, @@ -91,16 +90,9 @@ func TestLogAppend(t *testing.T) { Time: 20, } - if err := store.LogAppend(logEntry); err != nil { - t.Errorf("Unexpected error: log append: %s", err) - } + assert.NoError(t, store.LogAppend(logEntry)) _logEntries, err := store.LogFind(&step) - if err != nil { - t.Errorf("Unexpected error: log find: %s", err) - } - - if got, want := len(_logEntries), len(logEntries)+1; got != want { - t.Errorf("Want %d log entries, got %d", want, got) - } + assert.NoError(t, err) + assert.Len(t, _logEntries, len(logEntries)+1) } diff --git a/server/store/datastore/migration/018_alter_logs_table.go b/server/store/datastore/migration/018_alter_logs_table.go index 8526f7229f3..13323962b14 100644 --- a/server/store/datastore/migration/018_alter_logs_table.go +++ b/server/store/datastore/migration/018_alter_logs_table.go @@ -41,6 +41,10 @@ type oldLogs018 struct { Data []byte `xorm:"LONGBLOB 'log_data'"` } +func (oldLogs018) TableName() string { + return "logs" +} + type oldLogEntry018 struct { Step string `json:"step,omitempty"` Time int64 `json:"time,omitempty"` diff --git a/server/store/mocks/store.go b/server/store/mocks/store.go index 26c3316051f..e7d45cd977b 100644 --- a/server/store/mocks/store.go +++ b/server/store/mocks/store.go @@ -1101,6 +1101,20 @@ func (_m *Store) LogAppend(logEntry *model.LogEntry) error { return r0 } +// LogDelete provides a mock function with given fields: _a0 +func (_m *Store) LogDelete(_a0 *model.Step) error { + ret := _m.Called(_a0) + + var r0 error + if rf, ok := ret.Get(0).(func(*model.Step) error); ok { + r0 = rf(_a0) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // LogFind provides a mock function with given fields: _a0 func (_m *Store) LogFind(_a0 *model.Step) ([]*model.LogEntry, error) { ret := _m.Called(_a0) diff --git a/server/store/store.go b/server/store/store.go index b5000de5653..48166010557 100644 --- a/server/store/store.go +++ b/server/store/store.go @@ -146,6 +146,7 @@ type Store interface { LogFind(*model.Step) ([]*model.LogEntry, error) LogSave(*model.Step, []*model.LogEntry) error LogAppend(logEntry *model.LogEntry) error + LogDelete(*model.Step) error // Tasks // TaskList TODO: paginate & opt filter From ea13eb05f31cee8241a30c6b61f43465c9913510 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 4 Jun 2023 20:32:02 +0200 Subject: [PATCH 09/37] new entity will get new table so we dont need to rename on migration --- server/grpc/rpc.go | 6 ++++ server/grpc/server.go | 1 + server/model/log.go | 29 ++++++++++++++----- .../migration/018_alter_logs_table.go | 9 ++---- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/server/grpc/rpc.go b/server/grpc/rpc.go index b72028dee89..bf6f2adbad2 100644 --- a/server/grpc/rpc.go +++ b/server/grpc/rpc.go @@ -314,7 +314,13 @@ func (s *RPC) Log(c context.Context, _logEntry *rpc.LogEntry) error { Time: _logEntry.Time, Line: _logEntry.Line, Data: []byte(_logEntry.Data), + Type: model.LogEntryType(_logEntry.Type), } + // write line to listening web clients + if err := s.logger.Write(c, fmt.Sprint(logEntry.StepID), logEntry); err != nil { + log.Error().Err(err).Msgf("rpc server could not write to logger") + } + // make line persistent in database return s.store.LogAppend(logEntry) } diff --git a/server/grpc/server.go b/server/grpc/server.go index 83ba0819d91..00731df5be7 100644 --- a/server/grpc/server.go +++ b/server/grpc/server.go @@ -150,6 +150,7 @@ func (s *WoodpeckerServer) Log(c context.Context, req *proto.LogRequest) (*proto Line: int(req.GetLogEntry().GetLine()), Time: req.GetLogEntry().GetTime(), StepID: req.GetLogEntry().GetStepId(), + Type: int(req.GetLogEntry().GetType()), } res := new(proto.Empty) err := s.peer.Log(c, logEntry) diff --git a/server/model/log.go b/server/model/log.go index 71f5611cb58..c10b7755ade 100644 --- a/server/model/log.go +++ b/server/model/log.go @@ -14,12 +14,27 @@ package model +// LogEntryType identifies the type of line in the logs. +type LogEntryType int + +const ( + LogEntryStdout LogEntryType = iota + LogEntryStderr + LogEntryExitCode + LogEntryMetadata + LogEntryProgress +) + type LogEntry struct { - ID int64 `json:"id" xorm:"pk autoincr 'log_id'"` - StepID int64 `json:"step_id" xorm:"UNIQUE(log) 'log_step_id'"` - Time int64 `json:"time" xorm:"'log_time'"` - Line int `json:"line" xorm:"UNIQUE(log) 'log_line'"` - Data []byte `json:"data" xorm:"LONGBLOB 'log_data'"` - Created int64 `json:"-" xorm:"created"` - // TODO: should we add type from pipeline/rpc/line.go ? + ID int64 `json:"id" xorm:"pk autoincr"` + StepID int64 `json:"step_id" xorm:"UNIQUE(log)"` + Time int64 `json:"time"` + Line int `json:"line" xorm:"UNIQUE(log)"` + Data []byte `json:"data" xorm:"LONGBLOB"` + Created int64 `json:"-" xorm:"created"` + Type LogEntryType `json:"type"` +} + +func (LogEntry) TableName() string { + return "log_entries" } diff --git a/server/store/datastore/migration/018_alter_logs_table.go b/server/store/datastore/migration/018_alter_logs_table.go index 13323962b14..f6ae05d713e 100644 --- a/server/store/datastore/migration/018_alter_logs_table.go +++ b/server/store/datastore/migration/018_alter_logs_table.go @@ -61,19 +61,14 @@ var alterLogsTable = task{ return err } - if err := renameTable(sess, "logs", "old_logs"); err != nil { - return err - } - if err := sess.Sync(new(model.LogEntry)); err != nil { return err } - // TODO: copy data over from old_logs to logs page := 0 for { var logs []*oldLogs018 - err := sess.Limit(10, page*10).Find(&logs) + err := sess.Limit(10, page*10).Table("logs").Find(&logs) if err != nil { return err } @@ -113,6 +108,6 @@ var alterLogsTable = task{ page++ } - return sess.DropTable("old_logs") + return sess.DropTable("logs") }, } From b22c4eeb27119dda36ac6fe3ebf67072c3bd10f9 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 4 Jun 2023 20:50:35 +0200 Subject: [PATCH 10/37] cleanup + fixing --- agent/rpc/client_grpc.go | 1 + pipeline/rpc/log_entry.go | 2 + server/logging/log.go | 4 +- server/logging/logging.go | 45 +++---------------- server/model/log.go | 6 ++- server/store/datastore/log.go | 2 +- server/store/datastore/log_test.go | 6 --- .../migration/018_alter_logs_table.go | 13 ------ 8 files changed, 17 insertions(+), 62 deletions(-) diff --git a/agent/rpc/client_grpc.go b/agent/rpc/client_grpc.go index 5551269ae89..5c4035fa1fb 100644 --- a/agent/rpc/client_grpc.go +++ b/agent/rpc/client_grpc.go @@ -287,6 +287,7 @@ func (c *client) Log(ctx context.Context, logEntry *rpc.LogEntry) (err error) { req.LogEntry.Data = logEntry.Data req.LogEntry.Line = int32(logEntry.Line) req.LogEntry.Time = logEntry.Time + req.LogEntry.Type = int32(logEntry.Type) for { _, err = c.client.Log(ctx, req) if err == nil { diff --git a/pipeline/rpc/log_entry.go b/pipeline/rpc/log_entry.go index c7ab8408f67..82cc961753a 100644 --- a/pipeline/rpc/log_entry.go +++ b/pipeline/rpc/log_entry.go @@ -86,6 +86,8 @@ func (w *LineWriter) Write(p []byte) (n int, err error) { StepID: w.stepID, Time: int64(time.Since(w.now).Seconds()), Type: LogEntryStdout, + // TODO: figure out a way to calc and set correct line number + Line: w.num, } if err := w.peer.Log(context.Background(), line); err != nil { log.Error().Err(err).Msgf("fail to write pipeline log to peer '%d'", w.stepID) diff --git a/server/logging/log.go b/server/logging/log.go index df587eebcea..3dc8e874600 100644 --- a/server/logging/log.go +++ b/server/logging/log.go @@ -62,9 +62,9 @@ func (l *log) Open(_ context.Context, path string) error { return nil } -func (l *log) Write(_ context.Context, path string, logEntry *model.LogEntry) error { +func (l *log) Write(_ context.Context, stepID string, logEntry *model.LogEntry) error { l.Lock() - s, ok := l.streams[path] + s, ok := l.streams[stepID] l.Unlock() if !ok { return ErrNotFound diff --git a/server/logging/logging.go b/server/logging/logging.go index ffea703b460..8ee09dfee2c 100644 --- a/server/logging/logging.go +++ b/server/logging/logging.go @@ -8,6 +8,8 @@ import ( "github.com/woodpecker-ci/woodpecker/server/model" ) +// TODO: write adapter for external pubsub provider + // ErrNotFound is returned when the log does not exist. var ErrNotFound = errors.New("stream: not found") @@ -17,53 +19,20 @@ type Handler func(...*model.LogEntry) // Log defines a log multiplexer. type Log interface { // Open opens the log. - Open(c context.Context, path string) error + Open(c context.Context, stepID string) error // Write writes the entry to the log. - Write(c context.Context, path string, entry *model.LogEntry) error + Write(c context.Context, stepID string, entry *model.LogEntry) error // Tail tails the log. - Tail(c context.Context, path string, handler Handler) error + Tail(c context.Context, stepID string, handler Handler) error // Close closes the log. - Close(c context.Context, path string) error + Close(c context.Context, stepID string) error // Snapshot snapshots the stream to Writer w. - Snapshot(c context.Context, path string, w io.Writer) error + Snapshot(c context.Context, stepID string, w io.Writer) error // Info returns runtime information about the multiplexer. // Info(c context.Context) (interface{}, error) } - -// // global streamer -// var global = New() -// -// // Set sets a default global logger. -// func Set(log Log) { -// global = log -// } -// -// // Open opens the log stream. -// func Open(c context.Context, path string) error { -// return global.Open(c, path) -// } -// -// // Write writes the log entry to the stream. -// func Write(c context.Context, path string, entry *Entry) error { -// return global.Write(c, path, entry) -// } -// -// // Tail tails the log stream. -// func Tail(c context.Context, path string, handler Handler) error { -// return global.Tail(c, path, handler) -// } -// -// // Close closes the log stream. -// func Close(c context.Context, path string) error { -// return global.Close(c, path) -// } -// -// // Snapshot snapshots the stream to Writer w. -// func Snapshot(c context.Context, path string, w io.Writer) error { -// return global.Snapshot(c, path, w) -// } diff --git a/server/model/log.go b/server/model/log.go index c10b7755ade..02daeed6f6a 100644 --- a/server/model/log.go +++ b/server/model/log.go @@ -27,14 +27,16 @@ const ( type LogEntry struct { ID int64 `json:"id" xorm:"pk autoincr"` - StepID int64 `json:"step_id" xorm:"UNIQUE(log)"` + StepID int64 `json:"step_id"` Time int64 `json:"time"` - Line int `json:"line" xorm:"UNIQUE(log)"` + Line int `json:"line"` Data []byte `json:"data" xorm:"LONGBLOB"` Created int64 `json:"-" xorm:"created"` Type LogEntryType `json:"type"` } +// TODO: store info what specific command the line belongs to (must be optional and impl. by backend) + func (LogEntry) TableName() string { return "log_entries" } diff --git a/server/store/datastore/log.go b/server/store/datastore/log.go index 53ed8a06cce..982a8ddee34 100644 --- a/server/store/datastore/log.go +++ b/server/store/datastore/log.go @@ -22,7 +22,7 @@ import ( func (s storage) LogFind(step *model.Step) ([]*model.LogEntry, error) { var logEntries []*model.LogEntry - return logEntries, s.engine.Asc("log_line").Where("log_step_id = ?", step.ID).Find(&logEntries) + return logEntries, s.engine.Where("log_step_id = ?", step.ID).Find(&logEntries) } func (s storage) LogSave(step *model.Step, logEntries []*model.LogEntry) error { diff --git a/server/store/datastore/log_test.go b/server/store/datastore/log_test.go index edb793b4914..be96a9a74a8 100644 --- a/server/store/datastore/log_test.go +++ b/server/store/datastore/log_test.go @@ -47,12 +47,6 @@ func TestLogCreateFind(t *testing.T) { // first insert should just work assert.NoError(t, store.LogSave(&step, logEntries)) - // reset id and check against unique constrains (stepID+lineNr) - for i := range logEntries { - logEntries[i].ID = 0 - } - assert.Error(t, store.LogSave(&step, logEntries)) - // we want to find our inserted logs _logEntries, err := store.LogFind(&step) assert.NoError(t, err) diff --git a/server/store/datastore/migration/018_alter_logs_table.go b/server/store/datastore/migration/018_alter_logs_table.go index f6ae05d713e..dd23e07aa38 100644 --- a/server/store/datastore/migration/018_alter_logs_table.go +++ b/server/store/datastore/migration/018_alter_logs_table.go @@ -22,19 +22,6 @@ import ( "github.com/woodpecker-ci/woodpecker/server/model" ) -// old: -// - log_id -// - log_step_id -// - log_data - -// new: -// - log_id -// - log_step_id -// - log_time -// - log_pos -// - log_data (raw text) -// - log_command (can be added later when we support executing command by command) - type oldLogs018 struct { ID int64 `xorm:"pk autoincr 'log_id'"` StepID int64 `xorm:"UNIQUE 'log_step_id'"` From 7b1890a9818dafda1045f1b86eb9816ffef1af26 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 4 Jun 2023 20:57:13 +0200 Subject: [PATCH 11/37] fix --- server/model/log.go | 2 +- server/store/datastore/log.go | 4 ++-- server/store/datastore/log_test.go | 8 +++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/server/model/log.go b/server/model/log.go index 02daeed6f6a..b1d8bc06e0f 100644 --- a/server/model/log.go +++ b/server/model/log.go @@ -27,7 +27,7 @@ const ( type LogEntry struct { ID int64 `json:"id" xorm:"pk autoincr"` - StepID int64 `json:"step_id"` + StepID int64 `json:"step_id" xorm:"'step_ID'"` Time int64 `json:"time"` Line int `json:"line"` Data []byte `json:"data" xorm:"LONGBLOB"` diff --git a/server/store/datastore/log.go b/server/store/datastore/log.go index 982a8ddee34..fa101cb64fc 100644 --- a/server/store/datastore/log.go +++ b/server/store/datastore/log.go @@ -22,7 +22,7 @@ import ( func (s storage) LogFind(step *model.Step) ([]*model.LogEntry, error) { var logEntries []*model.LogEntry - return logEntries, s.engine.Where("log_step_id = ?", step.ID).Find(&logEntries) + return logEntries, s.engine.Where("step_id = ?", step.ID).Find(&logEntries) } func (s storage) LogSave(step *model.Step, logEntries []*model.LogEntry) error { @@ -50,6 +50,6 @@ func (s storage) LogAppend(logEntry *model.LogEntry) error { } func (s storage) LogDelete(step *model.Step) error { - _, err := s.engine.Where("log_step_id = ?", step.ID).Delete(new(model.LogEntry)) + _, err := s.engine.Where("step_id = ?", step.ID).Delete(new(model.LogEntry)) return err } diff --git a/server/store/datastore/log_test.go b/server/store/datastore/log_test.go index be96a9a74a8..a4955266006 100644 --- a/server/store/datastore/log_test.go +++ b/server/store/datastore/log_test.go @@ -21,7 +21,7 @@ import ( "github.com/woodpecker-ci/woodpecker/server/model" ) -func TestLogCreateFind(t *testing.T) { +func TestLogCreateFindDelete(t *testing.T) { store, closer := newTestStore(t, new(model.Step), new(model.LogEntry)) defer closer() @@ -51,6 +51,12 @@ func TestLogCreateFind(t *testing.T) { _logEntries, err := store.LogFind(&step) assert.NoError(t, err) assert.Len(t, _logEntries, len(logEntries)) + + // delete and check + assert.NoError(t, store.LogDelete(&step)) + _logEntries, err = store.LogFind(&step) + assert.NoError(t, err) + assert.Len(t, _logEntries, 0) } func TestLogAppend(t *testing.T) { From faa70be360c779cc6eb0cae3e3d40ffae8310da2 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 4 Jun 2023 20:59:30 +0200 Subject: [PATCH 12/37] fix --- server/model/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/model/log.go b/server/model/log.go index b1d8bc06e0f..103ae1f3382 100644 --- a/server/model/log.go +++ b/server/model/log.go @@ -27,7 +27,7 @@ const ( type LogEntry struct { ID int64 `json:"id" xorm:"pk autoincr"` - StepID int64 `json:"step_id" xorm:"'step_ID'"` + StepID int64 `json:"step_id" xorm:"'step_id'"` Time int64 `json:"time"` Line int `json:"line"` Data []byte `json:"data" xorm:"LONGBLOB"` From ae3c9fc66fb97f3e526519d421dce76731c14427 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 4 Jun 2023 22:27:12 +0200 Subject: [PATCH 13/37] steps get an UUID from pipeline.Compiler --- agent/logger.go | 4 +- go.mod | 2 +- pipeline/backend/docker/convert.go | 2 +- pipeline/backend/types/step.go | 2 +- pipeline/frontend/yaml/compiler/convert.go | 5 +- pipeline/frontend/yaml/container.go | 1 - pipeline/stepBuilder.go | 7 ++- server/model/step.go | 1 + server/model/step_test.go | 4 ++ server/pipeline/filter.go | 2 +- server/store/datastore/step.go | 7 ++- server/store/datastore/step_test.go | 64 ++++++++++++++++++++++ server/store/store.go | 1 + web/src/lib/api/types/pipeline.ts | 1 + 14 files changed, 94 insertions(+), 9 deletions(-) diff --git a/agent/logger.go b/agent/logger.go index c668d245822..ee4f7dfd648 100644 --- a/agent/logger.go +++ b/agent/logger.go @@ -51,7 +51,9 @@ func (r *Runner) createLogger(_ context.Context, logger zerolog.Logger, uploads loglogger.Debug().Msg("log stream opened") // TODO: use step-id instead of work-id - logStream := rpc.NewLineWriter(r.client, work.ID, secrets...) + // TODO: IMPORTANT!!!!!!!!!! + tmp := int64(2) + logStream := rpc.NewLineWriter(r.client, tmp, secrets...) if _, err := io.Copy(logStream, part); err != nil { log.Error().Err(err).Msg("copy limited logStream part") } diff --git a/go.mod b/go.mod index 6a7eb9a2395..b3b3b7ce061 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.0 github.com/google/go-github/v39 v39.2.0 github.com/google/tink/go v1.7.0 + github.com/google/uuid v1.3.0 github.com/gorilla/securecookie v1.1.1 github.com/joho/godotenv v1.5.1 github.com/lafriks/ttlcache/v3 v3.2.0 @@ -94,7 +95,6 @@ require ( github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.3.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.2.0 // indirect github.com/hashicorp/go-retryablehttp v0.7.2 // indirect diff --git a/pipeline/backend/docker/convert.go b/pipeline/backend/docker/convert.go index c5e715c3d0f..79251a47f66 100644 --- a/pipeline/backend/docker/convert.go +++ b/pipeline/backend/docker/convert.go @@ -30,7 +30,7 @@ import ( func toConfig(step *types.Step) *container.Config { config := &container.Config{ Image: step.Image, - Labels: step.Labels, + Labels: map[string]string{"wp_uuid": step.UUID}, WorkingDir: step.WorkingDir, AttachStdout: true, AttachStderr: true, diff --git a/pipeline/backend/types/step.go b/pipeline/backend/types/step.go index de458ad9bac..7c14f127134 100644 --- a/pipeline/backend/types/step.go +++ b/pipeline/backend/types/step.go @@ -3,6 +3,7 @@ package types // Step defines a container process. type Step struct { Name string `json:"name"` + UUID string `json:"uuid"` Alias string `json:"alias,omitempty"` Image string `json:"image,omitempty"` Pull bool `json:"pull,omitempty"` @@ -10,7 +11,6 @@ type Step struct { Privileged bool `json:"privileged,omitempty"` WorkingDir string `json:"working_dir,omitempty"` Environment map[string]string `json:"environment,omitempty"` - Labels map[string]string `json:"labels,omitempty"` Entrypoint []string `json:"entrypoint,omitempty"` Commands []string `json:"commands,omitempty"` ExtraHosts []string `json:"extra_hosts,omitempty"` diff --git a/pipeline/frontend/yaml/compiler/convert.go b/pipeline/frontend/yaml/compiler/convert.go index a661f05a429..31a8aa810c9 100644 --- a/pipeline/frontend/yaml/compiler/convert.go +++ b/pipeline/frontend/yaml/compiler/convert.go @@ -6,6 +6,7 @@ import ( "path/filepath" "strings" + "github.com/google/uuid" "github.com/rs/zerolog/log" backend "github.com/woodpecker-ci/woodpecker/pipeline/backend/types" @@ -16,6 +17,8 @@ import ( func (c *Compiler) createProcess(name string, container *yaml.Container, section string) *backend.Step { var ( + uuid = uuid.New() + detached bool workingdir string @@ -157,6 +160,7 @@ func (c *Compiler) createProcess(name string, container *yaml.Container, section return &backend.Step{ Name: name, + UUID: uuid.String(), Alias: container.Name, Image: container.Image, Pull: container.Pull, @@ -164,7 +168,6 @@ func (c *Compiler) createProcess(name string, container *yaml.Container, section Privileged: privileged, WorkingDir: workingdir, Environment: environment, - Labels: container.Labels, Commands: container.Commands, ExtraHosts: container.ExtraHosts, Volumes: volumes, diff --git a/pipeline/frontend/yaml/container.go b/pipeline/frontend/yaml/container.go index bcc835bf436..54848f69a89 100644 --- a/pipeline/frontend/yaml/container.go +++ b/pipeline/frontend/yaml/container.go @@ -59,7 +59,6 @@ type ( Image string `yaml:"image,omitempty"` Failure string `yaml:"failure,omitempty"` Isolation string `yaml:"isolation,omitempty"` - Labels types.SliceorMap `yaml:"labels,omitempty"` MemLimit types.MemStringorInt `yaml:"mem_limit,omitempty"` MemSwapLimit types.MemStringorInt `yaml:"memswap_limit,omitempty"` MemSwappiness types.MemStringorInt `yaml:"mem_swappiness,omitempty"` diff --git a/pipeline/stepBuilder.go b/pipeline/stepBuilder.go index 4a55d5a9644..12339b31bc8 100644 --- a/pipeline/stepBuilder.go +++ b/pipeline/stepBuilder.go @@ -138,6 +138,7 @@ func (b *StepBuilder) Build() ([]*Item, error) { } ir, err := b.toInternalRepresentation(parsed, environ, metadata, workflow.ID) + // TODO: mark compiler return server only!!! if err != nil { return nil, err } @@ -292,6 +293,9 @@ func (b *StepBuilder) toInternalRepresentation(parsed *yaml.Config, environ map[ ).Compile(parsed) } +// SetPipelineStepsOnPipeline is the link between pipeline representation in "pipeline package" and server +// to be specific this func currently is used to convert the pipeline.Item list (crafted by StepBuilder.Build()) into +// a pipeline that can be stored in the database by the server func SetPipelineStepsOnPipeline(pipeline *model.Pipeline, pipelineItems []*Item) *model.Pipeline { var pidSequence int for _, item := range pipelineItems { @@ -310,8 +314,9 @@ func SetPipelineStepsOnPipeline(pipeline *model.Pipeline, pipelineItems []*Item) gid = pidSequence } step := &model.Step{ - PipelineID: pipeline.ID, Name: step.Alias, + UUID: step.UUID, + PipelineID: pipeline.ID, PID: pidSequence, PPID: item.Workflow.PID, PGID: gid, diff --git a/server/model/step.go b/server/model/step.go index c6492f0e6b6..450cf9fe5d3 100644 --- a/server/model/step.go +++ b/server/model/step.go @@ -31,6 +31,7 @@ type StepStore interface { // Step represents a process in the pipeline. type Step struct { ID int64 `json:"id" xorm:"pk autoincr 'step_id'"` + UUID string `json:"uuid" xorm:"UNIQUE INDEX 'step_uuid'"` PipelineID int64 `json:"pipeline_id" xorm:"UNIQUE(s) INDEX 'step_pipeline_id'"` PID int `json:"pid" xorm:"UNIQUE(s) 'step_pid'"` PPID int `json:"ppid" xorm:"step_ppid"` diff --git a/server/model/step_test.go b/server/model/step_test.go index d87654379bd..e933012f8e9 100644 --- a/server/model/step_test.go +++ b/server/model/step_test.go @@ -23,6 +23,7 @@ import ( func TestTree(t *testing.T) { steps := []*Step{{ ID: 25, + UUID: "f80df0bb-77a7-4964-9412-2e1049872d57", PID: 2, PipelineID: 6, PPID: 1, @@ -32,6 +33,7 @@ func TestTree(t *testing.T) { Error: "0", }, { ID: 24, + UUID: "c19b49c5-990d-4722-ba9c-1c4fe9db1f91", PipelineID: 6, PID: 1, PPID: 0, @@ -41,6 +43,7 @@ func TestTree(t *testing.T) { Error: "1", }, { ID: 26, + UUID: "4380146f-c0ff-4482-8107-c90937d1faba", PipelineID: 6, PID: 3, PPID: 1, @@ -56,6 +59,7 @@ func TestTree(t *testing.T) { steps = []*Step{{ ID: 25, + UUID: "f80df0bb-77a7-4964-9412-2e1049872d57", PID: 2, PipelineID: 6, PPID: 1, diff --git a/server/pipeline/filter.go b/server/pipeline/filter.go index 56458ba9342..7abfcd7f6a9 100644 --- a/server/pipeline/filter.go +++ b/server/pipeline/filter.go @@ -49,7 +49,7 @@ func zeroSteps(currentPipeline *model.Pipeline, forgeYamlConfigs []*forge_types. return false } -// TODO: parse yaml once and not for each filter function +// TODO: parse yaml once and not for each filter function (-> move server/pipeline/filter* into pipeline/step_builder) // Check if at least one pipeline step will be execute otherwise we will just ignore this webhook func checkIfFiltered(pipeline *model.Pipeline, forgeYamlConfigs []*forge_types.FileMeta) (bool, error) { log.Trace().Msgf("hook.branchFiltered(): pipeline branch: '%s' pipeline event: '%s' config count: %d", pipeline.Branch, pipeline.Event, len(forgeYamlConfigs)) diff --git a/server/store/datastore/step.go b/server/store/datastore/step.go index 27b0266e9a1..4df262e0381 100644 --- a/server/store/datastore/step.go +++ b/server/store/datastore/step.go @@ -33,6 +33,11 @@ func (s storage) StepFind(pipeline *model.Pipeline, pid int) (*model.Step, error return step, wrapGet(s.engine.Get(step)) } +func (s storage) StepByUUID(uuid string) (*model.Step, error) { + step := new(model.Step) + return step, wrapGet(s.engine.Where("step_uuid = ?", uuid).Get(step)) +} + func (s storage) StepChild(pipeline *model.Pipeline, ppid int, child string) (*model.Step, error) { step := &model.Step{ PipelineID: pipeline.ID, @@ -87,7 +92,7 @@ func (s storage) StepClear(pipeline *model.Pipeline) error { } func deleteStep(sess *xorm.Session, stepID int64) error { - if _, err := sess.Where("log_step_id = ?", stepID).Delete(new(model.LogEntry)); err != nil { + if _, err := sess.Where("step_id = ?", stepID).Delete(new(model.LogEntry)); err != nil { return err } _, err := sess.ID(stepID).Delete(new(model.Step)) diff --git a/server/store/datastore/step_test.go b/server/store/datastore/step_test.go index d8f5f47b363..88bf434355f 100644 --- a/server/store/datastore/step_test.go +++ b/server/store/datastore/step_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/woodpecker-ci/woodpecker/server/model" + "github.com/woodpecker-ci/woodpecker/server/store/types" ) func TestStepFind(t *testing.T) { @@ -29,6 +30,7 @@ func TestStepFind(t *testing.T) { steps := []*model.Step{ { + UUID: "8d89104f-d44e-4b45-b86e-17f8b5e74a0e", PipelineID: 1000, PID: 1, PPID: 2, @@ -59,6 +61,7 @@ func TestStepChild(t *testing.T) { err := store.StepCreate([]*model.Step{ { + UUID: "ea6d4008-8ace-4f8a-ad03-53f1756465d9", PipelineID: 1, PID: 1, PPID: 1, @@ -66,6 +69,7 @@ func TestStepChild(t *testing.T) { State: "success", }, { + UUID: "2bf387f7-2913-4907-814c-c9ada88707c0", PipelineID: 1, PID: 2, PGID: 2, @@ -98,6 +102,7 @@ func TestStepList(t *testing.T) { err := store.StepCreate([]*model.Step{ { + UUID: "2bf387f7-2913-4907-814c-c9ada88707c0", PipelineID: 2, PID: 1, PPID: 1, @@ -105,6 +110,7 @@ func TestStepList(t *testing.T) { State: "success", }, { + UUID: "4b04073c-1827-4aa4-a5f5-c7b21c5e44a6", PipelineID: 1, PID: 1, PPID: 1, @@ -112,6 +118,7 @@ func TestStepList(t *testing.T) { State: "success", }, { + UUID: "40aab045-970b-4892-b6df-6f825a7ec97a", PipelineID: 1, PID: 2, PGID: 2, @@ -139,6 +146,7 @@ func TestStepUpdate(t *testing.T) { defer closer() step := &model.Step{ + UUID: "fc7c7fd6-553e-480b-8ed7-30d8563d0b79", PipelineID: 1, PID: 1, PPID: 2, @@ -176,6 +184,7 @@ func TestStepIndexes(t *testing.T) { if err := store.StepCreate([]*model.Step{ { + UUID: "4db7e5fc-5312-4d02-9e14-b51b9e3242cc", PipelineID: 1, PID: 1, PPID: 1, @@ -191,6 +200,7 @@ func TestStepIndexes(t *testing.T) { // fail due to duplicate pid if err := store.StepCreate([]*model.Step{ { + UUID: "c1f33a9e-2a02-4579-95ec-90255d785a12", PipelineID: 1, PID: 1, PPID: 1, @@ -201,6 +211,60 @@ func TestStepIndexes(t *testing.T) { }); err == nil { t.Errorf("Unexpected error: duplicate pid") } + + // fail due to duplicate uuid + if err := store.StepCreate([]*model.Step{ + { + UUID: "4db7e5fc-5312-4d02-9e14-b51b9e3242cc", + PipelineID: 5, + PID: 4, + PPID: 3, + PGID: 2, + State: "success", + Name: "clone", + }, + }); err == nil { + t.Errorf("Unexpected error: duplicate pid") + } +} + +func TestStepByUUID(t *testing.T) { + store, closer := newTestStore(t, new(model.Step), new(model.Pipeline)) + defer closer() + + assert.NoError(t, store.StepCreate([]*model.Step{ + { + UUID: "4db7e5fc-5312-4d02-9e14-b51b9e3242cc", + PipelineID: 1, + PID: 1, + PPID: 1, + PGID: 1, + State: "running", + Name: "build", + }, + { + UUID: "fc7c7fd6-553e-480b-8ed7-30d8563d0b79", + PipelineID: 4, + PID: 6, + PPID: 7, + PGID: 8, + Name: "build", + State: "pending", + Error: "pc load letter", + ExitCode: 255, + AgentID: 1, + Platform: "linux/amd64", + Environ: map[string]string{"GOLANG": "tip"}, + }, + })) + + step, err := store.StepByUUID("4db7e5fc-5312-4d02-9e14-b51b9e3242cc") + assert.NoError(t, err) + assert.NotEmpty(t, step) + + step, err = store.StepByUUID("52feb6f5-8ce2-40c0-9937-9d0e3349c98c") + assert.ErrorIs(t, err, types.RecordNotExist) + assert.Empty(t, step) } // TODO: func TestStepCascade(t *testing.T) {} diff --git a/server/store/store.go b/server/store/store.go index 48166010557..b76060d45fe 100644 --- a/server/store/store.go +++ b/server/store/store.go @@ -136,6 +136,7 @@ type Store interface { // Steps StepLoad(int64) (*model.Step, error) StepFind(*model.Pipeline, int) (*model.Step, error) + StepByUUID(string) (*model.Step, error) StepChild(*model.Pipeline, int, string) (*model.Step, error) StepList(*model.Pipeline) ([]*model.Step, error) StepCreate([]*model.Step) error diff --git a/web/src/lib/api/types/pipeline.ts b/web/src/lib/api/types/pipeline.ts index 348358e67b6..90a4f96b69c 100644 --- a/web/src/lib/api/types/pipeline.ts +++ b/web/src/lib/api/types/pipeline.ts @@ -123,6 +123,7 @@ export type PipelineLog = { time: number; line: number; data: string; + type: number; }; export type PipelineFeed = Pipeline & { From a07234f708ec27c44af205823cb7c5dc2437ca0b Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 4 Jun 2023 22:34:15 +0200 Subject: [PATCH 14/37] workflow need a uuid to atm ... show they have one in future too? --- pipeline/stepBuilder.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pipeline/stepBuilder.go b/pipeline/stepBuilder.go index 12339b31bc8..ebfcb7ce952 100644 --- a/pipeline/stepBuilder.go +++ b/pipeline/stepBuilder.go @@ -22,6 +22,7 @@ import ( "strings" "github.com/drone/envsubst" + "github.com/google/uuid" "github.com/oklog/ulid/v2" "github.com/rs/zerolog/log" @@ -77,6 +78,7 @@ func (b *StepBuilder) Build() ([]*Item, error) { for _, axis := range axes { workflow := &model.Step{ + UUID: uuid.New().String(), // TODO(#1784): Remove once workflows are a separate entity in database PipelineID: b.Curr.ID, PID: pidSequence, PGID: pidSequence, From fae1e52fa11f175d171b4e287bc9522e6066a80d Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 4 Jun 2023 23:13:01 +0200 Subject: [PATCH 15/37] for logs: - server <-grpc-> agent = StepUUID - api <-rest/stream-> server = StepID --- agent/logger.go | 5 +- agent/rpc/client_grpc.go | 5 +- pipeline/rpc/log_entry.go | 62 +++---- pipeline/rpc/log_entry_test.go | 8 +- pipeline/rpc/proto/woodpecker.pb.go | 258 ++++++++++++++-------------- pipeline/rpc/proto/woodpecker.proto | 2 +- pipeline/stepBuilder.go | 1 - server/grpc/rpc.go | 18 +- server/grpc/server.go | 10 +- server/logging/logging.go | 3 - server/store/mocks/store.go | 26 +++ 11 files changed, 207 insertions(+), 191 deletions(-) diff --git a/agent/logger.go b/agent/logger.go index ee4f7dfd648..ae9b3445ad6 100644 --- a/agent/logger.go +++ b/agent/logger.go @@ -50,10 +50,7 @@ func (r *Runner) createLogger(_ context.Context, logger zerolog.Logger, uploads loglogger.Debug().Msg("log stream opened") - // TODO: use step-id instead of work-id - // TODO: IMPORTANT!!!!!!!!!! - tmp := int64(2) - logStream := rpc.NewLineWriter(r.client, tmp, secrets...) + logStream := rpc.NewLineWriter(r.client, step.UUID, secrets...) if _, err := io.Copy(logStream, part); err != nil { log.Error().Err(err).Msg("copy limited logStream part") } diff --git a/agent/rpc/client_grpc.go b/agent/rpc/client_grpc.go index 5c4035fa1fb..a095cba0c6d 100644 --- a/agent/rpc/client_grpc.go +++ b/agent/rpc/client_grpc.go @@ -17,7 +17,6 @@ package rpc import ( "context" "encoding/json" - "strconv" "strings" "time" @@ -281,9 +280,9 @@ func (c *client) Update(ctx context.Context, id string, state rpc.State) (err er // Log writes the pipeline log entry. func (c *client) Log(ctx context.Context, logEntry *rpc.LogEntry) (err error) { req := new(proto.LogRequest) - req.Id = strconv.FormatInt(logEntry.StepID, 10) + req.Id = logEntry.StepUUID req.LogEntry = new(proto.LogEntry) - req.LogEntry.StepId = logEntry.StepID + req.LogEntry.StepUuid = logEntry.StepUUID req.LogEntry.Data = logEntry.Data req.LogEntry.Line = int32(logEntry.Line) req.LogEntry.Time = logEntry.Time diff --git a/pipeline/rpc/log_entry.go b/pipeline/rpc/log_entry.go index 82cc961753a..6b0c37ef52e 100644 --- a/pipeline/rpc/log_entry.go +++ b/pipeline/rpc/log_entry.go @@ -37,40 +37,40 @@ const ( // Line is a line of console output. type LogEntry struct { - StepID int64 `json:"step_id,omitempty"` - Time int64 `json:"time,omitempty"` - Type int `json:"type,omitempty"` - Line int `json:"line,omitempty"` - Data string `json:"data,omitempty"` + StepUUID string `json:"step_uuid,omitempty"` + Time int64 `json:"time,omitempty"` + Type int `json:"type,omitempty"` + Line int `json:"line,omitempty"` + Data string `json:"data,omitempty"` } func (l *LogEntry) String() string { switch l.Type { case LogEntryExitCode: - return fmt.Sprintf("[%d] exit code %s", l.StepID, l.Data) + return fmt.Sprintf("[%s] exit code %s", l.StepUUID, l.Data) default: - return fmt.Sprintf("[%d:L%v:%vs] %s", l.StepID, l.Line, l.Time, l.Data) + return fmt.Sprintf("[%s:L%v:%vs] %s", l.StepUUID, l.Line, l.Time, l.Data) } } // LineWriter sends logs to the client. type LineWriter struct { - peer Peer - stepID int64 - num int - now time.Time - rep *strings.Replacer - lines []*LogEntry + peer Peer + stepUUID string + num int + now time.Time + rep *strings.Replacer + lines []*LogEntry } // NewLineWriter returns a new line reader. -func NewLineWriter(peer Peer, stepID int64, secret ...string) *LineWriter { +func NewLineWriter(peer Peer, stepUUID string, secret ...string) *LineWriter { return &LineWriter{ - peer: peer, - stepID: stepID, - now: time.Now().UTC(), - rep: shared.NewSecretsReplacer(secret), - lines: nil, + peer: peer, + stepUUID: stepUUID, + now: time.Now().UTC(), + rep: shared.NewSecretsReplacer(secret), + lines: nil, } } @@ -79,32 +79,22 @@ func (w *LineWriter) Write(p []byte) (n int, err error) { if w.rep != nil { data = w.rep.Replace(data) } - log.Trace().Int64("step-id", w.stepID).Msgf("grpc write line: %s", data) + log.Trace().Str("step-uuid", w.stepUUID).Msgf("grpc write line: %s", data) + // TODO: split p by "\n" to create a new entry for each line? line := &LogEntry{ - Data: data, - StepID: w.stepID, - Time: int64(time.Since(w.now).Seconds()), - Type: LogEntryStdout, + Data: data, + StepUUID: w.stepUUID, + Time: int64(time.Since(w.now).Seconds()), + Type: LogEntryStdout, // TODO: figure out a way to calc and set correct line number Line: w.num, } if err := w.peer.Log(context.Background(), line); err != nil { - log.Error().Err(err).Msgf("fail to write pipeline log to peer '%d'", w.stepID) + log.Error().Err(err).Str("step-uuid", w.stepUUID).Msg("fail to write pipeline log to peer") } w.num++ - // for _, part := range bytes.Split(p, []byte{'\n'}) { - // line := &Line{ - // Out: string(part), - // Step: w.name, - // Pos: w.num, - // Time: int64(time.Since(w.now).Seconds()), - // Type: LineStdout, - // } - // w.peer.Log(context.Background(), w.id, line) - // w.num++ - // } w.lines = append(w.lines, line) return len(p), nil } diff --git a/pipeline/rpc/log_entry_test.go b/pipeline/rpc/log_entry_test.go index 6fd1bc0cedc..84d3d300d2e 100644 --- a/pipeline/rpc/log_entry_test.go +++ b/pipeline/rpc/log_entry_test.go @@ -20,10 +20,10 @@ import ( func TestLogEntry(t *testing.T) { line := LogEntry{ - StepID: int64(123), - Time: 60, - Line: 1, - Data: "starting redis server", + StepUUID: "e9ea76a5-44a1-4059-9c4a-6956c478b26d", + Time: 60, + Line: 1, + Data: "starting redis server", } got, want := line.String(), "[redis:L1:60s] starting redis server" if got != want { diff --git a/pipeline/rpc/proto/woodpecker.pb.go b/pipeline/rpc/proto/woodpecker.pb.go index 3ade4f19159..28d7213c677 100644 --- a/pipeline/rpc/proto/woodpecker.pb.go +++ b/pipeline/rpc/proto/woodpecker.pb.go @@ -127,11 +127,11 @@ type LogEntry struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - StepId int64 `protobuf:"varint,1,opt,name=step_id,json=stepId,proto3" json:"step_id,omitempty"` - Time int64 `protobuf:"varint,2,opt,name=time,proto3" json:"time,omitempty"` - Line int32 `protobuf:"varint,3,opt,name=line,proto3" json:"line,omitempty"` - Type int32 `protobuf:"varint,4,opt,name=type,proto3" json:"type,omitempty"` // 0 = stdout, 1 = stderr, 2 = exit-code, 3 = metadata, 4 = progress - Data string `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` + StepUuid string `protobuf:"bytes,1,opt,name=step_uuid,json=stepUuid,proto3" json:"step_uuid,omitempty"` + Time int64 `protobuf:"varint,2,opt,name=time,proto3" json:"time,omitempty"` + Line int32 `protobuf:"varint,3,opt,name=line,proto3" json:"line,omitempty"` + Type int32 `protobuf:"varint,4,opt,name=type,proto3" json:"type,omitempty"` // 0 = stdout, 1 = stderr, 2 = exit-code, 3 = metadata, 4 = progress + Data string `protobuf:"bytes,5,opt,name=data,proto3" json:"data,omitempty"` } func (x *LogEntry) Reset() { @@ -166,11 +166,11 @@ func (*LogEntry) Descriptor() ([]byte, []int) { return file_woodpecker_proto_rawDescGZIP(), []int{1} } -func (x *LogEntry) GetStepId() int64 { +func (x *LogEntry) GetStepUuid() string { if x != nil { - return x.StepId + return x.StepUuid } - return 0 + return "" } func (x *LogEntry) GetTime() int64 { @@ -1109,129 +1109,129 @@ var file_woodpecker_proto_rawDesc = []byte{ 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x73, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x17, 0x0a, 0x07, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x73, 0x74, 0x65, 0x70, 0x49, 0x64, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x74, 0x69, 0x6d, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x76, 0x0a, - 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, - 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4e, 0x0a, 0x08, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, - 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, - 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, - 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x34, 0x0a, 0x0b, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, - 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x41, 0x0a, 0x0b, 0x49, - 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x05, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x1d, - 0x0a, 0x0b, 0x57, 0x61, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x41, 0x0a, - 0x0b, 0x44, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x22, 0x1f, 0x0a, 0x0d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x77, 0x0a, 0x08, 0x4c, 0x6f, 0x67, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x74, 0x65, 0x70, 0x5f, 0x75, 0x75, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x74, 0x65, 0x70, 0x55, 0x75, 0x69, + 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x04, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x22, 0x76, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x06, 0x6c, + 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x4c, 0x61, 0x62, 0x65, 0x6c, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x1a, 0x39, + 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x4e, 0x0a, 0x08, 0x50, 0x69, 0x70, + 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, + 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x34, 0x0a, 0x0b, 0x4e, 0x65, 0x78, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x25, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, + 0x41, 0x0a, 0x0b, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, + 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x22, 0x1d, 0x0a, 0x0b, 0x57, 0x61, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, - 0x64, 0x22, 0x43, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, - 0x69, 0x64, 0x12, 0x22, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, - 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x49, 0x0a, 0x0a, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, - 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x2d, 0x0a, 0x13, 0x52, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x82, 0x01, 0x0a, 0x14, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x1a, - 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, - 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, - 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x61, 0x63, - 0x6b, 0x65, 0x6e, 0x64, 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, 0x22, 0x5b, - 0x0a, 0x0f, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x67, 0x72, 0x70, 0x63, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x76, - 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x3b, 0x0a, 0x0c, 0x4e, - 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x08, 0x70, - 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x52, 0x08, - 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x32, 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x49, 0x0a, 0x0b, - 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x61, - 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0a, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x19, 0x0a, 0x08, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x64, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0x8a, 0x04, - 0x0a, 0x0a, 0x57, 0x6f, 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x12, 0x31, 0x0a, 0x07, - 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, - 0x31, 0x0a, 0x04, 0x4e, 0x65, 0x78, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, + 0x64, 0x22, 0x41, 0x0a, 0x0b, 0x44, 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, + 0x12, 0x22, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x22, 0x1f, 0x0a, 0x0d, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x43, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x49, 0x0a, 0x0a, 0x4c, 0x6f, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6c, 0x6f, 0x67, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x2d, + 0x0a, 0x13, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x82, 0x01, + 0x0a, 0x14, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, + 0x72, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, + 0x72, 0x6d, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x18, + 0x0a, 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 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, 0x22, 0x5b, 0x0a, 0x0f, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x76, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x67, 0x72, 0x70, + 0x63, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, + 0x3b, 0x0a, 0x0c, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x2b, 0x0a, 0x08, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, + 0x6e, 0x65, 0x52, 0x08, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x22, 0x32, 0x0a, 0x15, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, + 0x22, 0x49, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x1f, 0x0a, 0x0b, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, + 0x12, 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x64, 0x0a, 0x0c, 0x41, + 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x21, + 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, + 0x6e, 0x32, 0x8a, 0x04, 0x0a, 0x0a, 0x57, 0x6f, 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, + 0x12, 0x31, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0c, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x12, 0x31, 0x0a, 0x04, 0x4e, 0x65, 0x78, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4e, 0x65, 0x78, 0x74, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x12, 0x12, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x04, 0x57, 0x61, 0x69, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x57, 0x61, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2a, - 0x0a, 0x04, 0x57, 0x61, 0x69, 0x74, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, - 0x61, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2a, 0x0a, 0x04, 0x44, 0x6f, - 0x6e, 0x65, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, 0x6f, 0x6e, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x06, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, - 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x28, 0x0a, 0x03, 0x4c, 0x6f, 0x67, 0x12, 0x11, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, - 0x12, 0x4c, 0x0a, 0x0d, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, - 0x74, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1c, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, - 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, - 0x0a, 0x0c, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x12, 0x1a, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, - 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x32, 0x43, 0x0a, 0x0e, 0x57, 0x6f, - 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, 0x12, 0x31, 0x0a, 0x04, - 0x41, 0x75, 0x74, 0x68, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74, - 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, - 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x77, 0x6f, - 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x2d, 0x63, 0x69, 0x2f, 0x77, 0x6f, 0x6f, 0x64, - 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x2f, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x2f, - 0x72, 0x70, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x33, + 0x0a, 0x04, 0x44, 0x6f, 0x6e, 0x65, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x44, + 0x6f, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x06, 0x45, 0x78, + 0x74, 0x65, 0x6e, 0x64, 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x78, 0x74, + 0x65, 0x6e, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x2e, 0x0a, 0x06, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x12, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x28, 0x0a, 0x03, 0x4c, 0x6f, + 0x67, 0x12, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x22, 0x00, 0x12, 0x4c, 0x0a, 0x0d, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0c, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, + 0x74, 0x68, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x32, 0x43, + 0x0a, 0x0e, 0x57, 0x6f, 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x41, 0x75, 0x74, 0x68, + 0x12, 0x31, 0x0a, 0x04, 0x41, 0x75, 0x74, 0x68, 0x12, 0x12, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x00, 0x42, 0x38, 0x5a, 0x36, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x77, 0x6f, 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x2d, 0x63, 0x69, 0x2f, + 0x77, 0x6f, 0x6f, 0x64, 0x70, 0x65, 0x63, 0x6b, 0x65, 0x72, 0x2f, 0x70, 0x69, 0x70, 0x65, 0x6c, + 0x69, 0x6e, 0x65, 0x2f, 0x72, 0x70, 0x63, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pipeline/rpc/proto/woodpecker.proto b/pipeline/rpc/proto/woodpecker.proto index 4dbf0080be4..fe756b10b4a 100644 --- a/pipeline/rpc/proto/woodpecker.proto +++ b/pipeline/rpc/proto/woodpecker.proto @@ -50,7 +50,7 @@ message State { } message LogEntry { - int64 step_id = 1; + string step_uuid = 1; int64 time = 2; int32 line = 3; int32 type = 4; // 0 = stdout, 1 = stderr, 2 = exit-code, 3 = metadata, 4 = progress diff --git a/pipeline/stepBuilder.go b/pipeline/stepBuilder.go index ebfcb7ce952..3efab787430 100644 --- a/pipeline/stepBuilder.go +++ b/pipeline/stepBuilder.go @@ -140,7 +140,6 @@ func (b *StepBuilder) Build() ([]*Item, error) { } ir, err := b.toInternalRepresentation(parsed, environ, metadata, workflow.ID) - // TODO: mark compiler return server only!!! if err != nil { return nil, err } diff --git a/server/grpc/rpc.go b/server/grpc/rpc.go index bf6f2adbad2..db969b24842 100644 --- a/server/grpc/rpc.go +++ b/server/grpc/rpc.go @@ -309,17 +309,25 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error { // Log implements the rpc.Log function func (s *RPC) Log(c context.Context, _logEntry *rpc.LogEntry) error { + // convert rpc log_entry to model.log_entry + step, err := s.store.StepByUUID(_logEntry.StepUUID) + if err != nil { + return fmt.Errorf("could not find step with uuid %s in store: %w", _logEntry.StepUUID, err) + } logEntry := &model.LogEntry{ - StepID: _logEntry.StepID, + StepID: step.ID, Time: _logEntry.Time, Line: _logEntry.Line, Data: []byte(_logEntry.Data), Type: model.LogEntryType(_logEntry.Type), } - // write line to listening web clients - if err := s.logger.Write(c, fmt.Sprint(logEntry.StepID), logEntry); err != nil { - log.Error().Err(err).Msgf("rpc server could not write to logger") - } + // make sure writes to pubsub are non blocking (https://github.com/woodpecker-ci/woodpecker/blob/c919f32e0b6432a95e1a6d3d0ad662f591adf73f/server/logging/log.go#L9) + go func() { + // write line to listening web clients + if err := s.logger.Write(c, fmt.Sprint(logEntry.StepID), logEntry); err != nil { + log.Error().Err(err).Msgf("rpc server could not write to logger") + } + }() // make line persistent in database return s.store.LogAppend(logEntry) } diff --git a/server/grpc/server.go b/server/grpc/server.go index 00731df5be7..f7c98059bfe 100644 --- a/server/grpc/server.go +++ b/server/grpc/server.go @@ -146,11 +146,11 @@ func (s *WoodpeckerServer) Extend(c context.Context, req *proto.ExtendRequest) ( func (s *WoodpeckerServer) Log(c context.Context, req *proto.LogRequest) (*proto.Empty, error) { logEntry := &rpc.LogEntry{ - Data: req.GetLogEntry().GetData(), - Line: int(req.GetLogEntry().GetLine()), - Time: req.GetLogEntry().GetTime(), - StepID: req.GetLogEntry().GetStepId(), - Type: int(req.GetLogEntry().GetType()), + Data: req.GetLogEntry().GetData(), + Line: int(req.GetLogEntry().GetLine()), + Time: req.GetLogEntry().GetTime(), + StepUUID: req.GetLogEntry().GetStepUuid(), + Type: int(req.GetLogEntry().GetType()), } res := new(proto.Empty) err := s.peer.Log(c, logEntry) diff --git a/server/logging/logging.go b/server/logging/logging.go index 8ee09dfee2c..6c3b648184c 100644 --- a/server/logging/logging.go +++ b/server/logging/logging.go @@ -32,7 +32,4 @@ type Log interface { // Snapshot snapshots the stream to Writer w. Snapshot(c context.Context, stepID string, w io.Writer) error - - // Info returns runtime information about the multiplexer. - // Info(c context.Context) (interface{}, error) } diff --git a/server/store/mocks/store.go b/server/store/mocks/store.go index e7d45cd977b..4300f418e0b 100644 --- a/server/store/mocks/store.go +++ b/server/store/mocks/store.go @@ -1635,6 +1635,32 @@ func (_m *Store) ServerConfigSet(_a0 string, _a1 string) error { return r0 } +// StepByUUID provides a mock function with given fields: _a0 +func (_m *Store) StepByUUID(_a0 string) (*model.Step, error) { + ret := _m.Called(_a0) + + var r0 *model.Step + var r1 error + if rf, ok := ret.Get(0).(func(string) (*model.Step, error)); ok { + return rf(_a0) + } + if rf, ok := ret.Get(0).(func(string) *model.Step); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*model.Step) + } + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // StepChild provides a mock function with given fields: _a0, _a1, _a2 func (_m *Store) StepChild(_a0 *model.Pipeline, _a1 int, _a2 string) (*model.Step, error) { ret := _m.Called(_a0, _a1, _a2) From 716503dddbfd66e431aa23a2057087268a29de3e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 4 Jun 2023 23:14:05 +0200 Subject: [PATCH 16/37] gen cods --- cmd/server/docs/docs.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index f2106a37467..2abd3fd404c 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -4233,6 +4233,9 @@ const docTemplate = `{ }, "state": { "$ref": "#/definitions/StatusValue" + }, + "uuid": { + "type": "string" } } }, From 5d4f34af19de4053a5768752b61c30aa866949a8 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 4 Jun 2023 23:20:36 +0200 Subject: [PATCH 17/37] document issue --- server/logging/logging.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/logging/logging.go b/server/logging/logging.go index 6c3b648184c..8d83c3cadc3 100644 --- a/server/logging/logging.go +++ b/server/logging/logging.go @@ -8,7 +8,7 @@ import ( "github.com/woodpecker-ci/woodpecker/server/model" ) -// TODO: write adapter for external pubsub provider +// TODO(#742): write adapter for external pubsub provider // ErrNotFound is returned when the log does not exist. var ErrNotFound = errors.New("stream: not found") From 46ca1ae1961da91e4bb7f85eda47fdacd3d5feaa Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 4 Jun 2023 23:27:07 +0200 Subject: [PATCH 18/37] clean unused --- agent/rpc/client_grpc.go | 1 - pipeline/rpc/proto/woodpecker.pb.go | 17 ++++------------- pipeline/rpc/proto/woodpecker.proto | 3 +-- 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/agent/rpc/client_grpc.go b/agent/rpc/client_grpc.go index a095cba0c6d..cda47713226 100644 --- a/agent/rpc/client_grpc.go +++ b/agent/rpc/client_grpc.go @@ -280,7 +280,6 @@ func (c *client) Update(ctx context.Context, id string, state rpc.State) (err er // Log writes the pipeline log entry. func (c *client) Log(ctx context.Context, logEntry *rpc.LogEntry) (err error) { req := new(proto.LogRequest) - req.Id = logEntry.StepUUID req.LogEntry = new(proto.LogEntry) req.LogEntry.StepUuid = logEntry.StepUUID req.LogEntry.Data = logEntry.Data diff --git a/pipeline/rpc/proto/woodpecker.pb.go b/pipeline/rpc/proto/woodpecker.pb.go index 28d7213c677..23027075075 100644 --- a/pipeline/rpc/proto/woodpecker.pb.go +++ b/pipeline/rpc/proto/woodpecker.pb.go @@ -622,8 +622,7 @@ type LogRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - LogEntry *LogEntry `protobuf:"bytes,2,opt,name=logEntry,proto3" json:"logEntry,omitempty"` + LogEntry *LogEntry `protobuf:"bytes,1,opt,name=logEntry,proto3" json:"logEntry,omitempty"` } func (x *LogRequest) Reset() { @@ -658,13 +657,6 @@ func (*LogRequest) Descriptor() ([]byte, []int) { return file_woodpecker_proto_rawDescGZIP(), []int{10} } -func (x *LogRequest) GetId() string { - if x != nil { - return x.Id - } - return "" -} - func (x *LogRequest) GetLogEntry() *LogEntry { if x != nil { return x.LogEntry @@ -1149,10 +1141,9 @@ var file_woodpecker_proto_rawDesc = []byte{ 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x22, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x49, 0x0a, 0x0a, 0x4c, 0x6f, - 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2b, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, + 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x22, 0x39, 0x0a, 0x0a, 0x4c, 0x6f, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2b, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x2d, 0x0a, 0x13, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x52, 0x65, diff --git a/pipeline/rpc/proto/woodpecker.proto b/pipeline/rpc/proto/woodpecker.proto index fe756b10b4a..5603826fbcd 100644 --- a/pipeline/rpc/proto/woodpecker.proto +++ b/pipeline/rpc/proto/woodpecker.proto @@ -99,8 +99,7 @@ message UpdateRequest { } message LogRequest { - string id = 1; - LogEntry logEntry = 2; + LogEntry logEntry = 1; } message Empty { From 0d0bdefdf213202eb3c4ab0ad96ccdbfc4fdb9eb Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 00:19:36 +0200 Subject: [PATCH 19/37] adjust cli --- cli/exec/exec.go | 2 +- cli/exec/line.go | 36 ++++++++++++++++++------------------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/cli/exec/exec.go b/cli/exec/exec.go index 26287d1d8b5..78b6649e1b5 100644 --- a/cli/exec/exec.go +++ b/cli/exec/exec.go @@ -324,7 +324,7 @@ var defaultLogger = pipeline.LogFunc(func(step *backendTypes.Step, rc multipart. return err } - logStream := NewLineWriter(step.Alias) + logStream := NewLineWriter(step.Alias, step.UUID) _, err = io.Copy(logStream, part) return err }) diff --git a/cli/exec/line.go b/cli/exec/line.go index ae4acd8caf9..7ce9d59276f 100644 --- a/cli/exec/line.go +++ b/cli/exec/line.go @@ -25,21 +25,21 @@ import ( // LineWriter sends logs to the client. type LineWriter struct { - stepID int64 - num int - now time.Time - rep *strings.Replacer - lines []*rpc.LogEntry + stepName string + stepUUID string + num int + now time.Time + rep *strings.Replacer + lines []*rpc.LogEntry } // NewLineWriter returns a new line reader. -func NewLineWriter(stepID int64) *LineWriter { - w := new(LineWriter) - w.stepID = stepID - w.num = 0 - w.now = time.Now().UTC() - - return w +func NewLineWriter(stepName, stepUUID string) *LineWriter { + return &LineWriter{ + stepName: stepName, + stepUUID: stepUUID, + now: time.Now().UTC(), + } } func (w *LineWriter) Write(p []byte) (n int, err error) { @@ -49,14 +49,14 @@ func (w *LineWriter) Write(p []byte) (n int, err error) { } line := &rpc.LogEntry{ - Data: data, - StepID: w.stepID, - Line: w.num, - Time: int64(time.Since(w.now).Seconds()), - Type: rpc.LogEntryStdout, + Data: data, + StepUUID: w.stepUUID, + Line: w.num, + Time: int64(time.Since(w.now).Seconds()), + Type: rpc.LogEntryStdout, } - fmt.Fprintf(os.Stderr, "[%d:L%d:%ds] %s", w.stepID, w.num, int64(time.Since(w.now).Seconds()), data) + fmt.Fprintf(os.Stderr, "[%s:L%d:%ds] %s", w.stepName, w.num, int64(time.Since(w.now).Seconds()), data) w.num++ From b5783119348613043b0b4b81ed6dbb3ffb49d79e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 01:18:50 +0200 Subject: [PATCH 20/37] adjust log streaming code so use stepID --- server/api/stream.go | 8 +++---- server/grpc/rpc.go | 36 ++++++++++++++++++------------- server/logging/log.go | 44 ++++++++++++++++++-------------------- server/logging/log_test.go | 16 +++++++------- server/logging/logging.go | 10 ++++----- server/pipeline/cancel.go | 17 +++++---------- server/pipeline/queue.go | 3 --- server/pipeline/start.go | 11 ++++++++++ 8 files changed, 74 insertions(+), 71 deletions(-) diff --git a/server/api/stream.go b/server/api/stream.go index 66e55cbc579..0f0a84b1dd2 100644 --- a/server/api/stream.go +++ b/server/api/stream.go @@ -17,7 +17,6 @@ package api import ( "context" - "fmt" "io" "net/http" "strconv" @@ -167,11 +166,10 @@ func LogStreamSSE(c *gin.Context) { }() go func() { - // TODO remove global variable - err := server.Config.Services.Logs.Tail(ctx, fmt.Sprint(step.ID), func(entries ...*model.LogEntry) { + err := server.Config.Services.Logs.Tail(ctx, step.ID, func(entries ...*model.LogEntry) { defer func() { - obj := recover() // fix #2480 // TODO: check if it's still needed - log.Trace().Msgf("pubsub subscribe recover return: %v", obj) + obj := recover() // TODO: check if it's still needed + log.Error().Msgf("## IF YOU SEE THIS OPEN AN ISSUE UPSTREAM ## - pubsub subscribe recover return: %v", obj) }() for _, entry := range entries { select { diff --git a/server/grpc/rpc.go b/server/grpc/rpc.go index db969b24842..3d750309f8a 100644 --- a/server/grpc/rpc.go +++ b/server/grpc/rpc.go @@ -238,40 +238,41 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error { workflow, err := s.store.StepLoad(workflowID) if err != nil { - log.Error().Msgf("error: cannot find step with id %d: %s", workflowID, err) + log.Error().Err(err).Msgf("cannot find step with id %d", workflowID) return err } currentPipeline, err := s.store.GetPipeline(workflow.PipelineID) if err != nil { - log.Error().Msgf("error: cannot find pipeline with id %d: %s", workflow.PipelineID, err) + log.Error().Err(err).Msgf("cannot find pipeline with id %d", workflow.PipelineID) return err } repo, err := s.store.GetRepo(currentPipeline.RepoID) if err != nil { - log.Error().Msgf("error: cannot find repo with id %d: %s", currentPipeline.RepoID, err) + log.Error().Err(err).Msgf("cannot find repo with id %d", currentPipeline.RepoID) return err } - log.Trace(). + logger := log.With(). Str("repo_id", fmt.Sprint(repo.ID)). - Str("build_id", fmt.Sprint(currentPipeline.ID)). - Str("step_id", id). - Msgf("gRPC Done with state: %#v", state) + Str("pipeline_id", fmt.Sprint(currentPipeline.ID)). + Str("workflow_id", id).Logger() + + logger.Trace().Msgf("gRPC Done with state: %#v", state) if workflow, err = pipeline.UpdateStepStatusToDone(s.store, *workflow, state); err != nil { - log.Error().Msgf("error: done: cannot update step_id %d state: %s", workflow.ID, err) + logger.Error().Err(err).Msgf("pipeline.UpdateStepStatusToDone: cannot update workflow state: %s", err) } var queueErr error if workflow.Failing() { - queueErr = s.queue.Error(c, id, fmt.Errorf("Step finished with exitcode %d, %s", state.ExitCode, state.Error)) + queueErr = s.queue.Error(c, id, fmt.Errorf("Step finished with exit code %d, %s", state.ExitCode, state.Error)) } else { queueErr = s.queue.Done(c, id, workflow.State) } if queueErr != nil { - log.Error().Msgf("error: done: cannot ack step_id %d: %s", workflowID, err) + logger.Error().Err(queueErr).Msg("queue.Done: cannot ack workflow") } steps, err := s.store.StepList(currentPipeline) @@ -282,15 +283,20 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error { if !model.IsThereRunningStage(steps) { if currentPipeline, err = pipeline.UpdateStatusToDone(s.store, *currentPipeline, model.PipelineStatus(steps), workflow.Stopped); err != nil { - log.Error().Err(err).Msgf("error: done: cannot update build_id %d final state", currentPipeline.ID) + logger.Error().Err(err).Msgf("pipeline.UpdateStatusToDone: cannot update workflow final state") } } s.updateForgeStatus(c, repo, currentPipeline, workflow) - if err := s.logger.Close(c, id); err != nil { - log.Error().Err(err).Msgf("done: cannot close build_id %d logger", workflow.ID) - } + // make sure writes to pubsub are non blocking (https://github.com/woodpecker-ci/woodpecker/blob/c919f32e0b6432a95e1a6d3d0ad662f591adf73f/server/logging/log.go#L9) + go func() { + for _, step := range steps { + if err := s.logger.Close(c, step.ID); err != nil { + logger.Error().Err(err).Msgf("done: cannot close log stream for step %d", step.ID) + } + } + }() if err := s.notify(c, repo, currentPipeline, steps); err != nil { return err @@ -324,7 +330,7 @@ func (s *RPC) Log(c context.Context, _logEntry *rpc.LogEntry) error { // make sure writes to pubsub are non blocking (https://github.com/woodpecker-ci/woodpecker/blob/c919f32e0b6432a95e1a6d3d0ad662f591adf73f/server/logging/log.go#L9) go func() { // write line to listening web clients - if err := s.logger.Write(c, fmt.Sprint(logEntry.StepID), logEntry); err != nil { + if err := s.logger.Write(c, logEntry.StepID, logEntry); err != nil { log.Error().Err(err).Msgf("rpc server could not write to logger") } }() diff --git a/server/logging/log.go b/server/logging/log.go index 3dc8e874600..8f8fed94358 100644 --- a/server/logging/log.go +++ b/server/logging/log.go @@ -29,40 +29,40 @@ type subscriber struct { type stream struct { sync.Mutex - path string - list []*model.LogEntry - subs map[*subscriber]struct{} - done chan struct{} + stepID int64 + list []*model.LogEntry + subs map[*subscriber]struct{} + done chan struct{} } type log struct { sync.Mutex - streams map[string]*stream + streams map[int64]*stream } // New returns a new logger. func New() Log { return &log{ - streams: map[string]*stream{}, + streams: map[int64]*stream{}, } } -func (l *log) Open(_ context.Context, path string) error { +func (l *log) Open(_ context.Context, stepID int64) error { l.Lock() - _, ok := l.streams[path] + _, ok := l.streams[stepID] if !ok { - l.streams[path] = &stream{ - path: path, - subs: make(map[*subscriber]struct{}), - done: make(chan struct{}), + l.streams[stepID] = &stream{ + stepID: stepID, + subs: make(map[*subscriber]struct{}), + done: make(chan struct{}), } } l.Unlock() return nil } -func (l *log) Write(_ context.Context, stepID string, logEntry *model.LogEntry) error { +func (l *log) Write(_ context.Context, stepID int64, logEntry *model.LogEntry) error { l.Lock() s, ok := l.streams[stepID] l.Unlock() @@ -78,9 +78,9 @@ func (l *log) Write(_ context.Context, stepID string, logEntry *model.LogEntry) return nil } -func (l *log) Tail(c context.Context, path string, handler Handler) error { +func (l *log) Tail(c context.Context, stepID int64, handler Handler) error { l.Lock() - s, ok := l.streams[path] + s, ok := l.streams[stepID] l.Unlock() if !ok { return ErrNotFound @@ -107,9 +107,9 @@ func (l *log) Tail(c context.Context, path string, handler Handler) error { return nil } -func (l *log) Close(_ context.Context, path string) error { +func (l *log) Close(_ context.Context, stepID int64) error { l.Lock() - s, ok := l.streams[path] + s, ok := l.streams[stepID] l.Unlock() if !ok { return ErrNotFound @@ -120,14 +120,14 @@ func (l *log) Close(_ context.Context, path string) error { s.Unlock() l.Lock() - delete(l.streams, path) + delete(l.streams, stepID) l.Unlock() return nil } -func (l *log) Snapshot(_ context.Context, path string, w io.Writer) error { +func (l *log) Snapshot(_ context.Context, stepID int64, w io.Writer) error { l.Lock() - s, ok := l.streams[path] + s, ok := l.streams[stepID] l.Unlock() if !ok { return ErrNotFound @@ -138,11 +138,9 @@ func (l *log) Snapshot(_ context.Context, path string, w io.Writer) error { if _, err := w.Write(entry.Data); err != nil { return err } - if _, err := w.Write(cr); err != nil { + if _, err := w.Write([]byte{'\n'}); err != nil { return err } } return nil } - -var cr = []byte{'\n'} diff --git a/server/logging/log_test.go b/server/logging/log_test.go index 3a1f1d194bb..cae7f68a2ba 100644 --- a/server/logging/log_test.go +++ b/server/logging/log_test.go @@ -14,8 +14,8 @@ func TestLogging(t *testing.T) { var ( wg sync.WaitGroup - testPath = "test" - testEntry = &model.LogEntry{ + testStepID = int64(123) + testEntry = &model.LogEntry{ Data: []byte("test"), } ) @@ -25,27 +25,27 @@ func TestLogging(t *testing.T) { ) logger := New() - assert.NoError(t, logger.Open(ctx, testPath)) + assert.NoError(t, logger.Open(ctx, testStepID)) go func() { - assert.NoError(t, logger.Tail(ctx, testPath, func(entry ...*model.LogEntry) { wg.Done() })) + assert.NoError(t, logger.Tail(ctx, testStepID, func(entry ...*model.LogEntry) { wg.Done() })) }() go func() { - assert.NoError(t, logger.Tail(ctx, testPath, func(entry ...*model.LogEntry) { wg.Done() })) + assert.NoError(t, logger.Tail(ctx, testStepID, func(entry ...*model.LogEntry) { wg.Done() })) }() <-time.After(500 * time.Millisecond) wg.Add(4) go func() { - assert.NoError(t, logger.Write(ctx, testPath, testEntry)) - assert.NoError(t, logger.Write(ctx, testPath, testEntry)) + assert.NoError(t, logger.Write(ctx, testStepID, testEntry)) + assert.NoError(t, logger.Write(ctx, testStepID, testEntry)) }() wg.Wait() wg.Add(1) go func() { - assert.NoError(t, logger.Tail(ctx, testPath, func(entry ...*model.LogEntry) { wg.Done() })) + assert.NoError(t, logger.Tail(ctx, testStepID, func(entry ...*model.LogEntry) { wg.Done() })) }() <-time.After(500 * time.Millisecond) diff --git a/server/logging/logging.go b/server/logging/logging.go index 8d83c3cadc3..fb1bfe0e1da 100644 --- a/server/logging/logging.go +++ b/server/logging/logging.go @@ -19,17 +19,17 @@ type Handler func(...*model.LogEntry) // Log defines a log multiplexer. type Log interface { // Open opens the log. - Open(c context.Context, stepID string) error + Open(c context.Context, stepID int64) error // Write writes the entry to the log. - Write(c context.Context, stepID string, entry *model.LogEntry) error + Write(c context.Context, stepID int64, entry *model.LogEntry) error // Tail tails the log. - Tail(c context.Context, stepID string, handler Handler) error + Tail(c context.Context, stepID int64, handler Handler) error // Close closes the log. - Close(c context.Context, stepID string) error + Close(c context.Context, stepID int64) error // Snapshot snapshots the stream to Writer w. - Snapshot(c context.Context, stepID string, w io.Writer) error + Snapshot(c context.Context, stepID int64, w io.Writer) error } diff --git a/server/pipeline/cancel.go b/server/pipeline/cancel.go index 446a1f52249..b0b353ebe9e 100644 --- a/server/pipeline/cancel.go +++ b/server/pipeline/cancel.go @@ -131,18 +131,18 @@ func cancelPreviousPipelines( return err } - pipelineNeedsCancel := func(active *model.Pipeline) (bool, error) { + pipelineNeedsCancel := func(active *model.Pipeline) bool { // always filter on same event if active.Event != pipeline.Event { - return false, nil + return false } // find events for the same context switch pipeline.Event { case model.EventPush: - return pipeline.Branch == active.Branch, nil + return pipeline.Branch == active.Branch default: - return pipeline.Refspec == active.Refspec, nil + return pipeline.Refspec == active.Refspec } } @@ -152,14 +152,7 @@ func cancelPreviousPipelines( continue } - cancel, err := pipelineNeedsCancel(active) - if err != nil { - log.Error(). - Err(err). - Str("Ref", active.Ref). - Msg("Error while trying to cancel pipeline, skipping") - continue - } + cancel := pipelineNeedsCancel(active) if !cancel { continue diff --git a/server/pipeline/queue.go b/server/pipeline/queue.go index 19fe42d06cd..2dd1d7c2373 100644 --- a/server/pipeline/queue.go +++ b/server/pipeline/queue.go @@ -49,9 +49,6 @@ func queuePipeline(repo *model.Repo, pipelineItems []*pipeline.Item) error { Timeout: repo.Timeout, }) - if err := server.Config.Services.Logs.Open(context.Background(), task.ID); err != nil { - return err - } tasks = append(tasks, task) } return server.Config.Services.Queue.PushAtOnce(context.Background(), tasks) diff --git a/server/pipeline/start.go b/server/pipeline/start.go index d54aad33b57..64f8d7b2454 100644 --- a/server/pipeline/start.go +++ b/server/pipeline/start.go @@ -20,6 +20,7 @@ import ( "github.com/rs/zerolog/log" "github.com/woodpecker-ci/woodpecker/pipeline" + "github.com/woodpecker-ci/woodpecker/server" "github.com/woodpecker-ci/woodpecker/server/model" "github.com/woodpecker-ci/woodpecker/server/store" ) @@ -46,6 +47,16 @@ func start(ctx context.Context, store store.Store, activePipeline *model.Pipelin return nil, err } + // open logs streamer for each step + go func() { + steps := activePipeline.Steps + for _, step := range steps { + if err := server.Config.Services.Logs.Open(context.Background(), step.ID); err != nil { + log.Error().Err(err).Msgf("could not open log stream for step %d", step.ID) + } + } + }() + updatePipelineStatus(ctx, activePipeline, repo, user) return activePipeline, nil From 0db1ac94f3076cb44d16c30cd7cf0ecaa35e8350 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 02:08:10 +0200 Subject: [PATCH 21/37] update swagger doc --- cli/pipeline/logs.go | 6 +- cmd/server/docs/docs.go | 166 ++++++++++++++++---------- server/api/pipeline.go | 63 +++------- server/api/stream.go | 11 ++ server/model/log.go | 4 +- server/router/api.go | 15 ++- web/src/lib/api/index.ts | 2 +- web/src/lib/api/types/pipeline.ts | 1 + woodpecker-go/woodpecker/client.go | 4 +- woodpecker-go/woodpecker/const.go | 11 ++ woodpecker-go/woodpecker/interface.go | 4 +- woodpecker-go/woodpecker/types.go | 12 +- 12 files changed, 169 insertions(+), 130 deletions(-) diff --git a/cli/pipeline/logs.go b/cli/pipeline/logs.go index 7a72849f0db..cf7869e8b7a 100644 --- a/cli/pipeline/logs.go +++ b/cli/pipeline/logs.go @@ -27,7 +27,7 @@ import ( var pipelineLogsCmd = &cli.Command{ Name: "logs", Usage: "show pipeline logs", - ArgsUsage: " [pipeline] [step]", + ArgsUsage: " [pipeline] [stepID]", Action: pipelineLogs, Flags: common.GlobalFlags, } @@ -54,13 +54,13 @@ func pipelineLogs(c *cli.Context) error { return err } - logs, err := client.PipelineLogs(owner, name, number, step) + logs, err := client.StepLogEntries(owner, name, number, step) if err != nil { return err } for _, log := range logs { - fmt.Print(log.Output) + fmt.Print(log.Data) } return nil diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index 2abd3fd404c..a91b6c1bf3d 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -751,6 +751,52 @@ const docTemplate = `{ } } }, + "/logs/{owner}/{name}/{pipeline}/{stepID}": { + "get": { + "produces": [ + "text/plain" + ], + "tags": [ + "Pipeline logs" + ], + "summary": "Log stream", + "parameters": [ + { + "type": "string", + "description": "the repository owner's name", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "the repository name", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "the number of the pipeline", + "name": "pipeline", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "the step id", + "name": "stepID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/orgs/{owner}/permissions": { "get": { "produces": [ @@ -1795,10 +1841,10 @@ const docTemplate = `{ } } }, - "/repos/{owner}/{name}/logs/{number}/{pid}": { + "/repos/{owner}/{name}/logs/{number}/{stepID}": { "get": { "produces": [ - "text/plain" + "application/json" ], "tags": [ "Pipeline logs" @@ -1836,76 +1882,21 @@ const docTemplate = `{ }, { "type": "integer", - "description": "the pipeline id", - "name": "pid", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK" - } - } - } - }, - "/repos/{owner}/{name}/logs/{number}/{pid}/{step}": { - "get": { - "produces": [ - "text/plain" - ], - "tags": [ - "Pipeline logs" - ], - "summary": "Log information per step", - "parameters": [ - { - "type": "string", - "default": "Bearer \u003cpersonal access token\u003e", - "description": "Insert your personal access token", - "name": "Authorization", - "in": "header", - "required": true - }, - { - "type": "string", - "description": "the repository owner's name", - "name": "owner", - "in": "path", - "required": true - }, - { - "type": "string", - "description": "the repository name", - "name": "name", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "the number of the pipeline", - "name": "number", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "the pipeline id", - "name": "pid", - "in": "path", - "required": true - }, - { - "type": "integer", - "description": "the step name", - "name": "step", + "description": "the step id", + "name": "stepID", "in": "path", "required": true } ], "responses": { "200": { - "description": "OK" + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/LogEntry" + } + } } } } @@ -3800,6 +3791,32 @@ const docTemplate = `{ } } }, + "LogEntry": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "integer" + } + }, + "id": { + "type": "integer" + }, + "line": { + "type": "integer" + }, + "step_id": { + "type": "integer" + }, + "time": { + "type": "integer" + }, + "type": { + "$ref": "#/definitions/model.LogEntryType" + } + } + }, "OrgPerm": { "type": "object", "properties": { @@ -4323,6 +4340,23 @@ const docTemplate = `{ "EventCron", "EventManual" ] + }, + "model.LogEntryType": { + "type": "integer", + "enum": [ + 0, + 1, + 2, + 3, + 4 + ], + "x-enum-varnames": [ + "LogEntryStdout", + "LogEntryStderr", + "LogEntryExitCode", + "LogEntryMetadata", + "LogEntryProgress" + ] } } }` diff --git a/server/api/pipeline.go b/server/api/pipeline.go index 045b92cd577..f0520fb1f60 100644 --- a/server/api/pipeline.go +++ b/server/api/pipeline.go @@ -21,6 +21,7 @@ package api import ( "encoding/json" "errors" + "fmt" "net/http" "strconv" "time" @@ -186,68 +187,36 @@ func GetPipelineLast(c *gin.Context) { c.JSON(http.StatusOK, pl) } -// GetPipelineLogs +// GetStepLogs // -// @Summary Log information per step -// @Router /repos/{owner}/{name}/logs/{number}/{pid}/{step} [get] -// @Produce plain -// @Success 200 -// @Tags Pipeline logs +// @Summary Log information +// @Router /repos/{owner}/{name}/logs/{number}/{stepID} [get] +// @Produce json +// @Success 200 {array} LogEntry +// @Tags Pipeline logs // @Param Authorization header string true "Insert your personal access token" default(Bearer ) // @Param owner path string true "the repository owner's name" // @Param name path string true "the repository name" -// @Param number path int true "the number of the pipeline" -// @Param pid path int true "the pipeline id" -// @Param step path int true "the step name" -func GetPipelineLogs(c *gin.Context) { +// @Param number path int true "the number of the pipeline" +// @Param stepID path int true "the step id" +func GetStepLogs(c *gin.Context) { _store := store.FromContext(c) repo := session.Repo(c) // parse the pipeline number and step sequence number from // the request parameter. - num, _ := strconv.ParseInt(c.Params.ByName("number"), 10, 64) - ppid, _ := strconv.Atoi(c.Params.ByName("pid")) - name := c.Params.ByName("step") - - pl, err := _store.GetPipelineNumber(repo, num) + num, err := strconv.ParseInt(c.Params.ByName("number"), 10, 64) if err != nil { - handleDbGetError(c, err) - return - } - - // TODO: get & use step id directly - step, err := _store.StepChild(pl, ppid, name) - if err != nil { - handleDbGetError(c, err) + _ = c.AbortWithError(http.StatusBadRequest, err) return } - logs, err := _store.LogFind(step) + pl, err := _store.GetPipelineNumber(repo, num) if err != nil { handleDbGetError(c, err) return } - c.JSON(http.StatusOK, logs) -} - -// GetStepLogs -// -// @Summary Log information -// @Router /repos/{owner}/{name}/logs/{number}/{pid} [get] -// @Produce plain -// @Success 200 -// @Tags Pipeline logs -// @Param Authorization header string true "Insert your personal access token" default(Bearer ) -// @Param owner path string true "the repository owner's name" -// @Param name path string true "the repository name" -// @Param number path int true "the number of the pipeline" -// @Param pid path int true "the pipeline id" -func GetStepLogs(c *gin.Context) { - _store := store.FromContext(c) - - // parse the pipeline number and step sequence number from - // the request parameter. stepID, err := strconv.ParseInt(c.Params.ByName("stepId"), 10, 64) if err != nil { _ = c.AbortWithError(http.StatusBadRequest, err) @@ -260,6 +229,12 @@ func GetStepLogs(c *gin.Context) { return } + if step.PipelineID != pl.ID { + // make sure we can not read arbitrary logs by id + _ = c.AbortWithError(http.StatusBadRequest, fmt.Errorf("step with id %d is not part of repo %s", stepID, repo.FullName)) + return + } + logs, err := _store.LogFind(step) if err != nil { handleDbGetError(c, err) diff --git a/server/api/stream.go b/server/api/stream.go index 0f0a84b1dd2..da7821b4a2a 100644 --- a/server/api/stream.go +++ b/server/api/stream.go @@ -119,6 +119,17 @@ func EventStreamSSE(c *gin.Context) { } } +// LogStream +// +// @Summary Log stream +// @Router /logs/{owner}/{name}/{pipeline}/{stepID} [get] +// @Produce plain +// @Success 200 +// @Tags Pipeline logs +// @Param owner path string true "the repository owner's name" +// @Param name path string true "the repository name" +// @Param pipeline path int true "the number of the pipeline" +// @Param stepID path int true "the step id" func LogStreamSSE(c *gin.Context) { c.Header("Content-Type", "text/event-stream") c.Header("Cache-Control", "no-cache") diff --git a/server/model/log.go b/server/model/log.go index 103ae1f3382..c64e2ed6906 100644 --- a/server/model/log.go +++ b/server/model/log.go @@ -15,7 +15,7 @@ package model // LogEntryType identifies the type of line in the logs. -type LogEntryType int +type LogEntryType int // @name LogEntryType const ( LogEntryStdout LogEntryType = iota @@ -33,7 +33,7 @@ type LogEntry struct { Data []byte `json:"data" xorm:"LONGBLOB"` Created int64 `json:"-" xorm:"created"` Type LogEntryType `json:"type"` -} +} // @name LogEntry // TODO: store info what specific command the line belongs to (must be optional and impl. by backend) diff --git a/server/router/api.go b/server/router/api.go index 278cb57edd0..7bdd7699ce9 100644 --- a/server/router/api.go +++ b/server/router/api.go @@ -178,6 +178,15 @@ func apiRoutes(e *gin.Engine) { apiBase.POST("/hook", api.PostHook) + stream := e.Group("/stream") + { + stream.GET("/logs/:owner/:name/:pipeline/:stepId", + session.SetRepo(), + session.SetPerm(), + session.MustPull, + api.LogStreamSSE) + } + if zerolog.GlobalLevel() <= zerolog.DebugLevel { debugger := apiBase.Group("/debug") { @@ -203,11 +212,5 @@ func apiRoutes(e *gin.Engine) { sse := e.Group("/stream") { sse.GET("/events", api.EventStreamSSE) - sse.GET("/logs/:owner/:name/:pipeline/:stepId", - session.SetRepo(), - session.SetPerm(), - session.MustPull, - api.LogStreamSSE, - ) } } diff --git a/web/src/lib/api/index.ts b/web/src/lib/api/index.ts index cec83b30f40..8132af28922 100644 --- a/web/src/lib/api/index.ts +++ b/web/src/lib/api/index.ts @@ -300,7 +300,7 @@ export default class WoodpeckerClient extends ApiClient { // eslint-disable-next-line promise/prefer-await-to-callbacks callback: (data: PipelineLog) => void, ): EventSource { - return this._subscribe(`/stream/logs/${owner}/${repo}/${pipeline}/${step}`, callback, { + return this._subscribe(`/api/stream/logs/${owner}/${repo}/${pipeline}/${step}`, callback, { reconnect: true, }); } diff --git a/web/src/lib/api/types/pipeline.ts b/web/src/lib/api/types/pipeline.ts index 90a4f96b69c..3c569c57d02 100644 --- a/web/src/lib/api/types/pipeline.ts +++ b/web/src/lib/api/types/pipeline.ts @@ -102,6 +102,7 @@ export type PipelineStatus = export type PipelineStep = { id: number; + uuid: string; pipeline_id: number; pid: number; ppid: number; diff --git a/woodpecker-go/woodpecker/client.go b/woodpecker-go/woodpecker/client.go index be18d153b53..bbe81609af4 100644 --- a/woodpecker-go/woodpecker/client.go +++ b/woodpecker-go/woodpecker/client.go @@ -286,9 +286,9 @@ func (c *client) PipelineKill(owner, name string, num int) error { } // PipelineLogs returns the pipeline logs for the specified step. -func (c *client) PipelineLogs(owner, name string, num, step int) ([]*Logs, error) { +func (c *client) StepLogEntries(owner, name string, num, step int) ([]*LogEntry, error) { uri := fmt.Sprintf(pathLogs, c.addr, owner, name, num, step) - var out []*Logs + var out []*LogEntry err := c.get(uri, &out) return out, err } diff --git a/woodpecker-go/woodpecker/const.go b/woodpecker-go/woodpecker/const.go index bdc9b51a9a3..70e5a5be512 100644 --- a/woodpecker-go/woodpecker/const.go +++ b/woodpecker-go/woodpecker/const.go @@ -33,3 +33,14 @@ const ( StatusKilled = "killed" StatusError = "error" ) + +// LogEntryType identifies the type of line in the logs. +type LogEntryType int + +const ( + LogEntryStdout LogEntryType = iota + LogEntryStderr + LogEntryExitCode + LogEntryMetadata + LogEntryProgress +) diff --git a/woodpecker-go/woodpecker/interface.go b/woodpecker-go/woodpecker/interface.go index 04ec9a83a6c..0a12939be77 100644 --- a/woodpecker-go/woodpecker/interface.go +++ b/woodpecker-go/woodpecker/interface.go @@ -105,8 +105,8 @@ type Client interface { // PipelineKill force kills the running pipeline. PipelineKill(string, string, int) error - // PipelineLogs returns the logs for the given pipeline - PipelineLogs(string, string, int, int) ([]*Logs, error) + // StepLogEntries returns the LogEntries for the given pipeline step + StepLogEntries(string, string, int, int) ([]*LogEntry, error) // Deploy triggers a deployment for an existing pipeline using the specified // target environment. diff --git a/woodpecker-go/woodpecker/types.go b/woodpecker-go/woodpecker/types.go index 2b901ce99e5..ac9ddc6d1bd 100644 --- a/woodpecker-go/woodpecker/types.go +++ b/woodpecker-go/woodpecker/types.go @@ -173,10 +173,14 @@ type ( Level string `json:"log-level"` } - // Logs is the JSON data for a logs response - Logs struct { - Step string `json:"step"` - Output string `json:"out"` + // LogEntry is a single log entry + LogEntry struct { + ID int64 `json:"id"` + StepID int64 `json:"step_id"` + Time int64 `json:"time"` + Line int `json:"line"` + Data []byte `json:"data"` + Type LogEntryType `json:"type"` } // Cron is the JSON data of a cron job From 88c91a268e323587866bf46a4801aacda43e6c6c Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 02:19:23 +0200 Subject: [PATCH 22/37] add check back --- server/api/stream.go | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/server/api/stream.go b/server/api/stream.go index da7821b4a2a..9ad6397121e 100644 --- a/server/api/stream.go +++ b/server/api/stream.go @@ -17,6 +17,7 @@ package api import ( "context" + "fmt" "io" "net/http" "strconv" @@ -148,15 +149,42 @@ func LogStreamSSE(c *gin.Context) { flusher.Flush() _store := store.FromContext(c) + repo := session.Repo(c) - stepID, _ := strconv.ParseInt(c.Param("stepId"), 10, 64) + pipeline, err := strconv.ParseInt(c.Param("pipeline"), 10, 64) + if err != nil { + log.Debug().Err(err).Msg("pipeline number invalid") + logWriteStringErr(io.WriteString(rw, "event: error\ndata: pipeline number invalid\n\n")) + return + } + pl, err := _store.GetPipelineNumber(repo, pipeline) + if err != nil { + log.Debug().Msgf("stream cannot get pipeline number: %v", err) + logWriteStringErr(io.WriteString(rw, "event: error\ndata: pipeline not found\n\n")) + return + } + stepID, err := strconv.ParseInt(c.Param("stepId"), 10, 64) + if err != nil { + log.Debug().Err(err).Msg("step id invalid") + logWriteStringErr(io.WriteString(rw, "event: error\ndata: step id invalid\n\n")) + return + } step, err := _store.StepLoad(stepID) if err != nil { log.Debug().Msgf("stream cannot get step number: %v", err) logWriteStringErr(io.WriteString(rw, "event: error\ndata: process not found\n\n")) return } + + if step.PipelineID != pl.ID { + // make sure we can not read arbitrary logs by id + err = fmt.Errorf("step with id %d is not part of repo %s", stepID, repo.FullName) + log.Debug().Err(err) + logWriteStringErr(io.WriteString(rw, "event: error\ndata: "+err.Error()+"\n\n")) + return + } + if step.State != model.StatusRunning { log.Debug().Msg("stream not found.") logWriteStringErr(io.WriteString(rw, "event: error\ndata: stream not found\n\n")) From 2d5c497cbd64cb71a24f939138479e5add71f947 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 02:21:07 +0200 Subject: [PATCH 23/37] fix --- server/router/api.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/router/api.go b/server/router/api.go index 7bdd7699ce9..cff366b8ddd 100644 --- a/server/router/api.go +++ b/server/router/api.go @@ -178,7 +178,7 @@ func apiRoutes(e *gin.Engine) { apiBase.POST("/hook", api.PostHook) - stream := e.Group("/stream") + stream := apiBase.Group("/stream") { stream.GET("/logs/:owner/:name/:pipeline/:stepId", session.SetRepo(), From 64123060c6ec6b87eac0a4111f04a06dab25a745 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 02:28:22 +0200 Subject: [PATCH 24/37] fix tests --- pipeline/frontend/yaml/container_test.go | 1 - pipeline/rpc/log_entry_test.go | 2 +- server/api/stream.go | 2 +- server/store/datastore/repo_test.go | 1 + 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pipeline/frontend/yaml/container_test.go b/pipeline/frontend/yaml/container_test.go index 2a22fa96433..600cf0d343e 100644 --- a/pipeline/frontend/yaml/container_test.go +++ b/pipeline/frontend/yaml/container_test.go @@ -87,7 +87,6 @@ func TestUnmarshalContainer(t *testing.T) { ExtraHosts: []string{"somehost:162.242.195.82", "otherhost:50.31.209.229"}, Image: "golang:latest", Isolation: "hyperv", - Labels: types.SliceorMap{"com.example.type": "build", "com.example.team": "frontend"}, MemLimit: types.MemStringorInt(1024), MemSwapLimit: types.MemStringorInt(1024), MemSwappiness: types.MemStringorInt(1024), diff --git a/pipeline/rpc/log_entry_test.go b/pipeline/rpc/log_entry_test.go index 84d3d300d2e..a60c4650e5d 100644 --- a/pipeline/rpc/log_entry_test.go +++ b/pipeline/rpc/log_entry_test.go @@ -25,7 +25,7 @@ func TestLogEntry(t *testing.T) { Line: 1, Data: "starting redis server", } - got, want := line.String(), "[redis:L1:60s] starting redis server" + got, want := line.String(), "[e9ea76a5-44a1-4059-9c4a-6956c478b26d:L1:60s] starting redis server" if got != want { t.Errorf("Wanted line string %q, got %q", want, got) } diff --git a/server/api/stream.go b/server/api/stream.go index 9ad6397121e..436c4e64d31 100644 --- a/server/api/stream.go +++ b/server/api/stream.go @@ -180,7 +180,7 @@ func LogStreamSSE(c *gin.Context) { if step.PipelineID != pl.ID { // make sure we can not read arbitrary logs by id err = fmt.Errorf("step with id %d is not part of repo %s", stepID, repo.FullName) - log.Debug().Err(err) + log.Debug().Err(err).Msg("event error") logWriteStringErr(io.WriteString(rw, "event: error\ndata: "+err.Error()+"\n\n")) return } diff --git a/server/store/datastore/repo_test.go b/server/store/datastore/repo_test.go index 8a0895abf98..58c2677dc42 100644 --- a/server/store/datastore/repo_test.go +++ b/server/store/datastore/repo_test.go @@ -334,6 +334,7 @@ func TestRepoCrud(t *testing.T) { RepoID: repoUnrelated.ID, } stepUnrelated := model.Step{ + UUID: "44c0de71-a6be-41c9-b860-e3716d1dfcef", Name: "a unrelated step", } assert.NoError(t, store.CreatePipeline(&pipelineUnrelated, &stepUnrelated)) From 08d75c65e31f3a5accafe026276329fdbd670b13 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 02:43:01 +0200 Subject: [PATCH 25/37] migration --- server/model/log.go | 2 +- .../store/datastore/migration/018_alter_logs_table.go | 11 ++++++----- server/store/datastore/migration/migration.go | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/server/model/log.go b/server/model/log.go index c64e2ed6906..acce9f1e021 100644 --- a/server/model/log.go +++ b/server/model/log.go @@ -26,7 +26,7 @@ const ( ) type LogEntry struct { - ID int64 `json:"id" xorm:"pk autoincr"` + ID int64 `json:"id" xorm:"pk autoincr 'id'"` StepID int64 `json:"step_id" xorm:"'step_id'"` Time int64 `json:"time"` Line int `json:"line"` diff --git a/server/store/datastore/migration/018_alter_logs_table.go b/server/store/datastore/migration/018_alter_logs_table.go index dd23e07aa38..737acbd0920 100644 --- a/server/store/datastore/migration/018_alter_logs_table.go +++ b/server/store/datastore/migration/018_alter_logs_table.go @@ -40,11 +40,12 @@ type oldLogEntry018 struct { Out string `json:"out,omitempty"` } -var alterLogsTable = task{ - name: "alter-logs-table", +var migrateLogs2LogEntries = task{ + name: "migrate-logs-to-log_entries", + required: true, fn: func(sess *xorm.Session) error { // make sure old logs table exists - if err := sess.Sync(new(oldLogs018)); err != nil { + if exist, err := sess.IsTableExist("logs"); !exist || err != nil { return err } @@ -55,7 +56,7 @@ var alterLogsTable = task{ page := 0 for { var logs []*oldLogs018 - err := sess.Limit(10, page*10).Table("logs").Find(&logs) + err := sess.Limit(10, page*10).Find(&logs) if err != nil { return err } @@ -75,11 +76,11 @@ var alterLogsTable = task{ } log := &model.LogEntry{ - ID: l.ID, StepID: l.StepID, Data: []byte(logEntry.Out), Line: logEntry.Pos, Time: time, + Type: model.LogEntryType(logEntry.Type), } if _, err := sess.Insert(log); err != nil { diff --git a/server/store/datastore/migration/migration.go b/server/store/datastore/migration/migration.go index e673a9e5a8a..d46cbdc7442 100644 --- a/server/store/datastore/migration/migration.go +++ b/server/store/datastore/migration/migration.go @@ -46,7 +46,7 @@ var migrationTasks = []*task{ &removeInactiveRepos, &dropFiles, &removeMachineCol, - &alterLogsTable, + &migrateLogs2LogEntries, } var allBeans = []interface{}{ From ff498e158b822f20bd32a73417bd47fda5aa244e Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 02:55:04 +0200 Subject: [PATCH 26/37] migration timeout --- server/store/datastore/migration/migration.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/store/datastore/migration/migration.go b/server/store/datastore/migration/migration.go index d46cbdc7442..6f7ad31a5ad 100644 --- a/server/store/datastore/migration/migration.go +++ b/server/store/datastore/migration/migration.go @@ -17,6 +17,7 @@ package migration import ( "fmt" "reflect" + "time" "github.com/rs/zerolog/log" "xorm.io/xorm" @@ -98,6 +99,9 @@ func Migrate(e *xorm.Engine) error { return err } + // account for long migrations + e.SetConnMaxLifetime(time.Minute * 30) + sess := e.NewSession() defer sess.Close() if err := sess.Begin(); err != nil { From 5c290f92f11136eaf9521757587991d6cb72751a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 03:03:16 +0200 Subject: [PATCH 27/37] migration takes a long time --- server/store/datastore/migration/018_alter_logs_table.go | 5 +++++ server/store/datastore/migration/migration.go | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/server/store/datastore/migration/018_alter_logs_table.go b/server/store/datastore/migration/018_alter_logs_table.go index 737acbd0920..ac6b32dcae1 100644 --- a/server/store/datastore/migration/018_alter_logs_table.go +++ b/server/store/datastore/migration/018_alter_logs_table.go @@ -17,6 +17,7 @@ package migration import ( "encoding/json" + "github.com/rs/zerolog/log" "xorm.io/xorm" "github.com/woodpecker-ci/woodpecker/server/model" @@ -53,6 +54,8 @@ var migrateLogs2LogEntries = task{ return err } + log.Info().Msg("migrate-logs-to-log_entries: start migration of logs") + page := 0 for { var logs []*oldLogs018 @@ -61,6 +64,8 @@ var migrateLogs2LogEntries = task{ return err } + log.Info().Msgf("migrate-logs-to-log_entries: start page %d", page) + for _, l := range logs { logEntries := []*oldLogEntry018{} diff --git a/server/store/datastore/migration/migration.go b/server/store/datastore/migration/migration.go index 6f7ad31a5ad..c584f2bfbbf 100644 --- a/server/store/datastore/migration/migration.go +++ b/server/store/datastore/migration/migration.go @@ -99,8 +99,8 @@ func Migrate(e *xorm.Engine) error { return err } - // account for long migrations - e.SetConnMaxLifetime(time.Minute * 30) + // account for long migrations of "migrate-logs-to-log_entries" + e.SetConnMaxLifetime(time.Hour * 1) sess := e.NewSession() defer sess.Close() From 762e5de91e88928260dd1390fda7fe276facfabc Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 03:23:56 +0200 Subject: [PATCH 28/37] make cli work --- cli/pipeline/logs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/pipeline/logs.go b/cli/pipeline/logs.go index cf7869e8b7a..a54b0699885 100644 --- a/cli/pipeline/logs.go +++ b/cli/pipeline/logs.go @@ -60,7 +60,7 @@ func pipelineLogs(c *cli.Context) error { } for _, log := range logs { - fmt.Print(log.Data) + fmt.Print(string(log.Data)) } return nil From fcee17833a2e0f3908926040089816f709d7f112 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 03:49:24 +0200 Subject: [PATCH 29/37] fix webui log display --- web/src/components/repo/pipeline/PipelineLog.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/components/repo/pipeline/PipelineLog.vue b/web/src/components/repo/pipeline/PipelineLog.vue index 5a2b5b78429..f606f9fe558 100644 --- a/web/src/components/repo/pipeline/PipelineLog.vue +++ b/web/src/components/repo/pipeline/PipelineLog.vue @@ -198,7 +198,7 @@ async function download() { downloadInProgress.value = false; } const fileURL = window.URL.createObjectURL( - new Blob([logs.map((line) => line.data).join('')], { + new Blob([logs.map((line) => atob(line.data)).join('')], { type: 'text/plain', }), ); @@ -240,7 +240,7 @@ async function loadLogs() { if (isStepFinished(step.value)) { const logs = await apiClient.getLogs(repo.value.owner, repo.value.name, pipeline.value.number, step.value.id); - logs?.forEach((line) => writeLog({ index: line.line, text: line.data, time: line.time })); + logs?.forEach((line) => writeLog({ index: line.line, text: atob(line.data), time: line.time })); flushLogs(false); } From e4d806daa14924a7419aaabacd777c9d3288a982 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 03:59:04 +0200 Subject: [PATCH 30/37] a todo got resolved --- web/src/components/repo/pipeline/PipelineLog.vue | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/web/src/components/repo/pipeline/PipelineLog.vue b/web/src/components/repo/pipeline/PipelineLog.vue index f606f9fe558..be02f527528 100644 --- a/web/src/components/repo/pipeline/PipelineLog.vue +++ b/web/src/components/repo/pipeline/PipelineLog.vue @@ -245,17 +245,12 @@ async function loadLogs() { } if (isStepRunning(step.value)) { - // load stream of parent process (which receives all child processes logs) - // TODO: change stream to only send data of single child process stream.value = apiClient.streamLogs( repo.value.owner, repo.value.name, pipeline.value.number, - step.value.ppid, + step.value.id, (line) => { - if (line?.step_id !== step.value?.id) { - return; - } writeLog({ index: line.line, text: line.data, time: line.time }); flushLogs(true); }, From 0b5c32e7159272c84151ea47d311ccb474abef05 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 04:17:03 +0200 Subject: [PATCH 31/37] cleanup code --- server/api/stream.go | 4 ---- server/logging/log.go | 21 --------------------- server/logging/logging.go | 4 ---- 3 files changed, 29 deletions(-) diff --git a/server/api/stream.go b/server/api/stream.go index 436c4e64d31..f340f326326 100644 --- a/server/api/stream.go +++ b/server/api/stream.go @@ -206,10 +206,6 @@ func LogStreamSSE(c *gin.Context) { go func() { err := server.Config.Services.Logs.Tail(ctx, step.ID, func(entries ...*model.LogEntry) { - defer func() { - obj := recover() // TODO: check if it's still needed - log.Error().Msgf("## IF YOU SEE THIS OPEN AN ISSUE UPSTREAM ## - pubsub subscribe recover return: %v", obj) - }() for _, entry := range entries { select { case <-ctx.Done(): diff --git a/server/logging/log.go b/server/logging/log.go index 8f8fed94358..e29e498cee0 100644 --- a/server/logging/log.go +++ b/server/logging/log.go @@ -2,7 +2,6 @@ package logging import ( "context" - "io" "sync" "github.com/woodpecker-ci/woodpecker/server/model" @@ -124,23 +123,3 @@ func (l *log) Close(_ context.Context, stepID int64) error { l.Unlock() return nil } - -func (l *log) Snapshot(_ context.Context, stepID int64, w io.Writer) error { - l.Lock() - s, ok := l.streams[stepID] - l.Unlock() - if !ok { - return ErrNotFound - } - s.Lock() - defer s.Unlock() - for _, entry := range s.list { - if _, err := w.Write(entry.Data); err != nil { - return err - } - if _, err := w.Write([]byte{'\n'}); err != nil { - return err - } - } - return nil -} diff --git a/server/logging/logging.go b/server/logging/logging.go index fb1bfe0e1da..69932a55204 100644 --- a/server/logging/logging.go +++ b/server/logging/logging.go @@ -3,7 +3,6 @@ package logging import ( "context" "errors" - "io" "github.com/woodpecker-ci/woodpecker/server/model" ) @@ -29,7 +28,4 @@ type Log interface { // Close closes the log. Close(c context.Context, stepID int64) error - - // Snapshot snapshots the stream to Writer w. - Snapshot(c context.Context, stepID int64, w io.Writer) error } From 0f82a4f5005c54c00c01f6d5b836b0cabd1a7f53 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 04:37:20 +0200 Subject: [PATCH 32/37] fix web logstream --- server/api/stream.go | 4 +++- web/src/components/repo/pipeline/PipelineLog.vue | 2 +- web/src/lib/api/types/pipeline.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/server/api/stream.go b/server/api/stream.go index f340f326326..86f21e2922e 100644 --- a/server/api/stream.go +++ b/server/api/stream.go @@ -17,6 +17,7 @@ package api import ( "context" + "encoding/json" "fmt" "io" "net/http" @@ -211,7 +212,8 @@ func LogStreamSSE(c *gin.Context) { case <-ctx.Done(): return default: - logc <- entry.Data + ee, _ := json.Marshal(entry) + logc <- ee } } }) diff --git a/web/src/components/repo/pipeline/PipelineLog.vue b/web/src/components/repo/pipeline/PipelineLog.vue index be02f527528..d8f06184653 100644 --- a/web/src/components/repo/pipeline/PipelineLog.vue +++ b/web/src/components/repo/pipeline/PipelineLog.vue @@ -251,7 +251,7 @@ async function loadLogs() { pipeline.value.number, step.value.id, (line) => { - writeLog({ index: line.line, text: line.data, time: line.time }); + writeLog({ index: line.line, text: atob(line.data), time: line.time }); flushLogs(true); }, ); diff --git a/web/src/lib/api/types/pipeline.ts b/web/src/lib/api/types/pipeline.ts index 3c569c57d02..5cb5a61d0a5 100644 --- a/web/src/lib/api/types/pipeline.ts +++ b/web/src/lib/api/types/pipeline.ts @@ -123,7 +123,7 @@ export type PipelineLog = { step_id: number; time: number; line: number; - data: string; + data: string; // base64 encoded type: number; }; From 1be32430e8996654eb4a6bf7eeb2af93aa26017a Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 04:41:19 +0200 Subject: [PATCH 33/37] clean --- pipeline/rpc/log_entry.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pipeline/rpc/log_entry.go b/pipeline/rpc/log_entry.go index 6b0c37ef52e..bd3f5163670 100644 --- a/pipeline/rpc/log_entry.go +++ b/pipeline/rpc/log_entry.go @@ -81,14 +81,12 @@ func (w *LineWriter) Write(p []byte) (n int, err error) { } log.Trace().Str("step-uuid", w.stepUUID).Msgf("grpc write line: %s", data) - // TODO: split p by "\n" to create a new entry for each line? line := &LogEntry{ Data: data, StepUUID: w.stepUUID, Time: int64(time.Since(w.now).Seconds()), Type: LogEntryStdout, - // TODO: figure out a way to calc and set correct line number - Line: w.num, + Line: w.num, } if err := w.peer.Log(context.Background(), line); err != nil { log.Error().Err(err).Str("step-uuid", w.stepUUID).Msg("fail to write pipeline log to peer") From dab2e5d7bdb6d0cb1b4d376a49ca76e04e85b4c2 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 06:24:38 +0200 Subject: [PATCH 34/37] Update server/store/datastore/log.go Co-authored-by: Anbraten --- server/store/datastore/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/store/datastore/log.go b/server/store/datastore/log.go index fa101cb64fc..5095353a5c9 100644 --- a/server/store/datastore/log.go +++ b/server/store/datastore/log.go @@ -34,7 +34,7 @@ func (s storage) LogSave(step *model.Step, logEntries []*model.LogEntry) error { for _, logEntry := range logEntries { if logEntry.StepID != step.ID { - return fmt.Errorf("got a log-entry with step id '%d' but require '%d'", logEntry.StepID, step.ID) + return fmt.Errorf("got a log-entry with step id '%d' but expected '%d'", logEntry.StepID, step.ID) } if _, err := sess.Insert(logEntry); err != nil { return err From 21b49890aa23f94c76f158801fc203f2da605271 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 06:25:19 +0200 Subject: [PATCH 35/37] Update server/logging/logging.go --- server/logging/logging.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/logging/logging.go b/server/logging/logging.go index 69932a55204..02acbba5535 100644 --- a/server/logging/logging.go +++ b/server/logging/logging.go @@ -7,8 +7,6 @@ import ( "github.com/woodpecker-ci/woodpecker/server/model" ) -// TODO(#742): write adapter for external pubsub provider - // ErrNotFound is returned when the log does not exist. var ErrNotFound = errors.New("stream: not found") From 9eb02734505e8f460c34ecb57796c9e0edc32047 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 5 Jun 2023 18:01:29 +0200 Subject: [PATCH 36/37] move migration into new pull: #1828 --- .../migration/018_alter_logs_table.go | 106 ------------------ server/store/datastore/migration/migration.go | 5 - 2 files changed, 111 deletions(-) delete mode 100644 server/store/datastore/migration/018_alter_logs_table.go diff --git a/server/store/datastore/migration/018_alter_logs_table.go b/server/store/datastore/migration/018_alter_logs_table.go deleted file mode 100644 index ac6b32dcae1..00000000000 --- a/server/store/datastore/migration/018_alter_logs_table.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2023 Woodpecker Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package migration - -import ( - "encoding/json" - - "github.com/rs/zerolog/log" - "xorm.io/xorm" - - "github.com/woodpecker-ci/woodpecker/server/model" -) - -type oldLogs018 struct { - ID int64 `xorm:"pk autoincr 'log_id'"` - StepID int64 `xorm:"UNIQUE 'log_step_id'"` - Data []byte `xorm:"LONGBLOB 'log_data'"` -} - -func (oldLogs018) TableName() string { - return "logs" -} - -type oldLogEntry018 struct { - Step string `json:"step,omitempty"` - Time int64 `json:"time,omitempty"` - Type int `json:"type,omitempty"` - Pos int `json:"pos,omitempty"` - Out string `json:"out,omitempty"` -} - -var migrateLogs2LogEntries = task{ - name: "migrate-logs-to-log_entries", - required: true, - fn: func(sess *xorm.Session) error { - // make sure old logs table exists - if exist, err := sess.IsTableExist("logs"); !exist || err != nil { - return err - } - - if err := sess.Sync(new(model.LogEntry)); err != nil { - return err - } - - log.Info().Msg("migrate-logs-to-log_entries: start migration of logs") - - page := 0 - for { - var logs []*oldLogs018 - err := sess.Limit(10, page*10).Find(&logs) - if err != nil { - return err - } - - log.Info().Msgf("migrate-logs-to-log_entries: start page %d", page) - - for _, l := range logs { - - logEntries := []*oldLogEntry018{} - if err := json.Unmarshal(l.Data, &logEntries); err != nil { - return err - } - - time := int64(0) - for _, logEntry := range logEntries { - - if logEntry.Time > time { - time = logEntry.Time - } - - log := &model.LogEntry{ - StepID: l.StepID, - Data: []byte(logEntry.Out), - Line: logEntry.Pos, - Time: time, - Type: model.LogEntryType(logEntry.Type), - } - - if _, err := sess.Insert(log); err != nil { - return err - } - } - } - - if len(logs) < 10 { - break - } - - page++ - } - - return sess.DropTable("logs") - }, -} diff --git a/server/store/datastore/migration/migration.go b/server/store/datastore/migration/migration.go index c584f2bfbbf..f79a5352914 100644 --- a/server/store/datastore/migration/migration.go +++ b/server/store/datastore/migration/migration.go @@ -17,7 +17,6 @@ package migration import ( "fmt" "reflect" - "time" "github.com/rs/zerolog/log" "xorm.io/xorm" @@ -47,7 +46,6 @@ var migrationTasks = []*task{ &removeInactiveRepos, &dropFiles, &removeMachineCol, - &migrateLogs2LogEntries, } var allBeans = []interface{}{ @@ -99,9 +97,6 @@ func Migrate(e *xorm.Engine) error { return err } - // account for long migrations of "migrate-logs-to-log_entries" - e.SetConnMaxLifetime(time.Hour * 1) - sess := e.NewSession() defer sess.Close() if err := sess.Begin(); err != nil { From 7ae0308cc201e806a1e51581fb593f3cc31977de Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 6 Jun 2023 00:57:29 +0200 Subject: [PATCH 37/37] OrderBy_ID --- server/store/datastore/log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/store/datastore/log.go b/server/store/datastore/log.go index 5095353a5c9..e9a9afbfd81 100644 --- a/server/store/datastore/log.go +++ b/server/store/datastore/log.go @@ -22,7 +22,7 @@ import ( func (s storage) LogFind(step *model.Step) ([]*model.LogEntry, error) { var logEntries []*model.LogEntry - return logEntries, s.engine.Where("step_id = ?", step.ID).Find(&logEntries) + return logEntries, s.engine.Asc("id").Where("step_id = ?", step.ID).Find(&logEntries) } func (s storage) LogSave(step *model.Step, logEntries []*model.LogEntry) error {