Logproto: Extract push.proto from logproto package to the separate module (#8259)

Co-authored-by: Owen Diehl <ow.diehl@gmail.com>
pull/8304/head
Irina 3 years ago committed by GitHub
parent 5993640329
commit 4cd1246b88
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 5
      go.mod
  3. 4
      go.sum
  4. 23
      pkg/logproto/alias.go
  5. 1675
      pkg/logproto/logproto.pb.go
  6. 37
      pkg/logproto/logproto.proto
  7. 462
      pkg/logproto/types.go
  8. 23
      pkg/push/go.mod
  9. 69
      pkg/push/go.sum
  10. 1292
      pkg/push/push.pb.go
  11. 40
      pkg/push/push.proto
  12. 6
      pkg/push/timestamp.go
  13. 465
      pkg/push/types.go
  14. 2
      pkg/push/types_test.go
  15. 134
      pkg/querier/queryrange/queryrange.pb.go
  16. 3
      pkg/querier/queryrange/queryrange.proto
  17. 1292
      vendor/github.com/grafana/loki/pkg/push/push.pb.go
  18. 40
      vendor/github.com/grafana/loki/pkg/push/push.proto
  19. 106
      vendor/github.com/grafana/loki/pkg/push/timestamp.go
  20. 465
      vendor/github.com/grafana/loki/pkg/push/types.go
  21. 7
      vendor/google.golang.org/grpc/balancer/grpclb/grpc_lb_v1/load_balancer.pb.go
  22. 2
      vendor/google.golang.org/grpc/balancer/grpclb/grpclb.go
  23. 8
      vendor/google.golang.org/grpc/balancer/grpclb/grpclbstate/state.go
  24. 7
      vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go
  25. 9
      vendor/google.golang.org/grpc/clientconn.go
  26. 7
      vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/altscontext.pb.go
  27. 7
      vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/handshaker.pb.go
  28. 7
      vendor/google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp/transport_security_common.pb.go
  29. 10
      vendor/google.golang.org/grpc/dialoptions.go
  30. 7
      vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go
  31. 2
      vendor/google.golang.org/grpc/internal/binarylog/method_logger.go
  32. 7
      vendor/google.golang.org/grpc/internal/proto/grpc_lookup_v1/rls.pb.go
  33. 7
      vendor/google.golang.org/grpc/internal/proto/grpc_lookup_v1/rls_config.pb.go
  34. 6
      vendor/google.golang.org/grpc/internal/resolver/dns/dns_resolver.go
  35. 9
      vendor/google.golang.org/grpc/internal/resolver/passthrough/passthrough.go
  36. 4
      vendor/google.golang.org/grpc/internal/resolver/unix/unix.go
  37. 46
      vendor/google.golang.org/grpc/internal/transport/controlbuf.go
  38. 45
      vendor/google.golang.org/grpc/internal/transport/handler_server.go
  39. 46
      vendor/google.golang.org/grpc/internal/transport/http2_client.go
  40. 96
      vendor/google.golang.org/grpc/internal/transport/http2_server.go
  41. 2
      vendor/google.golang.org/grpc/internal/transport/transport.go
  42. 4
      vendor/google.golang.org/grpc/pickfirst.go
  43. 7
      vendor/google.golang.org/grpc/regenerate.sh
  44. 77
      vendor/google.golang.org/grpc/server.go
  45. 2
      vendor/google.golang.org/grpc/stream.go
  46. 2
      vendor/google.golang.org/grpc/version.go
  47. 3
      vendor/google.golang.org/grpc/vet.sh
  48. 10
      vendor/google.golang.org/grpc/xds/internal/balancer/ringhash/config.go
  49. 7
      vendor/google.golang.org/grpc/xds/internal/balancer/ringhash/ring.go
  50. 2
      vendor/google.golang.org/grpc/xds/internal/balancer/ringhash/ringhash.go
  51. 14
      vendor/google.golang.org/grpc/xds/internal/xdsclient/client.go
  52. 5
      vendor/google.golang.org/grpc/xds/internal/xdsclient/client_new.go
  53. 25
      vendor/google.golang.org/grpc/xds/internal/xdsclient/clientimpl.go
  54. 68
      vendor/google.golang.org/grpc/xds/internal/xdsclient/clientimpl_watchers.go
  55. 19
      vendor/google.golang.org/grpc/xds/internal/xdsclient/controller/controller.go
  56. 12
      vendor/google.golang.org/grpc/xds/internal/xdsclient/controller/transport.go
  57. 5
      vendor/google.golang.org/grpc/xds/internal/xdsclient/singleton.go
  58. 149
      vendor/google.golang.org/grpc/xds/internal/xdsclient/xdsresource/cluster_resource_type.go
  59. 144
      vendor/google.golang.org/grpc/xds/internal/xdsclient/xdsresource/endpoints_resource_type.go
  60. 3
      vendor/google.golang.org/grpc/xds/internal/xdsclient/xdsresource/errors.go
  61. 181
      vendor/google.golang.org/grpc/xds/internal/xdsclient/xdsresource/listener_resource_type.go
  62. 158
      vendor/google.golang.org/grpc/xds/internal/xdsclient/xdsresource/resource_type.go
  63. 145
      vendor/google.golang.org/grpc/xds/internal/xdsclient/xdsresource/route_config_resource_type.go
  64. 8
      vendor/modules.txt

@ -27,6 +27,7 @@
* [8131](https://github.com/grafana/loki/pull/8131) **jeschkies**: Compile Promtail ARM and ARM64 with journald support.
* [8212](https://github.com/grafana/loki/pull/8212) **kavirajk**: ingester: Add `ingester_memory_streams_labels_bytes metric` for more visibility of size of metadata of in-memory streams.
* [8271](https://github.com/grafana/loki/pull/8271) **kavirajk**: logql: Support urlencode and urldecode template functions
* [8259](https://github.com/grafana/loki/pull/8259) **mar4uk**: Extract push.proto from the logproto package to the separate module.
* [7906](https://github.com/grafana/loki/pull/7906) **kavirajk**: Add API endpoint that formats LogQL expressions and support new `fmt` subcommand in `logcli` to format LogQL query.
##### Fixes

@ -103,7 +103,7 @@ require (
golang.org/x/sys v0.4.0
golang.org/x/time v0.3.0
google.golang.org/api v0.108.0
google.golang.org/grpc v1.51.0
google.golang.org/grpc v1.52.0
gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
@ -114,6 +114,7 @@ require (
require (
github.com/Azure/go-autorest/autorest v0.11.28
github.com/fsnotify/fsnotify v1.6.0
github.com/grafana/loki/pkg/push v0.0.0-00010101000000-000000000000
github.com/heroku/x v0.0.55
github.com/prometheus/alertmanager v0.25.0
github.com/prometheus/common/sigv4 v0.1.0
@ -324,3 +325,5 @@ replace github.com/hashicorp/memberlist => github.com/grafana/memberlist v0.3.1-
// Insist on the optimised version of grafana/regexp
replace github.com/grafana/regexp => github.com/grafana/regexp v0.0.0-20221005093135-b4c2bcb0a4b6
replace github.com/grafana/loki/pkg/push => ./pkg/push

@ -2110,8 +2110,8 @@ google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk=
google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/grpc/examples v0.0.0-20210916203835-567da6b86340/go.mod h1:gID3PKrg7pWKntu9Ss6zTLJ0ttC0X9IHgREOCZwbCVU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=

@ -0,0 +1,23 @@
package logproto
import (
"github.com/grafana/loki/pkg/push"
"google.golang.org/grpc"
)
// Aliases to avoid renaming all the imports of logproto
type Entry = push.Entry
type Stream = push.Stream
type PushRequest = push.PushRequest
type PushResponse = push.PushResponse
type PusherClient = push.PusherClient
type PusherServer = push.PusherServer
func NewPusherClient(cc *grpc.ClientConn) PusherClient {
return push.NewPusherClient(cc)
}
func RegisterPusherServer(s *grpc.Server, srv PusherServer) {
push.RegisterPusherServer(s, srv)
}

File diff suppressed because it is too large Load Diff

@ -5,13 +5,10 @@ package logproto;
import "gogoproto/gogo.proto";
import "google/protobuf/timestamp.proto";
import "pkg/logqlmodel/stats/stats.proto";
import "pkg/push/push.proto";
option go_package = "github.com/grafana/loki/pkg/logproto";
service Pusher {
rpc Push(PushRequest) returns (PushResponse) {}
}
service Querier {
rpc Query(QueryRequest) returns (stream QueryResponse) {}
@ -53,15 +50,6 @@ message StreamRate {
string tenant = 4;
}
message PushRequest {
repeated StreamAdapter streams = 1 [
(gogoproto.jsontag) = "streams",
(gogoproto.customtype) = "Stream"
];
}
message PushResponse {}
message QueryRequest {
string selector = 1;
uint32 limit = 2;
@ -101,7 +89,7 @@ message Delete {
message QueryResponse {
repeated StreamAdapter streams = 1 [
(gogoproto.customtype) = "Stream",
(gogoproto.customtype) = "github.com/grafana/loki/pkg/push.Stream",
(gogoproto.nullable) = true
];
stats.Ingester stats = 2 [(gogoproto.nullable) = false];
@ -137,25 +125,6 @@ message LabelResponse {
repeated string values = 1;
}
message StreamAdapter {
string labels = 1 [(gogoproto.jsontag) = "labels"];
repeated EntryAdapter entries = 2 [
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "entries"
];
// hash contains the original hash of the stream.
uint64 hash = 3 [(gogoproto.jsontag) = "-"];
}
message EntryAdapter {
google.protobuf.Timestamp timestamp = 1 [
(gogoproto.stdtime) = true,
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "ts"
];
string line = 2 [(gogoproto.jsontag) = "line"];
}
message Sample {
int64 timestamp = 1 [(gogoproto.jsontag) = "ts"];
double value = 2 [(gogoproto.jsontag) = "value"];
@ -189,7 +158,7 @@ message TailRequest {
}
message TailResponse {
StreamAdapter stream = 1 [(gogoproto.customtype) = "Stream"];
StreamAdapter stream = 1 [(gogoproto.customtype) = "github.com/grafana/loki/pkg/push.Stream"];
repeated DroppedStream droppedStreams = 2;
}

@ -1,471 +1,9 @@
package logproto
import (
fmt "fmt"
io "io"
"time"
"github.com/prometheus/common/model"
)
// Stream contains a unique labels set as a string and a set of entries for it.
// We are not using the proto generated version but this custom one so that we
// can improve serialization see benchmark.
type Stream struct {
Labels string `protobuf:"bytes,1,opt,name=labels,proto3" json:"labels"`
Entries []Entry `protobuf:"bytes,2,rep,name=entries,proto3,customtype=EntryAdapter" json:"entries"`
Hash uint64 `protobuf:"varint,3,opt,name=hash,proto3" json:"-"`
}
// Entry is a log entry with a timestamp.
type Entry struct {
Timestamp time.Time `protobuf:"bytes,1,opt,name=timestamp,proto3,stdtime" json:"ts"`
Line string `protobuf:"bytes,2,opt,name=line,proto3" json:"line"`
}
func (m *Stream) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Stream) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Stream) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.Hash != 0 {
i = encodeVarintLogproto(dAtA, i, m.Hash)
i--
dAtA[i] = 0x18
}
if len(m.Entries) > 0 {
for iNdEx := len(m.Entries) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Entries[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintLogproto(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
}
if len(m.Labels) > 0 {
i -= len(m.Labels)
copy(dAtA[i:], m.Labels)
i = encodeVarintLogproto(dAtA, i, uint64(len(m.Labels)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *Entry) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Entry) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Entry) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Line) > 0 {
i -= len(m.Line)
copy(dAtA[i:], m.Line)
i = encodeVarintLogproto(dAtA, i, uint64(len(m.Line)))
i--
dAtA[i] = 0x12
}
n7, err7 := StdTimeMarshalTo(m.Timestamp, dAtA[i-SizeOfStdTime(m.Timestamp):])
if err7 != nil {
return 0, err7
}
i -= n7
i = encodeVarintLogproto(dAtA, i, uint64(n7))
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func (m *Stream) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowLogproto
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: StreamAdapter: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: StreamAdapter: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowLogproto
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthLogproto
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthLogproto
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Labels = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Entries", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowLogproto
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthLogproto
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthLogproto
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Entries = append(m.Entries, Entry{})
if err := m.Entries[len(m.Entries)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType)
}
m.Hash = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowLogproto
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Hash |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipLogproto(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthLogproto
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthLogproto
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Entry) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowLogproto
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: EntryAdapter: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: EntryAdapter: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowLogproto
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthLogproto
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthLogproto
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Line", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowLogproto
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthLogproto
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthLogproto
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Line = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipLogproto(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthLogproto
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthLogproto
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Stream) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Labels)
if l > 0 {
n += 1 + l + sovLogproto(uint64(l))
}
if len(m.Entries) > 0 {
for _, e := range m.Entries {
l = e.Size()
n += 1 + l + sovLogproto(uint64(l))
}
}
if m.Hash != 0 {
n += 1 + sovLogproto(m.Hash)
}
return n
}
func (m *Entry) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = SizeOfStdTime(m.Timestamp)
n += 1 + l + sovLogproto(uint64(l))
l = len(m.Line)
if l > 0 {
n += 1 + l + sovLogproto(uint64(l))
}
return n
}
func (m *Stream) Equal(that interface{}) bool {
if that == nil {
return m == nil
}
that1, ok := that.(*Stream)
if !ok {
that2, ok := that.(Stream)
if ok {
that1 = &that2
} else {
return false
}
}
if that1 == nil {
return m == nil
} else if m == nil {
return false
}
if m.Labels != that1.Labels {
return false
}
if len(m.Entries) != len(that1.Entries) {
return false
}
for i := range m.Entries {
if !m.Entries[i].Equal(that1.Entries[i]) {
return false
}
}
return m.Hash == that1.Hash
}
func (m *Entry) Equal(that interface{}) bool {
if that == nil {
return m == nil
}
that1, ok := that.(*Entry)
if !ok {
that2, ok := that.(Entry)
if ok {
that1 = &that2
} else {
return false
}
}
if that1 == nil {
return m == nil
} else if m == nil {
return false
}
if !m.Timestamp.Equal(that1.Timestamp) {
return false
}
if m.Line != that1.Line {
return false
}
return true
}
func (c *ChunkRef) FingerprintModel() model.Fingerprint {
return model.Fingerprint(c.Fingerprint)
}

@ -0,0 +1,23 @@
module github.com/grafana/loki/pkg/push
go 1.19
require (
github.com/gogo/protobuf v1.3.2
github.com/stretchr/testify v1.8.1
google.golang.org/grpc v1.52.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/net v0.4.0 // indirect
golang.org/x/sys v0.3.0 // indirect
golang.org/x/text v0.5.0 // indirect
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

@ -0,0 +1,69 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 h1:a2S6M0+660BgMNl++4JPlcAO/CjkqYItDEZwkoDQK7c=
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk=
google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

File diff suppressed because it is too large Load Diff

@ -0,0 +1,40 @@
syntax = "proto3";
package logproto;
import "gogoproto/gogo.proto";
import "google/protobuf/timestamp.proto";
option go_package = "github.com/grafana/loki/pkg/push";
service Pusher {
rpc Push(PushRequest) returns (PushResponse) {}
}
message PushRequest {
repeated StreamAdapter streams = 1 [
(gogoproto.jsontag) = "streams",
(gogoproto.customtype) = "Stream"
];
}
message PushResponse {}
message StreamAdapter {
string labels = 1 [(gogoproto.jsontag) = "labels"];
repeated EntryAdapter entries = 2 [
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "entries"
];
// hash contains the original hash of the stream.
uint64 hash = 3 [(gogoproto.jsontag) = "-"];
}
message EntryAdapter {
google.protobuf.Timestamp timestamp = 1 [
(gogoproto.stdtime) = true,
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "ts"
];
string line = 2 [(gogoproto.jsontag) = "line"];
}

@ -1,9 +1,9 @@
package logproto
package push
import (
"errors"
strconv "strconv"
time "time"
"strconv"
"time"
"github.com/gogo/protobuf/types"
)

@ -0,0 +1,465 @@
package push
import (
"fmt"
"io"
"time"
)
// Stream contains a unique labels set as a string and a set of entries for it.
// We are not using the proto generated version but this custom one so that we
// can improve serialization see benchmark.
type Stream struct {
Labels string `protobuf:"bytes,1,opt,name=labels,proto3" json:"labels"`
Entries []Entry `protobuf:"bytes,2,rep,name=entries,proto3,customtype=EntryAdapter" json:"entries"`
Hash uint64 `protobuf:"varint,3,opt,name=hash,proto3" json:"-"`
}
// Entry is a log entry with a timestamp.
type Entry struct {
Timestamp time.Time `protobuf:"bytes,1,opt,name=timestamp,proto3,stdtime" json:"ts"`
Line string `protobuf:"bytes,2,opt,name=line,proto3" json:"line"`
}
func (m *Stream) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Stream) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Stream) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.Hash != 0 {
i = encodeVarintPush(dAtA, i, m.Hash)
i--
dAtA[i] = 0x18
}
if len(m.Entries) > 0 {
for iNdEx := len(m.Entries) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Entries[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintPush(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
}
if len(m.Labels) > 0 {
i -= len(m.Labels)
copy(dAtA[i:], m.Labels)
i = encodeVarintPush(dAtA, i, uint64(len(m.Labels)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *Entry) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Entry) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Entry) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Line) > 0 {
i -= len(m.Line)
copy(dAtA[i:], m.Line)
i = encodeVarintPush(dAtA, i, uint64(len(m.Line)))
i--
dAtA[i] = 0x12
}
n7, err7 := StdTimeMarshalTo(m.Timestamp, dAtA[i-SizeOfStdTime(m.Timestamp):])
if err7 != nil {
return 0, err7
}
i -= n7
i = encodeVarintPush(dAtA, i, uint64(n7))
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func (m *Stream) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPush
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: StreamAdapter: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: StreamAdapter: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPush
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthPush
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthPush
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Labels = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Entries", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPush
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthPush
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthPush
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Entries = append(m.Entries, Entry{})
if err := m.Entries[len(m.Entries)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType)
}
m.Hash = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPush
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Hash |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipPush(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthPush
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthPush
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Entry) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPush
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: EntryAdapter: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: EntryAdapter: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPush
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthPush
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthPush
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Line", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPush
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthPush
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthPush
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Line = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipPush(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthPush
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthPush
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Stream) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Labels)
if l > 0 {
n += 1 + l + sovPush(uint64(l))
}
if len(m.Entries) > 0 {
for _, e := range m.Entries {
l = e.Size()
n += 1 + l + sovPush(uint64(l))
}
}
if m.Hash != 0 {
n += 1 + sovPush(m.Hash)
}
return n
}
func (m *Entry) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = SizeOfStdTime(m.Timestamp)
n += 1 + l + sovPush(uint64(l))
l = len(m.Line)
if l > 0 {
n += 1 + l + sovPush(uint64(l))
}
return n
}
func (m *Stream) Equal(that interface{}) bool {
if that == nil {
return m == nil
}
that1, ok := that.(*Stream)
if !ok {
that2, ok := that.(Stream)
if ok {
that1 = &that2
} else {
return false
}
}
if that1 == nil {
return m == nil
} else if m == nil {
return false
}
if m.Labels != that1.Labels {
return false
}
if len(m.Entries) != len(that1.Entries) {
return false
}
for i := range m.Entries {
if !m.Entries[i].Equal(that1.Entries[i]) {
return false
}
}
return m.Hash == that1.Hash
}
func (m *Entry) Equal(that interface{}) bool {
if that == nil {
return m == nil
}
that1, ok := that.(*Entry)
if !ok {
that2, ok := that.(Entry)
if ok {
that1 = &that2
} else {
return false
}
}
if that1 == nil {
return m == nil
} else if m == nil {
return false
}
if !m.Timestamp.Equal(that1.Timestamp) {
return false
}
if m.Line != that1.Line {
return false
}
return true
}

@ -1,4 +1,4 @@
package logproto
package push
import (
"testing"

@ -12,6 +12,8 @@ import (
github_com_grafana_loki_pkg_logproto "github.com/grafana/loki/pkg/logproto"
logproto "github.com/grafana/loki/pkg/logproto"
stats "github.com/grafana/loki/pkg/logqlmodel/stats"
_ "github.com/grafana/loki/pkg/push"
github_com_grafana_loki_pkg_push "github.com/grafana/loki/pkg/push"
queryrangebase "github.com/grafana/loki/pkg/querier/queryrange/queryrangebase"
_ "github.com/grafana/loki/pkg/querier/queryrange/queryrangebase/definitions"
github_com_grafana_loki_pkg_querier_queryrange_queryrangebase_definitions "github.com/grafana/loki/pkg/querier/queryrange/queryrangebase/definitions"
@ -596,8 +598,8 @@ func (m *LokiLabelNamesResponse) GetStatistics() stats.Result {
}
type LokiData struct {
ResultType string `protobuf:"bytes,1,opt,name=ResultType,proto3" json:"resultType"`
Result []github_com_grafana_loki_pkg_logproto.Stream `protobuf:"bytes,2,rep,name=Result,proto3,customtype=github.com/grafana/loki/pkg/logproto.Stream" json:"result"`
ResultType string `protobuf:"bytes,1,opt,name=ResultType,proto3" json:"resultType"`
Result []github_com_grafana_loki_pkg_push.Stream `protobuf:"bytes,2,rep,name=Result,proto3,customtype=github.com/grafana/loki/pkg/push.Stream" json:"result"`
}
func (m *LokiData) Reset() { *m = LokiData{} }
@ -746,69 +748,69 @@ func init() {
}
var fileDescriptor_51b9d53b40d11902 = []byte{
// 978 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x55, 0xdd, 0x6e, 0x23, 0x35,
0x14, 0x8e, 0xf3, 0xd7, 0xc4, 0x65, 0x0b, 0xb8, 0x65, 0x77, 0x54, 0xd0, 0x4c, 0x14, 0x09, 0x08,
0x02, 0x66, 0x44, 0x97, 0x1f, 0x89, 0x3f, 0xb1, 0x43, 0x41, 0x54, 0x5a, 0x21, 0x34, 0x9b, 0x17,
0x70, 0x3a, 0xee, 0x64, 0xd4, 0xf9, 0xab, 0xed, 0xac, 0xe8, 0x1d, 0x0f, 0x00, 0xd2, 0xbe, 0x05,
0x08, 0x78, 0x02, 0x9e, 0xa0, 0x97, 0xbd, 0x5c, 0x55, 0x62, 0xa0, 0xe9, 0x0d, 0xe4, 0xaa, 0x8f,
0x80, 0x6c, 0xcf, 0x24, 0x4e, 0x7f, 0xd8, 0xa4, 0x7b, 0xb3, 0x17, 0x7b, 0x93, 0x9c, 0x73, 0x7c,
0x3e, 0xdb, 0xe7, 0x3b, 0xdf, 0xf1, 0xc0, 0x37, 0xb3, 0xfd, 0xc0, 0x39, 0x18, 0x11, 0x1a, 0x12,
0x2a, 0xff, 0x0f, 0x29, 0x4e, 0x02, 0xa2, 0x99, 0x76, 0x46, 0x53, 0x9e, 0x22, 0x38, 0x8b, 0x6c,
0x6e, 0x04, 0x69, 0x90, 0xca, 0xb0, 0x23, 0x2c, 0x95, 0xb1, 0x69, 0x05, 0x69, 0x1a, 0x44, 0xc4,
0x91, 0xde, 0x60, 0xb4, 0xe7, 0xf0, 0x30, 0x26, 0x8c, 0xe3, 0x38, 0x2b, 0x12, 0x5e, 0x15, 0x67,
0x45, 0x69, 0xa0, 0x90, 0xa5, 0x51, 0x2c, 0x76, 0x8a, 0xc5, 0x83, 0x28, 0x4e, 0x7d, 0x12, 0x39,
0x8c, 0x63, 0xce, 0xd4, 0x6f, 0x91, 0xf1, 0xe5, 0x13, 0xaf, 0x3a, 0xc0, 0x8c, 0x38, 0x3e, 0xd9,
0x0b, 0x93, 0x90, 0x87, 0x69, 0xc2, 0x74, 0xbb, 0xd8, 0xe4, 0xc3, 0xc5, 0x36, 0xb9, 0x58, 0x7e,
0xf7, 0xb8, 0x0a, 0x57, 0xef, 0xa7, 0xfb, 0xa1, 0x47, 0x0e, 0x46, 0x84, 0x71, 0xb4, 0x01, 0x1b,
0x32, 0xc7, 0x00, 0x1d, 0xd0, 0x6b, 0x7b, 0xca, 0x11, 0xd1, 0x28, 0x8c, 0x43, 0x6e, 0x54, 0x3b,
0xa0, 0x77, 0xcb, 0x53, 0x0e, 0x42, 0xb0, 0xce, 0x38, 0xc9, 0x8c, 0x5a, 0x07, 0xf4, 0x6a, 0x9e,
0xb4, 0xd1, 0x26, 0x6c, 0x85, 0x09, 0x27, 0xf4, 0x21, 0x8e, 0x8c, 0xb6, 0x8c, 0x4f, 0x7d, 0xf4,
0x39, 0x5c, 0x61, 0x1c, 0x53, 0xde, 0x67, 0x46, 0xbd, 0x03, 0x7a, 0xab, 0x5b, 0x9b, 0xb6, 0xa2,
0xd6, 0x2e, 0xa9, 0xb5, 0xfb, 0x25, 0xb5, 0x6e, 0xeb, 0x28, 0xb7, 0x2a, 0x8f, 0xfe, 0xb2, 0x80,
0x57, 0x82, 0xd0, 0xc7, 0xb0, 0x41, 0x12, 0xbf, 0xcf, 0x8c, 0xc6, 0x12, 0x68, 0x05, 0x41, 0xef,
0xc1, 0xb6, 0x1f, 0x52, 0xb2, 0x2b, 0x38, 0x33, 0x9a, 0x1d, 0xd0, 0x5b, 0xdb, 0x5a, 0xb7, 0xa7,
0xad, 0xda, 0x2e, 0x97, 0xbc, 0x59, 0x96, 0x28, 0x2f, 0xc3, 0x7c, 0x68, 0xac, 0x48, 0x26, 0xa4,
0x8d, 0xba, 0xb0, 0xc9, 0x86, 0x98, 0xfa, 0xcc, 0x68, 0x75, 0x6a, 0xbd, 0xb6, 0x0b, 0x27, 0xb9,
0x55, 0x44, 0xbc, 0xe2, 0xbf, 0xfb, 0x2f, 0x80, 0x48, 0x50, 0xba, 0x93, 0x30, 0x8e, 0x13, 0x7e,
0x13, 0x66, 0x3f, 0x85, 0x4d, 0x21, 0xb2, 0x3e, 0x93, 0xdc, 0x2e, 0x5a, 0x6a, 0x81, 0x99, 0xaf,
0xb5, 0xbe, 0x54, 0xad, 0x8d, 0x2b, 0x6b, 0x6d, 0x5e, 0x5b, 0xeb, 0x6f, 0x75, 0xf8, 0x82, 0x92,
0x0f, 0xcb, 0xd2, 0x84, 0x11, 0x01, 0x7a, 0xc0, 0x31, 0x1f, 0x31, 0x55, 0x66, 0x01, 0x92, 0x11,
0xaf, 0x58, 0x41, 0x5f, 0xc0, 0xfa, 0x36, 0xe6, 0x58, 0x96, 0xbc, 0xba, 0xb5, 0x61, 0x6b, 0xa2,
0x14, 0x7b, 0x89, 0x35, 0xf7, 0xb6, 0xa8, 0x6a, 0x92, 0x5b, 0x6b, 0x3e, 0xe6, 0xf8, 0x9d, 0x34,
0x0e, 0x39, 0x89, 0x33, 0x7e, 0xe8, 0x49, 0x24, 0xfa, 0x00, 0xb6, 0xbf, 0xa2, 0x34, 0xa5, 0xfd,
0xc3, 0x8c, 0x48, 0x8a, 0xda, 0xee, 0x9d, 0x49, 0x6e, 0xad, 0x93, 0x32, 0xa8, 0x21, 0x66, 0x99,
0xe8, 0x2d, 0xd8, 0x90, 0x8e, 0x24, 0xa5, 0xed, 0xae, 0x4f, 0x72, 0xeb, 0x45, 0x09, 0xd1, 0xd2,
0x55, 0xc6, 0x3c, 0x87, 0x8d, 0x85, 0x38, 0x9c, 0xb6, 0xb2, 0xa9, 0xb7, 0xd2, 0x80, 0x2b, 0x0f,
0x09, 0x65, 0x62, 0x9b, 0x15, 0x19, 0x2f, 0x5d, 0x74, 0x0f, 0x42, 0x41, 0x4c, 0xc8, 0x78, 0xb8,
0x2b, 0xf4, 0x24, 0xc8, 0xb8, 0x65, 0xab, 0x97, 0xc1, 0x23, 0x6c, 0x14, 0x71, 0x17, 0x15, 0x2c,
0x68, 0x89, 0x9e, 0x66, 0xa3, 0xdf, 0x01, 0x5c, 0xf9, 0x86, 0x60, 0x9f, 0x50, 0x66, 0xb4, 0x3b,
0xb5, 0xde, 0xea, 0xd6, 0xeb, 0xb6, 0xfe, 0x36, 0x7c, 0x47, 0xd3, 0x98, 0xf0, 0x21, 0x19, 0xb1,
0xb2, 0x41, 0x2a, 0xdb, 0xdd, 0x3f, 0xc9, 0xad, 0x41, 0x10, 0xf2, 0xe1, 0x68, 0x60, 0xef, 0xa6,
0xb1, 0x13, 0x50, 0xbc, 0x87, 0x13, 0xec, 0x44, 0xe9, 0x7e, 0xe8, 0x2c, 0xfd, 0x1e, 0x5d, 0x7b,
0xce, 0x24, 0xb7, 0xc0, 0xbb, 0x5e, 0x79, 0xc5, 0xee, 0x9f, 0x00, 0xbe, 0x2c, 0x3a, 0xfc, 0x40,
0xec, 0xcd, 0xb4, 0xc1, 0x88, 0x31, 0xdf, 0x1d, 0x1a, 0x40, 0xc8, 0xcc, 0x53, 0x8e, 0xfe, 0x58,
0x54, 0x9f, 0xea, 0xb1, 0xa8, 0x2d, 0xff, 0x58, 0x94, 0xd3, 0x50, 0xbf, 0x72, 0x1a, 0x1a, 0xd7,
0x4e, 0xc3, 0x8f, 0x35, 0x35, 0xf9, 0x65, 0x7d, 0x4b, 0xcc, 0xc4, 0xd7, 0xd3, 0x99, 0xa8, 0xc9,
0xdb, 0x4e, 0xa5, 0xa6, 0xf6, 0xda, 0xf1, 0x49, 0xc2, 0xc3, 0xbd, 0x90, 0xd0, 0x27, 0x4c, 0x86,
0x26, 0xb7, 0xda, 0xbc, 0xdc, 0x74, 0xad, 0xd4, 0x9f, 0x79, 0xad, 0x5c, 0x98, 0x8e, 0xc6, 0x0d,
0xa6, 0xa3, 0xfb, 0x33, 0x80, 0xaf, 0x88, 0x76, 0xdc, 0xc7, 0x03, 0x12, 0x7d, 0x8b, 0xe3, 0x99,
0xe4, 0x34, 0x71, 0x81, 0xa7, 0x12, 0x57, 0xf5, 0xe6, 0xe2, 0xaa, 0xcd, 0xc4, 0xd5, 0x3d, 0xaf,
0xc2, 0xdb, 0x17, 0x6f, 0xba, 0x84, 0x78, 0xde, 0xd0, 0xc4, 0xd3, 0x76, 0xd1, 0x73, 0x71, 0x2c,
0x20, 0x8e, 0x5f, 0x01, 0x6c, 0x95, 0x5f, 0x1b, 0x64, 0x43, 0xa8, 0x60, 0xf2, 0x83, 0xa2, 0x88,
0x5e, 0x13, 0x60, 0x3a, 0x8d, 0x7a, 0x5a, 0x06, 0x4a, 0x60, 0x53, 0x79, 0xc5, 0xbc, 0xde, 0xd1,
0xe6, 0x95, 0x53, 0x82, 0xe3, 0x7b, 0x3e, 0xce, 0x38, 0xa1, 0xee, 0x67, 0xe2, 0x16, 0x27, 0xb9,
0xf5, 0xf6, 0xff, 0x51, 0x74, 0x01, 0x2b, 0x1a, 0xac, 0xce, 0xf5, 0x8a, 0x53, 0xba, 0x3f, 0x01,
0xf8, 0x92, 0xb8, 0xac, 0xa0, 0x67, 0xaa, 0x8c, 0x6d, 0xd8, 0xa2, 0x85, 0x5d, 0xa8, 0xb8, 0x6b,
0xcf, 0x53, 0x7b, 0x05, 0x9d, 0x6e, 0xfd, 0x28, 0xb7, 0x80, 0x37, 0x45, 0xa2, 0xbb, 0x73, 0x54,
0x56, 0xaf, 0xa2, 0x52, 0x40, 0x2a, 0x73, 0xe4, 0xfd, 0x51, 0x85, 0x68, 0x27, 0xf1, 0xc9, 0xf7,
0x42, 0x80, 0x33, 0xad, 0x8e, 0x2e, 0xdd, 0xe8, 0xb5, 0x19, 0x31, 0x97, 0xf3, 0xdd, 0x4f, 0x4e,
0x72, 0xeb, 0xa3, 0x85, 0x98, 0xb9, 0x0c, 0xd6, 0x4a, 0xd0, 0xc5, 0x5b, 0x7d, 0xe6, 0xc5, 0xeb,
0xbe, 0x7f, 0x7c, 0x6a, 0x56, 0x1e, 0x9f, 0x9a, 0x95, 0xf3, 0x53, 0x13, 0xfc, 0x30, 0x36, 0xc1,
0x2f, 0x63, 0x13, 0x1c, 0x8d, 0x4d, 0x70, 0x3c, 0x36, 0xc1, 0xdf, 0x63, 0x13, 0xfc, 0x33, 0x36,
0x2b, 0xe7, 0x63, 0x13, 0x3c, 0x3a, 0x33, 0x2b, 0xc7, 0x67, 0x66, 0xe5, 0xf1, 0x99, 0x59, 0x19,
0x34, 0x25, 0x11, 0x77, 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xb4, 0xd5, 0xfa, 0x96, 0xd9, 0x0c,
0x00, 0x00,
// 992 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x56, 0xdd, 0x6e, 0x23, 0xb5,
0x17, 0x8f, 0xf3, 0x1d, 0xf7, 0xbf, 0xfd, 0x83, 0x5b, 0x76, 0x47, 0x05, 0xcd, 0x44, 0x91, 0x60,
0x83, 0x04, 0x13, 0xd1, 0xe5, 0x43, 0x7c, 0x08, 0xb1, 0x43, 0x41, 0x54, 0x5a, 0x21, 0x34, 0x9b,
0x17, 0x70, 0x3a, 0xee, 0x64, 0xe8, 0x7c, 0xd5, 0xf6, 0xac, 0xe8, 0x1d, 0x0f, 0x00, 0xd2, 0x3e,
0x05, 0x20, 0xc1, 0x13, 0xf0, 0x04, 0xbd, 0xec, 0xe5, 0xaa, 0x12, 0x03, 0x4d, 0x6f, 0x20, 0x57,
0x7d, 0x04, 0x64, 0x7b, 0x26, 0x71, 0xba, 0x6d, 0xb7, 0xe9, 0xde, 0xec, 0x05, 0x37, 0xc9, 0x39,
0xc7, 0xe7, 0x67, 0xfb, 0xfc, 0xce, 0xef, 0x38, 0x81, 0x77, 0xd3, 0x3d, 0x7f, 0xb0, 0x9f, 0x11,
0x1a, 0x10, 0x2a, 0xbf, 0x0f, 0x28, 0x8e, 0x7d, 0xa2, 0x99, 0x76, 0x4a, 0x13, 0x9e, 0x20, 0x38,
0x8f, 0x6c, 0xac, 0xfb, 0x89, 0x9f, 0xc8, 0xf0, 0x40, 0x58, 0x2a, 0x63, 0xc3, 0xf2, 0x93, 0xc4,
0x0f, 0xc9, 0x40, 0x7a, 0xa3, 0x6c, 0x77, 0xc0, 0x83, 0x88, 0x30, 0x8e, 0xa3, 0xb4, 0x48, 0x78,
0x55, 0x9c, 0x15, 0x26, 0xbe, 0x42, 0x96, 0x46, 0xb1, 0xd8, 0x2d, 0x16, 0xf7, 0xc3, 0x28, 0xf1,
0x48, 0x38, 0x60, 0x1c, 0x73, 0xa6, 0x3e, 0x8b, 0x8c, 0x35, 0x91, 0x91, 0x66, 0x6c, 0x2c, 0x3f,
0x8a, 0xe0, 0xe7, 0xcf, 0xbc, 0xff, 0x08, 0x33, 0x32, 0xf0, 0xc8, 0x6e, 0x10, 0x07, 0x3c, 0x48,
0x62, 0xa6, 0xdb, 0xc5, 0x26, 0xef, 0x5f, 0x6f, 0x93, 0xf3, 0x9c, 0xf4, 0x8e, 0xaa, 0x70, 0xe5,
0x41, 0xb2, 0x17, 0xb8, 0x64, 0x3f, 0x23, 0x8c, 0xa3, 0x75, 0xd8, 0x90, 0x39, 0x06, 0xe8, 0x82,
0x7e, 0xc7, 0x55, 0x8e, 0x88, 0x86, 0x41, 0x14, 0x70, 0xa3, 0xda, 0x05, 0xfd, 0x5b, 0xae, 0x72,
0x10, 0x82, 0x75, 0xc6, 0x49, 0x6a, 0xd4, 0xba, 0xa0, 0x5f, 0x73, 0xa5, 0x8d, 0x36, 0x60, 0x3b,
0x88, 0x39, 0xa1, 0x8f, 0x70, 0x68, 0x74, 0x64, 0x7c, 0xe6, 0xa3, 0x4f, 0x61, 0x8b, 0x71, 0x4c,
0xf9, 0x90, 0x19, 0xf5, 0x2e, 0xe8, 0xaf, 0x6c, 0x6e, 0xd8, 0x8a, 0x6f, 0xbb, 0xe4, 0xdb, 0x1e,
0x96, 0x7c, 0x3b, 0xed, 0xc3, 0xdc, 0xaa, 0x3c, 0xfe, 0xd3, 0x02, 0x6e, 0x09, 0x42, 0x1f, 0xc1,
0x06, 0x89, 0xbd, 0x21, 0x33, 0x1a, 0x4b, 0xa0, 0x15, 0x04, 0xbd, 0x03, 0x3b, 0x5e, 0x40, 0xc9,
0x8e, 0xe0, 0xcc, 0x68, 0x76, 0x41, 0x7f, 0x75, 0x73, 0xcd, 0x9e, 0xf5, 0x6f, 0xab, 0x5c, 0x72,
0xe7, 0x59, 0xa2, 0xbc, 0x14, 0xf3, 0xb1, 0xd1, 0x92, 0x4c, 0x48, 0x1b, 0xf5, 0x60, 0x93, 0x8d,
0x31, 0xf5, 0x98, 0xd1, 0xee, 0xd6, 0xfa, 0x1d, 0x07, 0x4e, 0x73, 0xab, 0x88, 0xb8, 0xc5, 0x77,
0xef, 0x1f, 0x00, 0x91, 0xa0, 0x74, 0x3b, 0x66, 0x1c, 0xc7, 0xfc, 0x26, 0xcc, 0x7e, 0x02, 0x9b,
0x42, 0x79, 0x43, 0x26, 0xb9, 0xbd, 0x6e, 0xa9, 0x05, 0x66, 0xb1, 0xd6, 0xfa, 0x52, 0xb5, 0x36,
0x2e, 0xac, 0xb5, 0x79, 0x69, 0xad, 0xbf, 0xd6, 0xe1, 0xff, 0x94, 0x7c, 0x58, 0x9a, 0xc4, 0x8c,
0x08, 0xd0, 0x43, 0x8e, 0x79, 0xc6, 0x54, 0x99, 0x05, 0x48, 0x46, 0xdc, 0x62, 0x05, 0x7d, 0x06,
0xeb, 0x5b, 0x98, 0x63, 0x59, 0xf2, 0xca, 0xe6, 0xba, 0xad, 0x89, 0x52, 0xec, 0x25, 0xd6, 0x9c,
0xdb, 0xa2, 0xaa, 0x69, 0x6e, 0xad, 0x7a, 0x98, 0xe3, 0xb7, 0x92, 0x28, 0xe0, 0x24, 0x4a, 0xf9,
0x81, 0x2b, 0x91, 0xe8, 0x3d, 0xd8, 0xf9, 0x82, 0xd2, 0x84, 0x0e, 0x0f, 0x52, 0x22, 0x29, 0xea,
0x38, 0x77, 0xa6, 0xb9, 0xb5, 0x46, 0xca, 0xa0, 0x86, 0x98, 0x67, 0xa2, 0x37, 0x61, 0x43, 0x3a,
0x92, 0x94, 0x8e, 0xb3, 0x36, 0xcd, 0xad, 0xff, 0x4b, 0x88, 0x96, 0xae, 0x32, 0x16, 0x39, 0x6c,
0x5c, 0x8b, 0xc3, 0x59, 0x2b, 0x9b, 0x7a, 0x2b, 0x0d, 0xd8, 0x7a, 0x44, 0x28, 0x13, 0xdb, 0xb4,
0x64, 0xbc, 0x74, 0xd1, 0x7d, 0x08, 0x05, 0x31, 0x01, 0xe3, 0xc1, 0x8e, 0xd0, 0x93, 0x20, 0xe3,
0x96, 0xad, 0x9e, 0x0b, 0x97, 0xb0, 0x2c, 0xe4, 0x0e, 0x2a, 0x58, 0xd0, 0x12, 0x5d, 0xcd, 0x46,
0xbf, 0x01, 0xd8, 0xfa, 0x8a, 0x60, 0x8f, 0x50, 0x66, 0x74, 0xba, 0xb5, 0xfe, 0xca, 0xe6, 0xeb,
0xb6, 0xfe, 0x36, 0x7c, 0x43, 0x93, 0x88, 0xf0, 0x31, 0xc9, 0x58, 0xd9, 0x20, 0x95, 0xed, 0xec,
0x1d, 0xe7, 0xd6, 0xc8, 0x0f, 0xf8, 0x38, 0x1b, 0xd9, 0x3b, 0x49, 0x34, 0xf0, 0x29, 0xde, 0xc5,
0x31, 0x1e, 0x84, 0xc9, 0x5e, 0x30, 0x58, 0xfa, 0x3d, 0xba, 0xf4, 0x9c, 0x69, 0x6e, 0x81, 0xb7,
0xdd, 0xf2, 0x8a, 0xbd, 0x3f, 0x00, 0x7c, 0x59, 0x74, 0xf8, 0xa1, 0xd8, 0x9b, 0x69, 0x83, 0x11,
0x61, 0xbe, 0x33, 0x36, 0x80, 0x90, 0x99, 0xab, 0x1c, 0xfd, 0xb1, 0xa8, 0x3e, 0xd7, 0x63, 0x51,
0x5b, 0xfe, 0xb1, 0x28, 0xa7, 0xa1, 0x7e, 0xe1, 0x34, 0x34, 0x2e, 0x9d, 0x86, 0x1f, 0x6a, 0x6a,
0xf2, 0xcb, 0xfa, 0x96, 0x98, 0x89, 0x2f, 0x67, 0x33, 0x51, 0x93, 0xb7, 0x9d, 0x49, 0x4d, 0xed,
0xb5, 0xed, 0x91, 0x98, 0x07, 0xbb, 0x01, 0xa1, 0xcf, 0x98, 0x0c, 0x4d, 0x6e, 0xb5, 0x45, 0xb9,
0xe9, 0x5a, 0xa9, 0xbf, 0xf0, 0x5a, 0x39, 0x37, 0x1d, 0x8d, 0x1b, 0x4c, 0x47, 0xef, 0x67, 0x00,
0x5f, 0x11, 0xed, 0x78, 0x80, 0x47, 0x24, 0xfc, 0x1a, 0x47, 0x73, 0xc9, 0x69, 0xe2, 0x02, 0xcf,
0x25, 0xae, 0xea, 0xcd, 0xc5, 0x55, 0x9b, 0x8b, 0xab, 0x77, 0x56, 0x85, 0xb7, 0xcf, 0xdf, 0x74,
0x09, 0xf1, 0xbc, 0xa1, 0x89, 0xa7, 0xe3, 0xa0, 0xff, 0xc4, 0x71, 0x0d, 0x71, 0xfc, 0x04, 0x60,
0xbb, 0xfc, 0xb5, 0x41, 0x36, 0x84, 0x0a, 0x26, 0x7f, 0x50, 0x14, 0xd1, 0xab, 0x02, 0x4c, 0x67,
0x51, 0x57, 0xcb, 0x40, 0xdf, 0xc2, 0xa6, 0xf2, 0x8a, 0x79, 0xbd, 0xa3, 0xcd, 0x2b, 0xa7, 0x04,
0x47, 0xf7, 0x3d, 0x9c, 0x72, 0x42, 0x9d, 0x0f, 0xc5, 0x2d, 0x8e, 0x73, 0xeb, 0xee, 0x55, 0x14,
0xc9, 0xff, 0x82, 0x0a, 0x27, 0x9a, 0xab, 0xce, 0x74, 0x8b, 0x13, 0x7a, 0x3f, 0x02, 0xf8, 0x92,
0xb8, 0xa8, 0xa0, 0x66, 0xa6, 0x8a, 0x2d, 0xd8, 0xa6, 0x85, 0x5d, 0x28, 0xb8, 0x67, 0x2f, 0xd2,
0x7a, 0x01, 0x95, 0x4e, 0xfd, 0x30, 0xb7, 0x80, 0x3b, 0x43, 0xa2, 0x7b, 0x0b, 0x34, 0x56, 0x2f,
0xa2, 0x51, 0x40, 0x2a, 0x0b, 0xc4, 0xfd, 0x5e, 0x85, 0x68, 0x3b, 0xf6, 0xc8, 0x77, 0x42, 0x7c,
0x73, 0x9d, 0x66, 0x4f, 0xdd, 0xe8, 0xb5, 0x39, 0x29, 0x4f, 0xe7, 0x3b, 0x1f, 0x1f, 0xe7, 0xd6,
0x07, 0x57, 0xb1, 0x72, 0x05, 0x58, 0x2b, 0x41, 0x17, 0x6e, 0xf5, 0x85, 0x17, 0xae, 0xf3, 0xee,
0xd1, 0x89, 0x59, 0x79, 0x72, 0x62, 0x56, 0xce, 0x4e, 0x4c, 0xf0, 0xfd, 0xc4, 0x04, 0xbf, 0x4c,
0x4c, 0x70, 0x38, 0x31, 0xc1, 0xd1, 0xc4, 0x04, 0x7f, 0x4d, 0x4c, 0xf0, 0xf7, 0xc4, 0xac, 0x9c,
0x4d, 0x4c, 0xf0, 0xf8, 0xd4, 0xac, 0x1c, 0x9d, 0x9a, 0x95, 0x27, 0xa7, 0x66, 0x65, 0xd4, 0x94,
0x44, 0xdc, 0xfb, 0x37, 0x00, 0x00, 0xff, 0xff, 0xe5, 0xe6, 0xf4, 0x15, 0xea, 0x0c, 0x00, 0x00,
}
func (this *LokiRequest) Equal(that interface{}) bool {
@ -4124,7 +4126,7 @@ func (m *LokiData) Unmarshal(dAtA []byte) error {
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Result = append(m.Result, github_com_grafana_loki_pkg_logproto.Stream{})
m.Result = append(m.Result, github_com_grafana_loki_pkg_push.Stream{})
if err := m.Result[len(m.Result)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}

@ -6,6 +6,7 @@ import "gogoproto/gogo.proto";
import "google/protobuf/timestamp.proto";
import "pkg/logproto/logproto.proto";
import "pkg/logqlmodel/stats/stats.proto";
import "pkg/push/push.proto";
import "pkg/querier/queryrange/queryrangebase/definitions/definitions.proto";
import "pkg/querier/queryrange/queryrangebase/queryrange.proto";
@ -125,7 +126,7 @@ message LokiData {
repeated logproto.StreamAdapter Result = 2 [
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "result",
(gogoproto.customtype) = "github.com/grafana/loki/pkg/logproto.Stream"
(gogoproto.customtype) = "github.com/grafana/loki/pkg/push.Stream"
];
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,40 @@
syntax = "proto3";
package logproto;
import "gogoproto/gogo.proto";
import "google/protobuf/timestamp.proto";
option go_package = "github.com/grafana/loki/pkg/push";
service Pusher {
rpc Push(PushRequest) returns (PushResponse) {}
}
message PushRequest {
repeated StreamAdapter streams = 1 [
(gogoproto.jsontag) = "streams",
(gogoproto.customtype) = "Stream"
];
}
message PushResponse {}
message StreamAdapter {
string labels = 1 [(gogoproto.jsontag) = "labels"];
repeated EntryAdapter entries = 2 [
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "entries"
];
// hash contains the original hash of the stream.
uint64 hash = 3 [(gogoproto.jsontag) = "-"];
}
message EntryAdapter {
google.protobuf.Timestamp timestamp = 1 [
(gogoproto.stdtime) = true,
(gogoproto.nullable) = false,
(gogoproto.jsontag) = "ts"
];
string line = 2 [(gogoproto.jsontag) = "line"];
}

@ -0,0 +1,106 @@
package push
import (
"errors"
"strconv"
"time"
"github.com/gogo/protobuf/types"
)
const (
// Seconds field of the earliest valid Timestamp.
// This is time.Date(1, 1, 1, 0, 0, 0, 0, time.UTC).Unix().
minValidSeconds = -62135596800
// Seconds field just after the latest valid Timestamp.
// This is time.Date(10000, 1, 1, 0, 0, 0, 0, time.UTC).Unix().
maxValidSeconds = 253402300800
)
// validateTimestamp determines whether a Timestamp is valid.
// A valid timestamp represents a time in the range
// [0001-01-01, 10000-01-01) and has a Nanos field
// in the range [0, 1e9).
//
// If the Timestamp is valid, validateTimestamp returns nil.
// Otherwise, it returns an error that describes
// the problem.
//
// Every valid Timestamp can be represented by a time.Time, but the converse is not true.
func validateTimestamp(ts *types.Timestamp) error {
if ts == nil {
return errors.New("timestamp: nil Timestamp")
}
if ts.Seconds < minValidSeconds {
return errors.New("timestamp: " + formatTimestamp(ts) + " before 0001-01-01")
}
if ts.Seconds >= maxValidSeconds {
return errors.New("timestamp: " + formatTimestamp(ts) + " after 10000-01-01")
}
if ts.Nanos < 0 || ts.Nanos >= 1e9 {
return errors.New("timestamp: " + formatTimestamp(ts) + ": nanos not in range [0, 1e9)")
}
return nil
}
// formatTimestamp is equivalent to fmt.Sprintf("%#v", ts)
// but avoids the escape incurred by using fmt.Sprintf, eliminating
// unnecessary heap allocations.
func formatTimestamp(ts *types.Timestamp) string {
if ts == nil {
return "nil"
}
seconds := strconv.FormatInt(ts.Seconds, 10)
nanos := strconv.FormatInt(int64(ts.Nanos), 10)
return "&types.Timestamp{Seconds: " + seconds + ",\nNanos: " + nanos + ",\n}"
}
func SizeOfStdTime(t time.Time) int {
ts, err := timestampProto(t)
if err != nil {
return 0
}
return ts.Size()
}
func StdTimeMarshalTo(t time.Time, data []byte) (int, error) {
ts, err := timestampProto(t)
if err != nil {
return 0, err
}
return ts.MarshalTo(data)
}
func StdTimeUnmarshal(t *time.Time, data []byte) error {
ts := &types.Timestamp{}
if err := ts.Unmarshal(data); err != nil {
return err
}
tt, err := timestampFromProto(ts)
if err != nil {
return err
}
*t = tt
return nil
}
func timestampFromProto(ts *types.Timestamp) (time.Time, error) {
// Don't return the zero value on error, because corresponds to a valid
// timestamp. Instead return whatever time.Unix gives us.
var t time.Time
if ts == nil {
t = time.Unix(0, 0).UTC() // treat nil like the empty Timestamp
} else {
t = time.Unix(ts.Seconds, int64(ts.Nanos)).UTC()
}
return t, validateTimestamp(ts)
}
func timestampProto(t time.Time) (types.Timestamp, error) {
ts := types.Timestamp{
Seconds: t.Unix(),
Nanos: int32(t.Nanosecond()),
}
return ts, validateTimestamp(&ts)
}

@ -0,0 +1,465 @@
package push
import (
"fmt"
"io"
"time"
)
// Stream contains a unique labels set as a string and a set of entries for it.
// We are not using the proto generated version but this custom one so that we
// can improve serialization see benchmark.
type Stream struct {
Labels string `protobuf:"bytes,1,opt,name=labels,proto3" json:"labels"`
Entries []Entry `protobuf:"bytes,2,rep,name=entries,proto3,customtype=EntryAdapter" json:"entries"`
Hash uint64 `protobuf:"varint,3,opt,name=hash,proto3" json:"-"`
}
// Entry is a log entry with a timestamp.
type Entry struct {
Timestamp time.Time `protobuf:"bytes,1,opt,name=timestamp,proto3,stdtime" json:"ts"`
Line string `protobuf:"bytes,2,opt,name=line,proto3" json:"line"`
}
func (m *Stream) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Stream) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Stream) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if m.Hash != 0 {
i = encodeVarintPush(dAtA, i, m.Hash)
i--
dAtA[i] = 0x18
}
if len(m.Entries) > 0 {
for iNdEx := len(m.Entries) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.Entries[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintPush(dAtA, i, uint64(size))
}
i--
dAtA[i] = 0x12
}
}
if len(m.Labels) > 0 {
i -= len(m.Labels)
copy(dAtA[i:], m.Labels)
i = encodeVarintPush(dAtA, i, uint64(len(m.Labels)))
i--
dAtA[i] = 0xa
}
return len(dAtA) - i, nil
}
func (m *Entry) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *Entry) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *Entry) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Line) > 0 {
i -= len(m.Line)
copy(dAtA[i:], m.Line)
i = encodeVarintPush(dAtA, i, uint64(len(m.Line)))
i--
dAtA[i] = 0x12
}
n7, err7 := StdTimeMarshalTo(m.Timestamp, dAtA[i-SizeOfStdTime(m.Timestamp):])
if err7 != nil {
return 0, err7
}
i -= n7
i = encodeVarintPush(dAtA, i, uint64(n7))
i--
dAtA[i] = 0xa
return len(dAtA) - i, nil
}
func (m *Stream) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPush
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: StreamAdapter: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: StreamAdapter: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Labels", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPush
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthPush
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthPush
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Labels = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Entries", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPush
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthPush
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthPush
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Entries = append(m.Entries, Entry{})
if err := m.Entries[len(m.Entries)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Hash", wireType)
}
m.Hash = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPush
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Hash |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipPush(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthPush
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthPush
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Entry) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPush
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: EntryAdapter: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: EntryAdapter: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Timestamp", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPush
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthPush
}
postIndex := iNdEx + msglen
if postIndex < 0 {
return ErrInvalidLengthPush
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := StdTimeUnmarshal(&m.Timestamp, dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Line", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowPush
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthPush
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthPush
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Line = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipPush(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthPush
}
if (iNdEx + skippy) < 0 {
return ErrInvalidLengthPush
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Stream) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = len(m.Labels)
if l > 0 {
n += 1 + l + sovPush(uint64(l))
}
if len(m.Entries) > 0 {
for _, e := range m.Entries {
l = e.Size()
n += 1 + l + sovPush(uint64(l))
}
}
if m.Hash != 0 {
n += 1 + sovPush(m.Hash)
}
return n
}
func (m *Entry) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
l = SizeOfStdTime(m.Timestamp)
n += 1 + l + sovPush(uint64(l))
l = len(m.Line)
if l > 0 {
n += 1 + l + sovPush(uint64(l))
}
return n
}
func (m *Stream) Equal(that interface{}) bool {
if that == nil {
return m == nil
}
that1, ok := that.(*Stream)
if !ok {
that2, ok := that.(Stream)
if ok {
that1 = &that2
} else {
return false
}
}
if that1 == nil {
return m == nil
} else if m == nil {
return false
}
if m.Labels != that1.Labels {
return false
}
if len(m.Entries) != len(that1.Entries) {
return false
}
for i := range m.Entries {
if !m.Entries[i].Equal(that1.Entries[i]) {
return false
}
}
return m.Hash == that1.Hash
}
func (m *Entry) Equal(that interface{}) bool {
if that == nil {
return m == nil
}
that1, ok := that.(*Entry)
if !ok {
that2, ok := that.(Entry)
if ok {
that1 = &that2
} else {
return false
}
}
if that1 == nil {
return m == nil
} else if m == nil {
return false
}
if !m.Timestamp.Equal(that1.Timestamp) {
return false
}
if m.Line != that1.Line {
return false
}
return true
}

@ -19,14 +19,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc-gen-go v1.28.1
// protoc v3.14.0
// source: grpc/lb/v1/load_balancer.proto
package grpc_lb_v1
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
durationpb "google.golang.org/protobuf/types/known/durationpb"
@ -42,10 +41,6 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type LoadBalanceRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache

@ -32,7 +32,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/balancer"
grpclbstate "google.golang.org/grpc/balancer/grpclb/state"
"google.golang.org/grpc/balancer/grpclb/grpclbstate"
"google.golang.org/grpc/connectivity"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/grpclog"

@ -16,9 +16,9 @@
*
*/
// Package state declares grpclb types to be set by resolvers wishing to pass
// information to grpclb via resolver.State Attributes.
package state
// Package grpclbstate declares grpclb types to be set by resolvers wishing to
// pass information to grpclb via resolver.State Attributes.
package grpclbstate
import (
"google.golang.org/grpc/resolver"
@ -27,7 +27,7 @@ import (
// keyType is the key to use for storing State in Attributes.
type keyType string
const key = keyType("grpc.grpclb.state")
const key = keyType("grpc.grpclb.grpclbstate")
// State contains gRPCLB-relevant data passed from the name resolver.
type State struct {

@ -18,14 +18,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc-gen-go v1.28.1
// protoc v3.14.0
// source: grpc/binlog/v1/binarylog.proto
package grpc_binarylog_v1
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
durationpb "google.golang.org/protobuf/types/known/durationpb"
@ -41,10 +40,6 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
// Enumerates the type of event
// Note the terminology is different from the RPC semantics
// definition, but the same meaning is expressed here.

@ -788,10 +788,16 @@ func (cc *ClientConn) incrCallsFailed() {
func (ac *addrConn) connect() error {
ac.mu.Lock()
if ac.state == connectivity.Shutdown {
if logger.V(2) {
logger.Infof("connect called on shutdown addrConn; ignoring.")
}
ac.mu.Unlock()
return errConnClosing
}
if ac.state != connectivity.Idle {
if logger.V(2) {
logger.Infof("connect called on addrConn in non-idle state (%v); ignoring.", ac.state)
}
ac.mu.Unlock()
return nil
}
@ -1268,6 +1274,9 @@ func (ac *addrConn) createTransport(addr resolver.Address, copts transport.Conne
newTr, err := transport.NewClientTransport(connectCtx, ac.cc.ctx, addr, copts, onGoAway, onClose)
if err != nil {
if logger.V(2) {
logger.Infof("Creating new client transport to %q: %v", addr, err)
}
// newTr is either nil, or closed.
hcancel()
channelz.Warningf(logger, ac.channelzID, "grpc: addrConn.createTransport failed to connect to %s. Err: %v", addr, err)

@ -17,14 +17,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc-gen-go v1.28.1
// protoc v3.14.0
// source: grpc/gcp/altscontext.proto
package grpc_gcp
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
@ -38,10 +37,6 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type AltsContext struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache

@ -17,14 +17,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc-gen-go v1.28.1
// protoc v3.14.0
// source: grpc/gcp/handshaker.proto
package grpc_gcp
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
@ -38,10 +37,6 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type HandshakeProtocol int32
const (

@ -17,14 +17,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc-gen-go v1.28.1
// protoc v3.14.0
// source: grpc/gcp/transport_security_common.proto
package grpc_gcp
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
@ -38,10 +37,6 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
// The security level of the created channel. The list is sorted in increasing
// level of security. This order must always be maintained.
type SecurityLevel int32

@ -116,8 +116,9 @@ func newFuncDialOption(f func(*dialOptions)) *funcDialOption {
// be twice the size to keep syscalls low. The default value for this buffer is
// 32KB.
//
// Zero will disable the write buffer such that each write will be on underlying
// connection. Note: A Send call may not directly translate to a write.
// Zero or negative values will disable the write buffer such that each write
// will be on underlying connection. Note: A Send call may not directly
// translate to a write.
func WithWriteBufferSize(s int) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.WriteBufferSize = s
@ -127,8 +128,9 @@ func WithWriteBufferSize(s int) DialOption {
// WithReadBufferSize lets you set the size of read buffer, this determines how
// much data can be read at most for each read syscall.
//
// The default value for this buffer is 32KB. Zero will disable read buffer for
// a connection so data framer can access the underlying conn directly.
// The default value for this buffer is 32KB. Zero or negative values will
// disable read buffer for a connection so data framer can access the
// underlying conn directly.
func WithReadBufferSize(s int) DialOption {
return newFuncDialOption(func(o *dialOptions) {
o.copts.ReadBufferSize = s

@ -17,14 +17,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc-gen-go v1.28.1
// protoc v3.14.0
// source: grpc/health/v1/health.proto
package grpc_health_v1
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
@ -38,10 +37,6 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type HealthCheckResponse_ServingStatus int32
const (

@ -121,7 +121,7 @@ func (ml *TruncatingMethodLogger) truncateMetadata(mdPb *pb.Metadata) (truncated
// but not counted towards the size limit.
continue
}
currentEntryLen := uint64(len(entry.Value))
currentEntryLen := uint64(len(entry.GetKey())) + uint64(len(entry.GetValue()))
if currentEntryLen > bytesLimit {
break
}

@ -14,14 +14,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc-gen-go v1.28.1
// protoc v3.14.0
// source: grpc/lookup/v1/rls.proto
package grpc_lookup_v1
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
@ -35,10 +34,6 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
// Possible reasons for making a request.
type RouteLookupRequest_Reason int32

@ -14,14 +14,13 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc-gen-go v1.28.1
// protoc v3.14.0
// source: grpc/lookup/v1/rls_config.proto
package grpc_lookup_v1
import (
proto "github.com/golang/protobuf/proto"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
durationpb "google.golang.org/protobuf/types/known/durationpb"
@ -36,10 +35,6 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
// Extract a key based on a given name (e.g. header name or query parameter
// name). The name must match one of the names listed in the "name" field. If
// the "required_match" field is true, one of the specified names must be

@ -32,7 +32,7 @@ import (
"sync"
"time"
grpclbstate "google.golang.org/grpc/balancer/grpclb/state"
grpclbstate "google.golang.org/grpc/balancer/grpclb/grpclbstate"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal/backoff"
"google.golang.org/grpc/internal/envconfig"
@ -140,10 +140,10 @@ func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts
disableServiceConfig: opts.DisableServiceConfig,
}
if target.Authority == "" {
if target.URL.Host == "" {
d.resolver = defaultResolver
} else {
d.resolver, err = customAuthorityResolver(target.Authority)
d.resolver, err = customAuthorityResolver(target.URL.Host)
if err != nil {
return nil, err
}

@ -20,13 +20,20 @@
// name without scheme back to gRPC as resolved address.
package passthrough
import "google.golang.org/grpc/resolver"
import (
"errors"
"google.golang.org/grpc/resolver"
)
const scheme = "passthrough"
type passthroughBuilder struct{}
func (*passthroughBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
if target.Endpoint == "" && opts.Dialer == nil {
return nil, errors.New("passthrough: received empty target in Build()")
}
r := &passthroughResolver{
target: target,
cc: cc,

@ -34,8 +34,8 @@ type builder struct {
}
func (b *builder) Build(target resolver.Target, cc resolver.ClientConn, _ resolver.BuildOptions) (resolver.Resolver, error) {
if target.Authority != "" {
return nil, fmt.Errorf("invalid (non-empty) authority: %v", target.Authority)
if target.URL.Host != "" {
return nil, fmt.Errorf("invalid (non-empty) authority: %v", target.URL.Host)
}
// gRPC was parsing the dial target manually before PR #4817, and we

@ -191,7 +191,7 @@ type goAway struct {
code http2.ErrCode
debugData []byte
headsUp bool
closeConn bool
closeConn error // if set, loopyWriter will exit, resulting in conn closure
}
func (*goAway) isTransportResponseFrame() bool { return false }
@ -209,6 +209,14 @@ type outFlowControlSizeRequest struct {
func (*outFlowControlSizeRequest) isTransportResponseFrame() bool { return false }
// closeConnection is an instruction to tell the loopy writer to flush the
// framer and exit, which will cause the transport's connection to be closed
// (by the client or server). The transport itself will close after the reader
// encounters the EOF caused by the connection closure.
type closeConnection struct{}
func (closeConnection) isTransportResponseFrame() bool { return false }
type outStreamState int
const (
@ -408,7 +416,7 @@ func (c *controlBuffer) get(block bool) (interface{}, error) {
select {
case <-c.ch:
case <-c.done:
return nil, ErrConnClosing
return nil, errors.New("transport closed by client")
}
}
}
@ -519,18 +527,6 @@ const minBatchSize = 1000
// As an optimization, to increase the batch size for each flush, loopy yields the processor, once
// if the batch size is too low to give stream goroutines a chance to fill it up.
func (l *loopyWriter) run() (err error) {
defer func() {
if err == ErrConnClosing {
// Don't log ErrConnClosing as error since it happens
// 1. When the connection is closed by some other known issue.
// 2. User closed the connection.
// 3. A graceful close of connection.
if logger.V(logLevel) {
logger.Infof("transport: loopyWriter.run returning. %v", err)
}
err = nil
}
}()
for {
it, err := l.cbuf.get(true)
if err != nil {
@ -574,7 +570,6 @@ func (l *loopyWriter) run() (err error) {
}
l.framer.writer.Flush()
break hasdata
}
}
}
@ -662,11 +657,10 @@ func (l *loopyWriter) headerHandler(h *headerFrame) error {
func (l *loopyWriter) originateStream(str *outStream) error {
hdr := str.itl.dequeue().(*headerFrame)
if err := hdr.initStream(str.id); err != nil {
if err == ErrConnClosing {
return err
if err == errStreamDrain { // errStreamDrain need not close transport
return nil
}
// Other errors(errStreamDrain) need not close transport.
return nil
return err
}
if err := l.writeHeader(str.id, hdr.endStream, hdr.hf, hdr.onWrite); err != nil {
return err
@ -764,7 +758,7 @@ func (l *loopyWriter) cleanupStreamHandler(c *cleanupStream) error {
}
}
if l.side == clientSide && l.draining && len(l.estdStreams) == 0 {
return ErrConnClosing
return errors.New("finished processing active streams while in draining mode")
}
return nil
}
@ -799,7 +793,7 @@ func (l *loopyWriter) incomingGoAwayHandler(*incomingGoAway) error {
if l.side == clientSide {
l.draining = true
if len(l.estdStreams) == 0 {
return ErrConnClosing
return errors.New("received GOAWAY with no active streams")
}
}
return nil
@ -817,6 +811,14 @@ func (l *loopyWriter) goAwayHandler(g *goAway) error {
return nil
}
func (l *loopyWriter) closeConnectionHandler() error {
l.framer.writer.Flush()
// Exit loopyWriter entirely by returning an error here. This will lead to
// the transport closing the connection, and, ultimately, transport
// closure.
return ErrConnClosing
}
func (l *loopyWriter) handle(i interface{}) error {
switch i := i.(type) {
case *incomingWindowUpdate:
@ -845,6 +847,8 @@ func (l *loopyWriter) handle(i interface{}) error {
return l.goAwayHandler(i)
case *outFlowControlSizeRequest:
return l.outFlowControlSizeRequestHandler(i)
case closeConnection:
return l.closeConnectionHandler()
default:
return fmt.Errorf("transport: unknown control message type %T", i)
}

@ -46,24 +46,32 @@ import (
"google.golang.org/grpc/status"
)
// NewServerHandlerTransport returns a ServerTransport handling gRPC
// from inside an http.Handler. It requires that the http Server
// supports HTTP/2.
// NewServerHandlerTransport returns a ServerTransport handling gRPC from
// inside an http.Handler, or writes an HTTP error to w and returns an error.
// It requires that the http Server supports HTTP/2.
func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats []stats.Handler) (ServerTransport, error) {
if r.ProtoMajor != 2 {
return nil, errors.New("gRPC requires HTTP/2")
msg := "gRPC requires HTTP/2"
http.Error(w, msg, http.StatusBadRequest)
return nil, errors.New(msg)
}
if r.Method != "POST" {
return nil, errors.New("invalid gRPC request method")
msg := fmt.Sprintf("invalid gRPC request method %q", r.Method)
http.Error(w, msg, http.StatusBadRequest)
return nil, errors.New(msg)
}
contentType := r.Header.Get("Content-Type")
// TODO: do we assume contentType is lowercase? we did before
contentSubtype, validContentType := grpcutil.ContentSubtype(contentType)
if !validContentType {
return nil, errors.New("invalid gRPC request content-type")
msg := fmt.Sprintf("invalid gRPC request content-type %q", contentType)
http.Error(w, msg, http.StatusBadRequest)
return nil, errors.New(msg)
}
if _, ok := w.(http.Flusher); !ok {
return nil, errors.New("gRPC requires a ResponseWriter supporting http.Flusher")
msg := "gRPC requires a ResponseWriter supporting http.Flusher"
http.Error(w, msg, http.StatusInternalServerError)
return nil, errors.New(msg)
}
st := &serverHandlerTransport{
@ -79,7 +87,9 @@ func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats []s
if v := r.Header.Get("grpc-timeout"); v != "" {
to, err := decodeTimeout(v)
if err != nil {
return nil, status.Errorf(codes.Internal, "malformed time-out: %v", err)
msg := fmt.Sprintf("malformed time-out: %v", err)
http.Error(w, msg, http.StatusBadRequest)
return nil, status.Error(codes.Internal, msg)
}
st.timeoutSet = true
st.timeout = to
@ -97,7 +107,9 @@ func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats []s
for _, v := range vv {
v, err := decodeMetadataHeader(k, v)
if err != nil {
return nil, status.Errorf(codes.Internal, "malformed binary metadata: %v", err)
msg := fmt.Sprintf("malformed binary metadata %q in header %q: %v", v, k, err)
http.Error(w, msg, http.StatusBadRequest)
return nil, status.Error(codes.Internal, msg)
}
metakv = append(metakv, k, v)
}
@ -141,12 +153,15 @@ type serverHandlerTransport struct {
stats []stats.Handler
}
func (ht *serverHandlerTransport) Close() {
ht.closeOnce.Do(ht.closeCloseChanOnce)
func (ht *serverHandlerTransport) Close(err error) {
ht.closeOnce.Do(func() {
if logger.V(logLevel) {
logger.Infof("Closing serverHandlerTransport: %v", err)
}
close(ht.closedCh)
})
}
func (ht *serverHandlerTransport) closeCloseChanOnce() { close(ht.closedCh) }
func (ht *serverHandlerTransport) RemoteAddr() net.Addr { return strAddr(ht.req.RemoteAddr) }
// strAddr is a net.Addr backed by either a TCP "ip:port" string, or
@ -236,7 +251,7 @@ func (ht *serverHandlerTransport) WriteStatus(s *Stream, st *status.Status) erro
})
}
}
ht.Close()
ht.Close(errors.New("finished writing status"))
return err
}
@ -346,7 +361,7 @@ func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream), trace
case <-ht.req.Context().Done():
}
cancel()
ht.Close()
ht.Close(errors.New("request is done processing"))
}()
req := ht.req

@ -59,11 +59,15 @@ var clientConnectionCounter uint64
// http2Client implements the ClientTransport interface with HTTP2.
type http2Client struct {
lastRead int64 // Keep this field 64-bit aligned. Accessed atomically.
ctx context.Context
cancel context.CancelFunc
ctxDone <-chan struct{} // Cache the ctx.Done() chan.
userAgent string
lastRead int64 // Keep this field 64-bit aligned. Accessed atomically.
ctx context.Context
cancel context.CancelFunc
ctxDone <-chan struct{} // Cache the ctx.Done() chan.
userAgent string
// address contains the resolver returned address for this transport.
// If the `ServerName` field is set, it takes precedence over `CallHdr.Host`
// passed to `NewStream`, when determining the :authority header.
address resolver.Address
md metadata.MD
conn net.Conn // underlying communication channel
loopy *loopyWriter
@ -238,8 +242,11 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
go func(conn net.Conn) {
defer ctxMonitorDone.Fire() // Signal this goroutine has exited.
<-newClientCtx.Done() // Block until connectCtx expires or the defer above executes.
if connectCtx.Err() != nil {
if err := connectCtx.Err(); err != nil {
// connectCtx expired before exiting the function. Hard close the connection.
if logger.V(logLevel) {
logger.Infof("newClientTransport: aborting due to connectCtx: %v", err)
}
conn.Close()
}
}(conn)
@ -314,6 +321,7 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
cancel: cancel,
userAgent: opts.UserAgent,
registeredCompressors: grpcutil.RegisteredCompressors(),
address: addr,
conn: conn,
remoteAddr: conn.RemoteAddr(),
localAddr: conn.LocalAddr(),
@ -440,10 +448,8 @@ func newHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts
go func() {
t.loopy = newLoopyWriter(clientSide, t.framer, t.controlBuf, t.bdpEst)
err := t.loopy.run()
if err != nil {
if logger.V(logLevel) {
logger.Errorf("transport: loopyWriter.run returning. Err: %v", err)
}
if logger.V(logLevel) {
logger.Infof("transport: loopyWriter exited. Closing connection. Err: %v", err)
}
// Do not close the transport. Let reader goroutine handle it since
// there might be data in the buffers.
@ -702,6 +708,18 @@ func (e NewStreamError) Error() string {
// streams. All non-nil errors returned will be *NewStreamError.
func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, error) {
ctx = peer.NewContext(ctx, t.getPeer())
// ServerName field of the resolver returned address takes precedence over
// Host field of CallHdr to determine the :authority header. This is because,
// the ServerName field takes precedence for server authentication during
// TLS handshake, and the :authority header should match the value used
// for server authentication.
if t.address.ServerName != "" {
newCallHdr := *callHdr
newCallHdr.Host = t.address.ServerName
callHdr = &newCallHdr
}
headerFields, err := t.createHeaderFields(ctx, callHdr)
if err != nil {
return nil, &NewStreamError{Err: err, AllowTransparentRetry: false}
@ -934,6 +952,9 @@ func (t *http2Client) Close(err error) {
t.mu.Unlock()
return
}
if logger.V(logLevel) {
logger.Infof("transport: closing: %v", err)
}
// Call t.onClose ASAP to prevent the client from attempting to create new
// streams.
t.onClose()
@ -986,11 +1007,14 @@ func (t *http2Client) GracefulClose() {
t.mu.Unlock()
return
}
if logger.V(logLevel) {
logger.Infof("transport: GracefulClose called")
}
t.state = draining
active := len(t.activeStreams)
t.mu.Unlock()
if active == 0 {
t.Close(ErrConnClosing)
t.Close(connectionErrorf(true, nil, "no active streams left to process while draining"))
return
}
t.controlBuf.put(&incomingGoAway{})

@ -21,6 +21,7 @@ package transport
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"math"
@ -41,6 +42,7 @@ import (
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/internal/channelz"
"google.golang.org/grpc/internal/grpcrand"
"google.golang.org/grpc/internal/grpcsync"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
@ -101,13 +103,13 @@ type http2Server struct {
mu sync.Mutex // guard the following
// drainChan is initialized when Drain() is called the first time.
// After which the server writes out the first GoAway(with ID 2^31-1) frame.
// Then an independent goroutine will be launched to later send the second GoAway.
// During this time we don't want to write another first GoAway(with ID 2^31 -1) frame.
// Thus call to Drain() will be a no-op if drainChan is already initialized since draining is
// already underway.
drainChan chan struct{}
// drainEvent is initialized when Drain() is called the first time. After
// which the server writes out the first GoAway(with ID 2^31-1) frame. Then
// an independent goroutine will be launched to later send the second
// GoAway. During this time we don't want to write another first GoAway(with
// ID 2^31 -1) frame. Thus call to Drain() will be a no-op if drainEvent is
// already initialized since draining is already underway.
drainEvent *grpcsync.Event
state transportState
activeStreams map[uint32]*Stream
// idle is the time instant when the connection went idle.
@ -293,7 +295,7 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
defer func() {
if err != nil {
t.Close()
t.Close(err)
}
}()
@ -331,10 +333,9 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
go func() {
t.loopy = newLoopyWriter(serverSide, t.framer, t.controlBuf, t.bdpEst)
t.loopy.ssGoAwayHandler = t.outgoingGoAwayHandler
if err := t.loopy.run(); err != nil {
if logger.V(logLevel) {
logger.Errorf("transport: loopyWriter.run returning. Err: %v", err)
}
err := t.loopy.run()
if logger.V(logLevel) {
logger.Infof("transport: loopyWriter exited. Closing connection. Err: %v", err)
}
t.conn.Close()
t.controlBuf.finish()
@ -344,8 +345,9 @@ func NewServerTransport(conn net.Conn, config *ServerConfig) (_ ServerTransport,
return t, nil
}
// operateHeader takes action on the decoded headers.
func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream), traceCtx func(context.Context, string) context.Context) (fatal bool) {
// operateHeaders takes action on the decoded headers. Returns an error if fatal
// error encountered and transport needs to close, otherwise returns nil.
func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream), traceCtx func(context.Context, string) context.Context) error {
// Acquire max stream ID lock for entire duration
t.maxStreamMu.Lock()
defer t.maxStreamMu.Unlock()
@ -361,15 +363,12 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
rstCode: http2.ErrCodeFrameSize,
onWrite: func() {},
})
return false
return nil
}
if streamID%2 != 1 || streamID <= t.maxStreamID {
// illegal gRPC stream id.
if logger.V(logLevel) {
logger.Errorf("transport: http2Server.HandleStreams received an illegal stream id: %v", streamID)
}
return true
return fmt.Errorf("received an illegal stream id: %v. headers frame: %+v", streamID, frame)
}
t.maxStreamID = streamID
@ -453,7 +452,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
status: status.New(codes.Internal, errMsg),
rst: !frame.StreamEnded(),
})
return false
return nil
}
if !isGRPC || headerError {
@ -463,7 +462,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
rstCode: http2.ErrCodeProtocol,
onWrite: func() {},
})
return false
return nil
}
// "If :authority is missing, Host must be renamed to :authority." - A41
@ -503,7 +502,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
if t.state != reachable {
t.mu.Unlock()
s.cancel()
return false
return nil
}
if uint32(len(t.activeStreams)) >= t.maxStreams {
t.mu.Unlock()
@ -514,7 +513,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
onWrite: func() {},
})
s.cancel()
return false
return nil
}
if httpMethod != http.MethodPost {
t.mu.Unlock()
@ -530,7 +529,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
rst: !frame.StreamEnded(),
})
s.cancel()
return false
return nil
}
if t.inTapHandle != nil {
var err error
@ -550,7 +549,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
status: stat,
rst: !frame.StreamEnded(),
})
return false
return nil
}
}
t.activeStreams[streamID] = s
@ -597,7 +596,7 @@ func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(
wq: s.wq,
})
handle(s)
return false
return nil
}
// HandleStreams receives incoming streams using the given handler. This is
@ -630,19 +629,16 @@ func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.
continue
}
if err == io.EOF || err == io.ErrUnexpectedEOF {
t.Close()
t.Close(err)
return
}
if logger.V(logLevel) {
logger.Warningf("transport: http2Server.HandleStreams failed to read frame: %v", err)
}
t.Close()
t.Close(err)
return
}
switch frame := frame.(type) {
case *http2.MetaHeadersFrame:
if t.operateHeaders(frame, handle, traceCtx) {
t.Close()
if err := t.operateHeaders(frame, handle, traceCtx); err != nil {
t.Close(err)
break
}
case *http2.DataFrame:
@ -843,8 +839,8 @@ const (
func (t *http2Server) handlePing(f *http2.PingFrame) {
if f.IsAck() {
if f.Data == goAwayPing.data && t.drainChan != nil {
close(t.drainChan)
if f.Data == goAwayPing.data && t.drainEvent != nil {
t.drainEvent.Fire()
return
}
// Maybe it's a BDP ping.
@ -886,10 +882,7 @@ func (t *http2Server) handlePing(f *http2.PingFrame) {
if t.pingStrikes > maxPingStrikes {
// Send goaway and close the connection.
if logger.V(logLevel) {
logger.Errorf("transport: Got too many pings from the client, closing the connection.")
}
t.controlBuf.put(&goAway{code: http2.ErrCodeEnhanceYourCalm, debugData: []byte("too_many_pings"), closeConn: true})
t.controlBuf.put(&goAway{code: http2.ErrCodeEnhanceYourCalm, debugData: []byte("too_many_pings"), closeConn: errors.New("got too many pings from the client")})
}
}
@ -1153,7 +1146,7 @@ func (t *http2Server) keepalive() {
if logger.V(logLevel) {
logger.Infof("transport: closing server transport due to maximum connection age.")
}
t.Close()
t.controlBuf.put(closeConnection{})
case <-t.done:
}
return
@ -1169,10 +1162,7 @@ func (t *http2Server) keepalive() {
continue
}
if outstandingPing && kpTimeoutLeft <= 0 {
if logger.V(logLevel) {
logger.Infof("transport: closing server transport due to idleness.")
}
t.Close()
t.Close(fmt.Errorf("keepalive ping not acked within timeout %s", t.kp.Time))
return
}
if !outstandingPing {
@ -1199,12 +1189,15 @@ func (t *http2Server) keepalive() {
// Close starts shutting down the http2Server transport.
// TODO(zhaoq): Now the destruction is not blocked on any pending streams. This
// could cause some resource issue. Revisit this later.
func (t *http2Server) Close() {
func (t *http2Server) Close(err error) {
t.mu.Lock()
if t.state == closing {
t.mu.Unlock()
return
}
if logger.V(logLevel) {
logger.Infof("transport: closing: %v", err)
}
t.state = closing
streams := t.activeStreams
t.activeStreams = nil
@ -1295,10 +1288,10 @@ func (t *http2Server) RemoteAddr() net.Addr {
func (t *http2Server) Drain() {
t.mu.Lock()
defer t.mu.Unlock()
if t.drainChan != nil {
if t.drainEvent != nil {
return
}
t.drainChan = make(chan struct{})
t.drainEvent = grpcsync.NewEvent()
t.controlBuf.put(&goAway{code: http2.ErrCodeNo, debugData: []byte{}, headsUp: true})
}
@ -1319,19 +1312,20 @@ func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) {
// Stop accepting more streams now.
t.state = draining
sid := t.maxStreamID
retErr := g.closeConn
if len(t.activeStreams) == 0 {
g.closeConn = true
retErr = errors.New("second GOAWAY written and no active streams left to process")
}
t.mu.Unlock()
t.maxStreamMu.Unlock()
if err := t.framer.fr.WriteGoAway(sid, g.code, g.debugData); err != nil {
return false, err
}
if g.closeConn {
if retErr != nil {
// Abruptly close the connection following the GoAway (via
// loopywriter). But flush out what's inside the buffer first.
t.framer.writer.Flush()
return false, fmt.Errorf("transport: Connection closing")
return false, retErr
}
return true, nil
}
@ -1353,7 +1347,7 @@ func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) {
timer := time.NewTimer(time.Minute)
defer timer.Stop()
select {
case <-t.drainChan:
case <-t.drainEvent.Done():
case <-timer.C:
case <-t.done:
return

@ -701,7 +701,7 @@ type ServerTransport interface {
// Close tears down the transport. Once it is called, the transport
// should not be accessed any more. All the pending streams and their
// handlers will be terminated asynchronously.
Close()
Close(err error)
// RemoteAddr returns the remote network address.
RemoteAddr() net.Addr

@ -102,8 +102,8 @@ func (b *pickfirstBalancer) UpdateClientConnState(state balancer.ClientConnState
b.subConn = subConn
b.state = connectivity.Idle
b.cc.UpdateState(balancer.State{
ConnectivityState: connectivity.Idle,
Picker: &picker{result: balancer.PickResult{SubConn: b.subConn}},
ConnectivityState: connectivity.Connecting,
Picker: &picker{err: balancer.ErrNoSubConnAvailable},
})
b.subConn.Connect()
return nil

@ -57,7 +57,8 @@ LEGACY_SOURCES=(
${WORKDIR}/grpc-proto/grpc/health/v1/health.proto
${WORKDIR}/grpc-proto/grpc/lb/v1/load_balancer.proto
profiling/proto/service.proto
reflection/grpc_reflection_v1alpha/reflection.proto
${WORKDIR}/grpc-proto/grpc/reflection/v1alpha/reflection.proto
${WORKDIR}/grpc-proto/grpc/reflection/v1/reflection.proto
)
# Generates only the new gRPC Service symbols
@ -119,8 +120,4 @@ mv ${WORKDIR}/out/google.golang.org/grpc/lookup/grpc_lookup_v1/* ${WORKDIR}/out/
# see grpc_testing_not_regenerate/README.md for details.
rm ${WORKDIR}/out/google.golang.org/grpc/reflection/grpc_testing_not_regenerate/*.pb.go
# grpc/testing does not have a go_package option.
mv ${WORKDIR}/out/grpc/testing/*.pb.go interop/grpc_testing/
mv ${WORKDIR}/out/grpc/core/*.pb.go interop/grpc_testing/core/
cp -R ${WORKDIR}/out/google.golang.org/grpc/* .

@ -233,10 +233,11 @@ func newJoinServerOption(opts ...ServerOption) ServerOption {
return &joinServerOption{opts: opts}
}
// WriteBufferSize determines how much data can be batched before doing a write on the wire.
// The corresponding memory allocation for this buffer will be twice the size to keep syscalls low.
// The default value for this buffer is 32KB.
// Zero will disable the write buffer such that each write will be on underlying connection.
// WriteBufferSize determines how much data can be batched before doing a write
// on the wire. The corresponding memory allocation for this buffer will be
// twice the size to keep syscalls low. The default value for this buffer is
// 32KB. Zero or negative values will disable the write buffer such that each
// write will be on underlying connection.
// Note: A Send call may not directly translate to a write.
func WriteBufferSize(s int) ServerOption {
return newFuncServerOption(func(o *serverOptions) {
@ -244,11 +245,10 @@ func WriteBufferSize(s int) ServerOption {
})
}
// ReadBufferSize lets you set the size of read buffer, this determines how much data can be read at most
// for one read syscall.
// The default value for this buffer is 32KB.
// Zero will disable read buffer for a connection so data framer can access the underlying
// conn directly.
// ReadBufferSize lets you set the size of read buffer, this determines how much
// data can be read at most for one read syscall. The default value for this
// buffer is 32KB. Zero or negative values will disable read buffer for a
// connection so data framer can access the underlying conn directly.
func ReadBufferSize(s int) ServerOption {
return newFuncServerOption(func(o *serverOptions) {
o.readBufferSize = s
@ -942,7 +942,7 @@ func (s *Server) newHTTP2Transport(c net.Conn) transport.ServerTransport {
}
func (s *Server) serveStreams(st transport.ServerTransport) {
defer st.Close()
defer st.Close(errors.New("finished serving streams for the server transport"))
var wg sync.WaitGroup
var roundRobinCounter uint32
@ -1008,7 +1008,8 @@ var _ http.Handler = (*Server)(nil)
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
st, err := transport.NewServerHandlerTransport(w, r, s.opts.statsHandlers)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
// Errors returned from transport.NewServerHandlerTransport have
// already been written to w.
return
}
if !s.addConn(listenerAddressForServeHTTP, st) {
@ -1046,7 +1047,7 @@ func (s *Server) addConn(addr string, st transport.ServerTransport) bool {
s.mu.Lock()
defer s.mu.Unlock()
if s.conns == nil {
st.Close()
st.Close(errors.New("Server.addConn called when server has already been stopped"))
return false
}
if s.drain {
@ -1150,21 +1151,16 @@ func chainUnaryServerInterceptors(s *Server) {
func chainUnaryInterceptors(interceptors []UnaryServerInterceptor) UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (interface{}, error) {
// the struct ensures the variables are allocated together, rather than separately, since we
// know they should be garbage collected together. This saves 1 allocation and decreases
// time/call by about 10% on the microbenchmark.
var state struct {
i int
next UnaryHandler
}
state.next = func(ctx context.Context, req interface{}) (interface{}, error) {
if state.i == len(interceptors)-1 {
return interceptors[state.i](ctx, req, info, handler)
}
state.i++
return interceptors[state.i-1](ctx, req, info, state.next)
}
return state.next(ctx, req)
return interceptors[0](ctx, req, info, getChainUnaryHandler(interceptors, 0, info, handler))
}
}
func getChainUnaryHandler(interceptors []UnaryServerInterceptor, curr int, info *UnaryServerInfo, finalHandler UnaryHandler) UnaryHandler {
if curr == len(interceptors)-1 {
return finalHandler
}
return func(ctx context.Context, req interface{}) (interface{}, error) {
return interceptors[curr+1](ctx, req, info, getChainUnaryHandler(interceptors, curr+1, info, finalHandler))
}
}
@ -1470,21 +1466,16 @@ func chainStreamServerInterceptors(s *Server) {
func chainStreamInterceptors(interceptors []StreamServerInterceptor) StreamServerInterceptor {
return func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error {
// the struct ensures the variables are allocated together, rather than separately, since we
// know they should be garbage collected together. This saves 1 allocation and decreases
// time/call by about 10% on the microbenchmark.
var state struct {
i int
next StreamHandler
}
state.next = func(srv interface{}, ss ServerStream) error {
if state.i == len(interceptors)-1 {
return interceptors[state.i](srv, ss, info, handler)
}
state.i++
return interceptors[state.i-1](srv, ss, info, state.next)
}
return state.next(srv, ss)
return interceptors[0](srv, ss, info, getChainStreamHandler(interceptors, 0, info, handler))
}
}
func getChainStreamHandler(interceptors []StreamServerInterceptor, curr int, info *StreamServerInfo, finalHandler StreamHandler) StreamHandler {
if curr == len(interceptors)-1 {
return finalHandler
}
return func(srv interface{}, stream ServerStream) error {
return interceptors[curr+1](srv, stream, info, getChainStreamHandler(interceptors, curr+1, info, finalHandler))
}
}
@ -1819,7 +1810,7 @@ func (s *Server) Stop() {
}
for _, cs := range conns {
for st := range cs {
st.Close()
st.Close(errors.New("Server.Stop called"))
}
}
if s.opts.numServerWorkers > 0 {

@ -416,7 +416,7 @@ func (cs *clientStream) newAttemptLocked(isTransparent bool) (*csAttempt, error)
ctx = trace.NewContext(ctx, trInfo.tr)
}
if cs.cc.parsedTarget.Scheme == "xds" {
if cs.cc.parsedTarget.URL.Scheme == "xds" {
// Add extra metadata (metadata that will be added by transport) to context
// so the balancer can see them.
ctx = grpcutil.WithExtraMetadata(ctx, metadata.Pairs(

@ -19,4 +19,4 @@
package grpc
// Version is the current grpc version.
const Version = "1.51.0"
const Version = "1.52.0"

@ -121,8 +121,9 @@ done
#
# TODO(dfawley): don't use deprecated functions in examples or first-party
# plugins.
# TODO(dfawley): enable ST1019 (duplicate imports) but allow for protobufs.
SC_OUT="$(mktemp)"
staticcheck -go 1.9 -checks 'inherit,-ST1015' ./... > "${SC_OUT}" || true
staticcheck -go 1.19 -checks 'inherit,-ST1015,-ST1019,-SA1019' ./... > "${SC_OUT}" || true
# Error if anything other than deprecation warnings are printed.
not grep -v "is deprecated:.*SA1019" "${SC_OUT}"
# Only ignore the following deprecated types/fields/functions.

@ -35,7 +35,9 @@ type LBConfig struct {
const (
defaultMinSize = 1024
defaultMaxSize = 8 * 1024 * 1024 // 8M
defaultMaxSize = 4096
// TODO(apolcyn): make makeRingSizeCap configurable, with either a dial option or global setting
maxRingSizeCap = 4096
)
func parseConfig(c json.RawMessage) (*LBConfig, error) {
@ -49,6 +51,12 @@ func parseConfig(c json.RawMessage) (*LBConfig, error) {
if cfg.MaxRingSize == 0 {
cfg.MaxRingSize = defaultMaxSize
}
if cfg.MinRingSize > maxRingSizeCap {
cfg.MinRingSize = maxRingSizeCap
}
if cfg.MaxRingSize > maxRingSizeCap {
cfg.MaxRingSize = maxRingSizeCap
}
if cfg.MinRingSize > cfg.MaxRingSize {
return nil, fmt.Errorf("min %v is greater than max %v", cfg.MinRingSize, cfg.MaxRingSize)
}

@ -24,6 +24,7 @@ import (
"strconv"
xxhash "github.com/cespare/xxhash/v2"
"google.golang.org/grpc/internal/grpclog"
"google.golang.org/grpc/resolver"
)
@ -65,9 +66,12 @@ type ringEntry struct {
// and first item with hash >= given hash will be returned.
//
// Must be called with a non-empty subConns map.
func newRing(subConns *resolver.AddressMap, minRingSize, maxRingSize uint64) *ring {
func newRing(subConns *resolver.AddressMap, minRingSize, maxRingSize uint64, logger *grpclog.PrefixLogger) *ring {
logger.Debugf("newRing: number of subConns is %d, minRingSize is %d, maxRingSize is %d", subConns.Len(), minRingSize, maxRingSize)
// https://github.com/envoyproxy/envoy/blob/765c970f06a4c962961a0e03a467e165b276d50f/source/common/upstream/ring_hash_lb.cc#L114
normalizedWeights, minWeight := normalizeWeights(subConns)
logger.Debugf("newRing: normalized subConn weights is %v", normalizedWeights)
// Normalized weights for {3,3,4} is {0.3,0.3,0.4}.
@ -78,6 +82,7 @@ func newRing(subConns *resolver.AddressMap, minRingSize, maxRingSize uint64) *ri
scale := math.Min(math.Ceil(minWeight*float64(minRingSize))/minWeight, float64(maxRingSize))
ringSize := math.Ceil(scale)
items := make([]*ringEntry, 0, int(ringSize))
logger.Debugf("newRing: creating new ring of size %v", ringSize)
// For each entry, scale*weight nodes are generated in the ring.
//

@ -293,7 +293,7 @@ func (b *ringhashBalancer) UpdateClientConnState(s balancer.ClientConnState) err
if regenerateRing {
// Ring creation is guaranteed to not fail because we call newRing()
// with a non-empty subConns map.
b.ring = newRing(b.subConns, b.config.MinRingSize, b.config.MaxRingSize)
b.ring = newRing(b.subConns, b.config.MinRingSize, b.config.MaxRingSize, b.logger)
b.regeneratePicker()
b.cc.UpdateState(balancer.State{ConnectivityState: b.state, Picker: b.picker})
}

@ -36,6 +36,20 @@ type XDSClient interface {
WatchEndpoints(string, func(xdsresource.EndpointsUpdate, error)) func()
ReportLoad(*bootstrap.ServerConfig) (*load.Store, func())
// WatchResource uses xDS to discover the resource associated with the
// provided resource name. The resource type implementation determines how
// xDS requests are sent out and how responses are deserialized and
// validated. Upon receipt of a response from the management server, an
// appropriate callback on the watcher is invoked.
//
// Most callers will not have a need to use this API directly. They will
// instead use a resource-type-specific wrapper API provided by the relevant
// resource type implementation.
//
// TODO: Once this generic client API is fully implemented and integrated,
// delete the resource type specific watch APIs on this interface.
WatchResource(rType xdsresource.Type, resourceName string, watcher xdsresource.ResourceWatcher) (cancel func())
DumpLDS() map[string]xdsresource.UpdateWithMD
DumpRDS() map[string]xdsresource.UpdateWithMD
DumpCDS() map[string]xdsresource.UpdateWithMD

@ -20,6 +20,7 @@ package xdsclient
import (
"bytes"
"context"
"encoding/json"
"fmt"
"sync"
@ -55,10 +56,14 @@ func NewWithConfig(config *bootstrap.Config) (XDSClient, error) {
// newWithConfig returns a new xdsClient with the given config.
func newWithConfig(config *bootstrap.Config, watchExpiryTimeout time.Duration, idleAuthorityDeleteTimeout time.Duration) (*clientImpl, error) {
ctx, cancel := context.WithCancel(context.Background())
c := &clientImpl{
done: grpcsync.NewEvent(),
config: config,
watchExpiryTimeout: watchExpiryTimeout,
serializer: newCallbackSerializer(ctx),
serializerClose: cancel,
resourceTypes: newResourceTypeRegistry(),
authorities: make(map[string]*authority),
idleAuthorities: cache.NewTimeoutCache(idleAuthorityDeleteTimeout),
}

@ -32,14 +32,14 @@ var _ XDSClient = &clientImpl{}
// clientImpl is the real implementation of the xds client. The exported Client
// is a wrapper of this struct with a ref count.
//
// Implements UpdateHandler interface.
// TODO(easwars): Make a wrapper struct which implements this interface in the
// style of ccBalancerWrapper so that the Client type does not implement these
// exported methods.
type clientImpl struct {
done *grpcsync.Event
config *bootstrap.Config
done *grpcsync.Event
config *bootstrap.Config
logger *grpclog.PrefixLogger
watchExpiryTimeout time.Duration
serializer *callbackSerializer
serializerClose func()
resourceTypes *resourceTypeRegistry
// authorityMu protects the authority fields. It's necessary because an
// authority is created when it's used.
@ -60,9 +60,6 @@ type clientImpl struct {
// An authority is either in authorities, or idleAuthorities,
// never both.
idleAuthorities *cache.TimeoutCache
logger *grpclog.PrefixLogger
watchExpiryTimeout time.Duration
}
// BootstrapConfig returns the configuration read from the bootstrap file.
@ -72,6 +69,9 @@ func (c *clientImpl) BootstrapConfig() *bootstrap.Config {
}
// Close closes the gRPC connection to the management server.
//
// TODO: ensure that all underlying transports are closed before this function
// returns.
func (c *clientImpl) Close() {
if c.done.HasFired() {
return
@ -80,16 +80,13 @@ func (c *clientImpl) Close() {
// TODO: Should we invoke the registered callbacks here with an error that
// the client is closed?
// Note that Close needs to check for nils even if some of them are always
// set in the constructor. This is because the constructor defers Close() in
// error cases, and the fields might not be set when the error happens.
c.authorityMu.Lock()
for _, a := range c.authorities {
a.close()
}
c.idleAuthorities.Clear(true)
c.authorityMu.Unlock()
c.serializerClose()
c.logger.Infof("Shutdown")
}

@ -18,6 +18,10 @@
package xdsclient
import (
"context"
"fmt"
"sync"
"google.golang.org/grpc/xds/internal/xdsclient/xdsresource"
)
@ -103,3 +107,67 @@ func (c *clientImpl) WatchEndpoints(clusterName string, cb func(xdsresource.Endp
unref()
}
}
// WatchResource uses xDS to discover the resource associated with the provided
// resource name. The resource type implementation determines how xDS requests
// are sent out and how responses are deserialized and validated. Upon receipt
// of a response from the management server, an appropriate callback on the
// watcher is invoked.
func (c *clientImpl) WatchResource(rType xdsresource.Type, resourceName string, watcher xdsresource.ResourceWatcher) (cancel func()) {
// Return early if the client is already closed.
//
// The client returned from the top-level API is a ref-counted client which
// contains a pointer to `clientImpl`. When all references are released, the
// ref-counted client sets its pointer to `nil`. And if any watch APIs are
// made on such a closed client, we will get here with a `nil` receiver.
if c == nil || c.done.HasFired() {
logger.Warningf("Watch registered for name %q of type %q, but client is closed", rType.TypeEnum().String(), resourceName)
return func() {}
}
if err := c.resourceTypes.maybeRegister(rType); err != nil {
c.serializer.Schedule(func(context.Context) { watcher.OnError(err) })
return func() {}
}
// TODO: replace this with the code does the following when we have
// implemented generic watch API on the authority:
// - Parse the resource name and extract the authority.
// - Locate the corresponding authority object and acquire a reference to
// it. If the authority is not found, error out.
// - Call the watchResource() method on the authority.
// - Return a cancel function to cancel the watch on the authority and to
// release the reference.
return func() {}
}
// A registry of xdsresource.Type implementations indexed by their corresponding
// type URLs. Registration of an xdsresource.Type happens the first time a watch
// for a resource of that type is invoked.
type resourceTypeRegistry struct {
mu sync.Mutex
types map[string]xdsresource.Type
}
func newResourceTypeRegistry() *resourceTypeRegistry {
return &resourceTypeRegistry{types: make(map[string]xdsresource.Type)}
}
func (r *resourceTypeRegistry) maybeRegister(rType xdsresource.Type) error {
r.mu.Lock()
defer r.mu.Unlock()
urls := []string{rType.V2TypeURL(), rType.V3TypeURL()}
for _, u := range urls {
if u == "" {
// Silently ignore unsupported versions of the resource.
continue
}
typ, ok := r.types[u]
if ok && typ != rType {
return fmt.Errorf("attempt to re-register a resource type implementation for %v", rType.TypeEnum())
}
r.types[u] = rType
}
return nil
}

@ -57,6 +57,11 @@ type Controller struct {
cc *grpc.ClientConn // Connection to the management server.
vClient version.VersionedClient
stopRunGoroutine context.CancelFunc
// The run goroutine closes this channel when it exits, and we block on this
// channel in Close(). This ensures that when Close() returns, the
// underlying transport is closed, and we can guarantee that we will not
// process any subsequent responses from the management server.
runDoneCh chan struct{}
backoff func(int) time.Duration
streamCh chan grpc.ClientStream
@ -77,6 +82,7 @@ type Controller struct {
versionMap map[xdsresource.ResourceType]string
// nonceMap contains the nonce from the most recent received response.
nonceMap map[xdsresource.ResourceType]string
closed bool
// Changes to map lrsClients and the lrsClient inside the map need to be
// protected by lrsMu.
@ -127,6 +133,7 @@ func New(config *bootstrap.ServerConfig, updateHandler pubsub.UpdateHandler, val
config: config,
updateValidator: validator,
updateHandler: updateHandler,
runDoneCh: make(chan struct{}),
backoff: boff,
streamCh: make(chan grpc.ClientStream, 1),
@ -170,6 +177,14 @@ func New(config *bootstrap.ServerConfig, updateHandler pubsub.UpdateHandler, val
// Close closes the controller.
func (t *Controller) Close() {
t.mu.Lock()
if t.closed {
t.mu.Unlock()
return
}
t.closed = true
t.mu.Unlock()
// Note that Close needs to check for nils even if some of them are always
// set in the constructor. This is because the constructor defers Close() in
// error cases, and the fields might not be set when the error happens.
@ -179,4 +194,8 @@ func (t *Controller) Close() {
if t.cc != nil {
t.cc.Close()
}
// Wait on the run goroutine to be done only if it was started.
if t.stopRunGoroutine != nil {
<-t.runDoneCh
}
}

@ -54,7 +54,13 @@ func (t *Controller) RemoveWatch(rType xdsresource.ResourceType, resourceName st
// stream failed without receiving a single reply) and runs the sender and
// receiver routines to send and receive data from the stream respectively.
func (t *Controller) run(ctx context.Context) {
go t.send(ctx)
sendDoneCh := make(chan struct{})
defer func() {
<-sendDoneCh
close(t.runDoneCh)
}()
go t.send(ctx, sendDoneCh)
// TODO: start a goroutine monitoring ClientConn's connectivity state, and
// report error (and log) when stats is transient failure.
@ -109,7 +115,9 @@ func (t *Controller) run(ctx context.Context) {
// Note that this goroutine doesn't do anything to the old stream when there's a
// new one. In fact, there should be only one stream in progress, and new one
// should only be created when the old one fails (recv returns an error).
func (t *Controller) send(ctx context.Context) {
func (t *Controller) send(ctx context.Context, doneCh chan struct{}) {
defer func() { close(doneCh) }()
var stream grpc.ClientStream
for {
select {

@ -90,6 +90,11 @@ func newRefCountedWithConfig(config *bootstrap.Config) (XDSClient, error) {
singletonClient.clientImpl = c
singletonClient.refCount++
singletonClientImplCreateHook()
nodeID := "<unknown>"
if node, ok := config.XDSServer.NodeProto.(interface{ GetId() string }); ok {
nodeID = node.GetId()
}
logger.Infof("xDS node ID: %s", nodeID)
return &onceClosingClient{XDSClient: singletonClient}, nil
}

@ -0,0 +1,149 @@
/*
*
* Copyright 2022 gRPC 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 xdsresource
import (
"google.golang.org/grpc/internal/pretty"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
)
var (
// Compile time interface checks.
_ Type = clusterResourceType{}
_ ResourceData = &ClusterResourceData{}
// Singleton instantiation of the resource type implementation.
clusterType = clusterResourceType{
resourceTypeState: resourceTypeState{
v2TypeURL: "type.googleapis.com/envoy.api.v2.Cluster",
v3TypeURL: "type.googleapis.com/envoy.config.cluster.v3.Cluster",
typeEnum: ClusterResource,
allResourcesRequiredInSotW: true,
},
}
)
// clusterResourceType provides the resource-type specific functionality for a
// Cluster resource.
//
// Implements the Type interface.
type clusterResourceType struct {
resourceTypeState
}
// Decode deserializes and validates an xDS resource serialized inside the
// provided `Any` proto, as received from the xDS management server.
func (clusterResourceType) Decode(opts *DecodeOptions, resource *anypb.Any) (*DecodeResult, error) {
name, cluster, err := unmarshalClusterResource(resource, nil, opts.Logger)
switch {
case name == "":
// Name is unset only when protobuf deserialization fails.
return nil, err
case err != nil:
// Protobuf deserialization succeeded, but resource validation failed.
return &DecodeResult{Name: name, Resource: &ClusterResourceData{Resource: ClusterUpdate{}}}, err
}
// Perform extra validation here.
if err := securityConfigValidator(opts.BootstrapConfig, cluster.SecurityCfg); err != nil {
return &DecodeResult{Name: name, Resource: &ClusterResourceData{Resource: ClusterUpdate{}}}, err
}
return &DecodeResult{Name: name, Resource: &ClusterResourceData{Resource: cluster}}, nil
}
// ClusterResourceData wraps the configuration of a Cluster resource as received
// from the management server.
//
// Implements the ResourceData interface.
type ClusterResourceData struct {
ResourceData
// TODO: We have always stored update structs by value. See if this can be
// switched to a pointer?
Resource ClusterUpdate
}
// Equal returns true if other is equal to r.
func (c *ClusterResourceData) Equal(other ResourceData) bool {
if c == nil && other == nil {
return true
}
if (c == nil) != (other == nil) {
return false
}
return proto.Equal(c.Resource.Raw, other.Raw())
}
// ToJSON returns a JSON string representation of the resource data.
func (c *ClusterResourceData) ToJSON() string {
return pretty.ToJSON(c.Resource)
}
// Raw returns the underlying raw protobuf form of the cluster resource.
func (c *ClusterResourceData) Raw() *anypb.Any {
return c.Resource.Raw
}
// ClusterWatcher wraps the callbacks to be invoked for different events
// corresponding to the cluster resource being watched.
type ClusterWatcher interface {
// OnUpdate is invoked to report an update for the resource being watched.
OnUpdate(*ClusterResourceData)
// OnError is invoked under different error conditions including but not
// limited to the following:
// - authority mentioned in the resource is not found
// - resource name parsing error
// - resource deserialization error
// - resource validation error
// - ADS stream failure
// - connection failure
OnError(error)
// OnResourceDoesNotExist is invoked for a specific error condition where
// the requested resource is not found on the xDS management server.
OnResourceDoesNotExist()
}
type delegatingClusterWatcher struct {
watcher ClusterWatcher
}
func (d *delegatingClusterWatcher) OnUpdate(data ResourceData) {
c := data.(*ClusterResourceData)
d.watcher.OnUpdate(c)
}
func (d *delegatingClusterWatcher) OnError(err error) {
d.watcher.OnError(err)
}
func (d *delegatingClusterWatcher) OnResourceDoesNotExist() {
d.watcher.OnResourceDoesNotExist()
}
// WatchCluster uses xDS to discover the configuration associated with the
// provided cluster resource name.
func WatchCluster(p Producer, name string, w ClusterWatcher) (cancel func()) {
delegator := &delegatingClusterWatcher{watcher: w}
return p.WatchResource(clusterType, name, delegator)
}

@ -0,0 +1,144 @@
/*
*
* Copyright 2022 gRPC 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 xdsresource
import (
"google.golang.org/grpc/internal/pretty"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
)
var (
// Compile time interface checks.
_ Type = endpointsResourceType{}
_ ResourceData = &EndpointsResourceData{}
// Singleton instantiation of the resource type implementation.
endpointsType = endpointsResourceType{
resourceTypeState: resourceTypeState{
v2TypeURL: "type.googleapis.com/envoy.api.v2.ClusterLoadAssignment",
v3TypeURL: "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
typeEnum: EndpointsResource,
allResourcesRequiredInSotW: false,
},
}
)
// endpointsResourceType provides the resource-type specific functionality for a
// ClusterLoadAssignment (or Endpoints) resource.
//
// Implements the Type interface.
type endpointsResourceType struct {
resourceTypeState
}
// Decode deserializes and validates an xDS resource serialized inside the
// provided `Any` proto, as received from the xDS management server.
func (endpointsResourceType) Decode(opts *DecodeOptions, resource *anypb.Any) (*DecodeResult, error) {
name, rc, err := unmarshalEndpointsResource(resource, opts.Logger)
switch {
case name == "":
// Name is unset only when protobuf deserialization fails.
return nil, err
case err != nil:
// Protobuf deserialization succeeded, but resource validation failed.
return &DecodeResult{Name: name, Resource: &EndpointsResourceData{Resource: EndpointsUpdate{}}}, err
}
return &DecodeResult{Name: name, Resource: &EndpointsResourceData{Resource: rc}}, nil
}
// EndpointsResourceData wraps the configuration of an Endpoints resource as
// received from the management server.
//
// Implements the ResourceData interface.
type EndpointsResourceData struct {
ResourceData
// TODO: We have always stored update structs by value. See if this can be
// switched to a pointer?
Resource EndpointsUpdate
}
// Equal returns true if other is equal to r.
func (e *EndpointsResourceData) Equal(other ResourceData) bool {
if e == nil && other == nil {
return true
}
if (e == nil) != (other == nil) {
return false
}
return proto.Equal(e.Resource.Raw, other.Raw())
}
// ToJSON returns a JSON string representation of the resource data.
func (e *EndpointsResourceData) ToJSON() string {
return pretty.ToJSON(e.Resource)
}
// Raw returns the underlying raw protobuf form of the listener resource.
func (e *EndpointsResourceData) Raw() *anypb.Any {
return e.Resource.Raw
}
// EndpointsWatcher wraps the callbacks to be invoked for different
// events corresponding to the endpoints resource being watched.
type EndpointsWatcher interface {
// OnUpdate is invoked to report an update for the resource being watched.
OnUpdate(*EndpointsResourceData)
// OnError is invoked under different error conditions including but not
// limited to the following:
// - authority mentioned in the resource is not found
// - resource name parsing error
// - resource deserialization error
// - resource validation error
// - ADS stream failure
// - connection failure
OnError(error)
// OnResourceDoesNotExist is invoked for a specific error condition where
// the requested resource is not found on the xDS management server.
OnResourceDoesNotExist()
}
type delegatingEndpointsWatcher struct {
watcher EndpointsWatcher
}
func (d *delegatingEndpointsWatcher) OnUpdate(data ResourceData) {
e := data.(*EndpointsResourceData)
d.watcher.OnUpdate(e)
}
func (d *delegatingEndpointsWatcher) OnError(err error) {
d.watcher.OnError(err)
}
func (d *delegatingEndpointsWatcher) OnResourceDoesNotExist() {
d.watcher.OnResourceDoesNotExist()
}
// WatchEndpoints uses xDS to discover the configuration associated with the
// provided endpoints resource name.
func WatchEndpoints(p Producer, name string, w EndpointsWatcher) (cancel func()) {
delegator := &delegatingEndpointsWatcher{watcher: w}
return p.WatchResource(endpointsType, name, delegator)
}

@ -34,6 +34,9 @@ const (
// response. It's typically returned if the resource is removed in the xds
// server.
ErrorTypeResourceNotFound
// ErrorTypeResourceTypeUnsupported indicates the receipt of a message from
// the management server with resources of an unsupported resource type.
ErrorTypeResourceTypeUnsupported
)
type xdsClientError struct {

@ -0,0 +1,181 @@
/*
*
* Copyright 2022 gRPC 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 xdsresource
import (
"fmt"
"google.golang.org/grpc/internal/pretty"
"google.golang.org/grpc/xds/internal/xdsclient/bootstrap"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
)
var (
// Compile time interface checks.
_ Type = listenerResourceType{}
_ ResourceData = &ListenerResourceData{}
// Singleton instantiation of the resource type implementation.
listenerType = listenerResourceType{
resourceTypeState: resourceTypeState{
v2TypeURL: "type.googleapis.com/envoy.api.v2.Listener",
v3TypeURL: "type.googleapis.com/envoy.config.listener.v3.Listener",
typeEnum: ListenerResource,
allResourcesRequiredInSotW: true,
},
}
)
// listenerResourceType provides the resource-type specific functionality for a
// Listener resource.
//
// Implements the Type interface.
type listenerResourceType struct {
resourceTypeState
}
func securityConfigValidator(bc *bootstrap.Config, sc *SecurityConfig) error {
if sc == nil {
return nil
}
if sc.IdentityInstanceName != "" {
if _, ok := bc.CertProviderConfigs[sc.IdentityInstanceName]; !ok {
return fmt.Errorf("identitiy certificate provider instance name %q missing in bootstrap configuration", sc.IdentityInstanceName)
}
}
if sc.RootInstanceName != "" {
if _, ok := bc.CertProviderConfigs[sc.RootInstanceName]; !ok {
return fmt.Errorf("root certificate provider instance name %q missing in bootstrap configuration", sc.RootInstanceName)
}
}
return nil
}
func listenerValidator(bc *bootstrap.Config, lis ListenerUpdate) error {
if lis.InboundListenerCfg == nil || lis.InboundListenerCfg.FilterChains == nil {
return nil
}
return lis.InboundListenerCfg.FilterChains.Validate(func(fc *FilterChain) error {
if fc == nil {
return nil
}
return securityConfigValidator(bc, fc.SecurityCfg)
})
}
// Decode deserializes and validates an xDS resource serialized inside the
// provided `Any` proto, as received from the xDS management server.
func (listenerResourceType) Decode(opts *DecodeOptions, resource *anypb.Any) (*DecodeResult, error) {
name, listener, err := unmarshalListenerResource(resource, nil, opts.Logger)
switch {
case name == "":
// Name is unset only when protobuf deserialization fails.
return nil, err
case err != nil:
// Protobuf deserialization succeeded, but resource validation failed.
return &DecodeResult{Name: name, Resource: &ListenerResourceData{Resource: ListenerUpdate{}}}, err
}
// Perform extra validation here.
if err := listenerValidator(opts.BootstrapConfig, listener); err != nil {
return &DecodeResult{Name: name, Resource: &ListenerResourceData{Resource: ListenerUpdate{}}}, err
}
return &DecodeResult{Name: name, Resource: &ListenerResourceData{Resource: listener}}, nil
}
// ListenerResourceData wraps the configuration of a Listener resource as
// received from the management server.
//
// Implements the ResourceData interface.
type ListenerResourceData struct {
ResourceData
// TODO: We have always stored update structs by value. See if this can be
// switched to a pointer?
Resource ListenerUpdate
}
// Equal returns true if other is equal to l.
func (l *ListenerResourceData) Equal(other ResourceData) bool {
if l == nil && other == nil {
return true
}
if (l == nil) != (other == nil) {
return false
}
return proto.Equal(l.Resource.Raw, other.Raw())
}
// ToJSON returns a JSON string representation of the resource data.
func (l *ListenerResourceData) ToJSON() string {
return pretty.ToJSON(l.Resource)
}
// Raw returns the underlying raw protobuf form of the listener resource.
func (l *ListenerResourceData) Raw() *anypb.Any {
return l.Resource.Raw
}
// ListenerWatcher wraps the callbacks to be invoked for different
// events corresponding to the listener resource being watched.
type ListenerWatcher interface {
// OnUpdate is invoked to report an update for the resource being watched.
OnUpdate(*ListenerResourceData)
// OnError is invoked under different error conditions including but not
// limited to the following:
// - authority mentioned in the resource is not found
// - resource name parsing error
// - resource deserialization error
// - resource validation error
// - ADS stream failure
// - connection failure
OnError(error)
// OnResourceDoesNotExist is invoked for a specific error condition where
// the requested resource is not found on the xDS management server.
OnResourceDoesNotExist()
}
type delegatingListenerWatcher struct {
watcher ListenerWatcher
}
func (d *delegatingListenerWatcher) OnUpdate(data ResourceData) {
l := data.(*ListenerResourceData)
d.watcher.OnUpdate(l)
}
func (d *delegatingListenerWatcher) OnError(err error) {
d.watcher.OnError(err)
}
func (d *delegatingListenerWatcher) OnResourceDoesNotExist() {
d.watcher.OnResourceDoesNotExist()
}
// WatchListener uses xDS to discover the configuration associated with the
// provided listener resource name.
func WatchListener(p Producer, name string, w ListenerWatcher) (cancel func()) {
delegator := &delegatingListenerWatcher{watcher: w}
return p.WatchResource(listenerType, name, delegator)
}

@ -0,0 +1,158 @@
/*
*
* Copyright 2022 gRPC 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 xdsresource
import (
"google.golang.org/grpc/internal/grpclog"
"google.golang.org/grpc/xds/internal/xdsclient/bootstrap"
"google.golang.org/protobuf/types/known/anypb"
)
// Producer contains a single method to discover resource configuration from a
// remote management server using xDS APIs.
//
// The xdsclient package provides a concrete implementation of this interface.
type Producer interface {
// WatchResource uses xDS to discover the resource associated with the
// provided resource name. The resource type implementation determines how
// xDS requests are sent out and how responses are deserialized and
// validated. Upon receipt of a response from the management server, an
// appropriate callback on the watcher is invoked.
WatchResource(rType Type, resourceName string, watcher ResourceWatcher) (cancel func())
}
// ResourceWatcher wraps the callbacks to be invoked for different events
// corresponding to the resource being watched.
type ResourceWatcher interface {
// OnUpdate is invoked to report an update for the resource being watched.
// The ResourceData parameter needs to be type asserted to the appropriate
// type for the resource being watched.
OnUpdate(ResourceData)
// OnError is invoked under different error conditions including but not
// limited to the following:
// - authority mentioned in the resource is not found
// - resource name parsing error
// - resource deserialization error
// - resource validation error
// - ADS stream failure
// - connection failure
OnError(error)
// OnResourceDoesNotExist is invoked for a specific error condition where
// the requested resource is not found on the xDS management server.
OnResourceDoesNotExist()
}
// TODO: Once the implementation is complete, rename this interface as
// ResourceType and get rid of the existing ResourceType enum.
// Type wraps all resource-type specific functionality. Each supported resource
// type will provide an implementation of this interface.
type Type interface {
// V2TypeURL is the xDS type URL of this resource type for v2 transport.
V2TypeURL() string
// V3TypeURL is the xDS type URL of this resource type for v3 transport.
V3TypeURL() string
// TypeEnum is an enumerated value for this resource type. This can be used
// for logging/debugging purposes, as well in cases where the resource type
// is to be uniquely identified but the actual functionality provided by the
// resource type is not required.
//
// TODO: once Type is renamed to ResourceType, rename ResourceType to
// ResourceTypeEnum.
TypeEnum() ResourceType
// AllResourcesRequiredInSotW indicates whether this resource type requires
// that all resources be present in every SotW response from the server. If
// true, a response that does not include a previously seen resource will be
// interpreted as a deletion of that resource.
AllResourcesRequiredInSotW() bool
// Decode deserializes and validates an xDS resource serialized inside the
// provided `Any` proto, as received from the xDS management server.
//
// If protobuf deserialization fails or resource validation fails,
// returns a non-nil error. Otherwise, returns a fully populated
// DecodeResult.
Decode(*DecodeOptions, *anypb.Any) (*DecodeResult, error)
}
// ResourceData contains the configuration data sent by the xDS management
// server, associated with the resource being watched. Every resource type must
// provide an implementation of this interface to represent the configuration
// received from the xDS management server.
type ResourceData interface {
isResourceData()
// Equal returns true if the passed in resource data is equal to that of the
// receiver.
Equal(ResourceData) bool
// ToJSON returns a JSON string representation of the resource data.
ToJSON() string
Raw() *anypb.Any
}
// DecodeOptions wraps the options required by ResourceType implementation for
// decoding configuration received from the xDS management server.
type DecodeOptions struct {
// BootstrapConfig contains the bootstrap configuration passed to the
// top-level xdsClient. This contains useful data for resource validation.
BootstrapConfig *bootstrap.Config
// Logger is to be used for emitting logs during the Decode operation.
Logger *grpclog.PrefixLogger
}
// DecodeResult is the result of a decode operation.
type DecodeResult struct {
// Name is the name of the resource being watched.
Name string
// Resource contains the configuration associated with the resource being
// watched.
Resource ResourceData
}
// resourceTypeState wraps the static state associated with concrete resource
// type implementations, which can then embed this struct and get the methods
// implemented here for free.
type resourceTypeState struct {
v2TypeURL string
v3TypeURL string
typeEnum ResourceType
allResourcesRequiredInSotW bool
}
func (r resourceTypeState) V2TypeURL() string {
return r.v2TypeURL
}
func (r resourceTypeState) V3TypeURL() string {
return r.v3TypeURL
}
func (r resourceTypeState) TypeEnum() ResourceType {
return r.typeEnum
}
func (r resourceTypeState) AllResourcesRequiredInSotW() bool {
return r.allResourcesRequiredInSotW
}

@ -0,0 +1,145 @@
/*
*
* Copyright 2022 gRPC 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 xdsresource
import (
"google.golang.org/grpc/internal/pretty"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/anypb"
)
var (
// Compile time interface checks.
_ Type = routeConfigResourceType{}
_ ResourceData = &RouteConfigResourceData{}
// Singleton instantiation of the resource type implementation.
routeConfigType = routeConfigResourceType{
resourceTypeState: resourceTypeState{
v2TypeURL: "type.googleapis.com/envoy.api.v2.RouteConfiguration",
v3TypeURL: "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
typeEnum: RouteConfigResource,
allResourcesRequiredInSotW: false,
},
}
)
// routeConfigResourceType provides the resource-type specific functionality for
// a RouteConfiguration resource.
//
// Implements the Type interface.
type routeConfigResourceType struct {
resourceTypeState
}
// Decode deserializes and validates an xDS resource serialized inside the
// provided `Any` proto, as received from the xDS management server.
func (routeConfigResourceType) Decode(opts *DecodeOptions, resource *anypb.Any) (*DecodeResult, error) {
name, rc, err := unmarshalRouteConfigResource(resource, opts.Logger)
switch {
case name == "":
// Name is unset only when protobuf deserialization fails.
return nil, err
case err != nil:
// Protobuf deserialization succeeded, but resource validation failed.
return &DecodeResult{Name: name, Resource: &RouteConfigResourceData{Resource: RouteConfigUpdate{}}}, err
}
return &DecodeResult{Name: name, Resource: &RouteConfigResourceData{Resource: rc}}, nil
}
// RouteConfigResourceData wraps the configuration of a RouteConfiguration
// resource as received from the management server.
//
// Implements the ResourceData interface.
type RouteConfigResourceData struct {
ResourceData
// TODO: We have always stored update structs by value. See if this can be
// switched to a pointer?
Resource RouteConfigUpdate
}
// Equal returns true if other is equal to r.
func (r *RouteConfigResourceData) Equal(other ResourceData) bool {
if r == nil && other == nil {
return true
}
if (r == nil) != (other == nil) {
return false
}
return proto.Equal(r.Resource.Raw, other.Raw())
}
// ToJSON returns a JSON string representation of the resource data.
func (r *RouteConfigResourceData) ToJSON() string {
return pretty.ToJSON(r.Resource)
}
// Raw returns the underlying raw protobuf form of the route configuration
// resource.
func (r *RouteConfigResourceData) Raw() *anypb.Any {
return r.Resource.Raw
}
// RouteConfigWatcher wraps the callbacks to be invoked for different
// events corresponding to the route configuration resource being watched.
type RouteConfigWatcher interface {
// OnUpdate is invoked to report an update for the resource being watched.
OnUpdate(*RouteConfigResourceData)
// OnError is invoked under different error conditions including but not
// limited to the following:
// - authority mentioned in the resource is not found
// - resource name parsing error
// - resource deserialization error
// - resource validation error
// - ADS stream failure
// - connection failure
OnError(error)
// OnResourceDoesNotExist is invoked for a specific error condition where
// the requested resource is not found on the xDS management server.
OnResourceDoesNotExist()
}
type delegatingRouteConfigWatcher struct {
watcher RouteConfigWatcher
}
func (d *delegatingRouteConfigWatcher) OnUpdate(data ResourceData) {
rc := data.(*RouteConfigResourceData)
d.watcher.OnUpdate(rc)
}
func (d *delegatingRouteConfigWatcher) OnError(err error) {
d.watcher.OnError(err)
}
func (d *delegatingRouteConfigWatcher) OnResourceDoesNotExist() {
d.watcher.OnResourceDoesNotExist()
}
// WatchRouteConfig uses xDS to discover the configuration associated with the
// provided route configuration resource name.
func WatchRouteConfig(p Producer, name string, w RouteConfigWatcher) (cancel func()) {
delegator := &delegatingRouteConfigWatcher{watcher: w}
return p.WatchResource(routeConfigType, name, delegator)
}

@ -718,6 +718,9 @@ github.com/grafana/go-gelf/v2/gelf
# github.com/grafana/gomemcache v0.0.0-20230105173749-11f792309e1f
## explicit; go 1.18
github.com/grafana/gomemcache/memcache
# github.com/grafana/loki/pkg/push v0.0.0-00010101000000-000000000000 => ./pkg/push
## explicit; go 1.19
github.com/grafana/loki/pkg/push
# github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd => github.com/grafana/regexp v0.0.0-20221005093135-b4c2bcb0a4b6
## explicit; go 1.17
github.com/grafana/regexp
@ -1507,7 +1510,7 @@ google.golang.org/genproto/googleapis/rpc/status
google.golang.org/genproto/googleapis/type/date
google.golang.org/genproto/googleapis/type/expr
google.golang.org/genproto/protobuf/field_mask
# google.golang.org/grpc v1.51.0
# google.golang.org/grpc v1.52.0
## explicit; go 1.17
google.golang.org/grpc
google.golang.org/grpc/attributes
@ -1516,7 +1519,7 @@ google.golang.org/grpc/balancer
google.golang.org/grpc/balancer/base
google.golang.org/grpc/balancer/grpclb
google.golang.org/grpc/balancer/grpclb/grpc_lb_v1
google.golang.org/grpc/balancer/grpclb/state
google.golang.org/grpc/balancer/grpclb/grpclbstate
google.golang.org/grpc/balancer/roundrobin
google.golang.org/grpc/balancer/weightedroundrobin
google.golang.org/grpc/balancer/weightedtarget
@ -1963,3 +1966,4 @@ sigs.k8s.io/yaml
# github.com/gocql/gocql => github.com/grafana/gocql v0.0.0-20200605141915-ba5dc39ece85
# github.com/hashicorp/memberlist => github.com/grafana/memberlist v0.3.1-0.20220714140823-09ffed8adbbe
# github.com/grafana/regexp => github.com/grafana/regexp v0.0.0-20221005093135-b4c2bcb0a4b6
# github.com/grafana/loki/pkg/push => ./pkg/push

Loading…
Cancel
Save