grpc: log non-utf8 string fields in messages (#52208)

This commit is contained in:
Geoffrey Gilmore 2023-06-12 16:58:55 -07:00 committed by GitHub
parent a8aa7d98b8
commit 464c58883b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 853 additions and 26 deletions

View File

@ -1,3 +1,4 @@
load("//dev:go_defs.bzl", "go_test")
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
load("@rules_buf//buf:defs.bzl", "buf_lint_test")

View File

@ -12,12 +12,16 @@ go_library(
visibility = ["//:__subpackages__"],
deps = [
"//internal/env",
"//lib/errors",
"@com_github_prometheus_client_golang//prometheus",
"@com_github_prometheus_client_golang//prometheus/promauto",
"@com_github_sourcegraph_log//:log",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//codes",
"@org_golang_google_grpc//status",
"@org_golang_google_protobuf//proto",
"@org_golang_google_protobuf//reflect/protopath",
"@org_golang_google_protobuf//reflect/protorange",
],
)
@ -26,9 +30,13 @@ go_test(
srcs = ["common_test.go"],
embed = [":internalerrs"],
deps = [
"//internal/grpc/testprotos/news/v1:news",
"@com_github_google_go_cmp//cmp",
"@com_github_google_go_cmp//cmp/cmpopts",
"@org_golang_google_grpc//:go_default_library",
"@org_golang_google_grpc//codes",
"@org_golang_google_grpc//status",
"@org_golang_google_protobuf//proto",
"@org_golang_google_protobuf//types/known/timestamppb",
],
)

View File

@ -2,6 +2,12 @@ package internalerrs
import (
"strings"
"unicode/utf8"
"github.com/sourcegraph/sourcegraph/lib/errors"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protopath"
"google.golang.org/protobuf/reflect/protorange"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
@ -12,20 +18,20 @@ import (
type callBackClientStream struct {
grpc.ClientStream
postMessageSend func(error)
postMessageReceive func(error)
postMessageSend func(message any, err error)
postMessageReceive func(message any, err error)
}
func (c *callBackClientStream) SendMsg(m interface{}) error {
func (c *callBackClientStream) SendMsg(m any) error {
err := c.ClientStream.SendMsg(m)
c.postMessageSend(err)
c.postMessageSend(m, err)
return err
}
func (c *callBackClientStream) RecvMsg(m interface{}) error {
func (c *callBackClientStream) RecvMsg(m any) error {
err := c.ClientStream.RecvMsg(m)
c.postMessageReceive(err)
c.postMessageReceive(m, err)
return err
}
@ -87,3 +93,31 @@ func splitMethodName(fullMethod string) (string, string) {
}
return "unknown", "unknown"
}
// findNonUTF8StringFields returns a list of field names that contain invalid UTF-8 strings
// in the given proto message.
//
// Example: ["author", "attachments[1].key_value_attachment.data["key2"]`]
func findNonUTF8StringFields(m proto.Message) ([]string, error) {
if m == nil {
return nil, nil
}
var fields []string
err := protorange.Range(m.ProtoReflect(), func(p protopath.Values) error {
last := p.Index(-1)
s, ok := last.Value.Interface().(string)
if ok && !utf8.ValidString(s) {
fieldName := p.Path[1:].String()
fields = append(fields, strings.TrimPrefix(fieldName, "."))
}
return nil
})
if err != nil {
return nil, errors.Wrap(err, "iterating over proto message")
}
return fields, nil
}

View File

@ -2,9 +2,15 @@ package internalerrs
import (
"errors"
"sort"
"strings"
"testing"
"github.com/google/go-cmp/cmp/cmpopts"
newspb "github.com/sourcegraph/sourcegraph/internal/grpc/testprotos/news/v1"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/google/go-cmp/cmp"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
@ -12,7 +18,8 @@ import (
)
func TestCallBackClientStream(t *testing.T) {
t.Run("SendMsg calls postMessageSend with error", func(t *testing.T) {
t.Run("SendMsg calls postMessageSend with message and error", func(t *testing.T) {
sentinelMessage := struct{}{}
sentinelErr := errors.New("send error")
var called bool
@ -20,16 +27,19 @@ func TestCallBackClientStream(t *testing.T) {
ClientStream: &mockClientStream{
sendErr: sentinelErr,
},
postMessageSend: func(err error) {
postMessageSend: func(message any, err error) {
called = true
if diff := cmp.Diff(message, sentinelMessage); diff != "" {
t.Errorf("postMessageSend called with unexpected message (-want +got):\n%s", diff)
}
if !errors.Is(err, sentinelErr) {
t.Errorf("got %v, want %v", err, sentinelErr)
}
},
}
sendErr := stream.SendMsg(nil)
sendErr := stream.SendMsg(sentinelMessage)
if !called {
t.Error("postMessageSend not called")
}
@ -39,7 +49,8 @@ func TestCallBackClientStream(t *testing.T) {
}
})
t.Run("RecvMsg calls postMessageReceive with error", func(t *testing.T) {
t.Run("RecvMsg calls postMessageReceive with message and error", func(t *testing.T) {
sentinelMessage := struct{}{}
sentinelErr := errors.New("receive error")
var called bool
@ -47,16 +58,19 @@ func TestCallBackClientStream(t *testing.T) {
ClientStream: &mockClientStream{
recvErr: sentinelErr,
},
postMessageReceive: func(err error) {
postMessageReceive: func(message any, err error) {
called = true
if diff := cmp.Diff(message, sentinelMessage); diff != "" {
t.Errorf("postMessageReceive called with unexpected message (-want +got):\n%s", diff)
}
if !errors.Is(err, sentinelErr) {
t.Errorf("got %v, want %v", err, sentinelErr)
}
},
}
receiveErr := stream.RecvMsg(nil)
receiveErr := stream.RecvMsg(sentinelMessage)
if !called {
t.Error("postMessageReceive not called")
}
@ -74,11 +88,11 @@ type mockClientStream struct {
recvErr error
}
func (s *mockClientStream) SendMsg(m interface{}) error {
func (s *mockClientStream) SendMsg(any) error {
return s.sendErr
}
func (s *mockClientStream) RecvMsg(m interface{}) error {
func (s *mockClientStream) RecvMsg(any) error {
return s.recvErr
}
@ -221,3 +235,70 @@ func TestSplitMethodName(t *testing.T) {
})
}
}
func TestFindNonUTF8StringFields(t *testing.T) {
// Create instances of the BinaryAttachment and KeyValueAttachment messages
invalidBinaryAttachment := &newspb.BinaryAttachment{
Name: "inval\x80id_binary",
Data: []byte("sample data"),
}
invalidKeyValueAttachment := &newspb.KeyValueAttachment{
Name: "inval\x80id_key_value",
Data: map[string]string{
"key1": "value1",
"key2": "inval\x80id_value",
},
}
// Create a sample Article message with invalid UTF-8 strings
article := &newspb.Article{
Author: "inval\x80id_author",
Date: &timestamppb.Timestamp{Seconds: 1234567890},
Title: "valid_title",
Content: "valid_content",
Status: newspb.Article_STATUS_PUBLISHED,
Attachments: []*newspb.Attachment{
{Contents: &newspb.Attachment_BinaryAttachment{BinaryAttachment: invalidBinaryAttachment}},
{Contents: &newspb.Attachment_KeyValueAttachment{KeyValueAttachment: invalidKeyValueAttachment}},
},
}
tests := []struct {
name string
message proto.Message
expectedPaths []string
}{
{
name: "Article with invalid UTF-8 strings",
message: article,
expectedPaths: []string{
"author",
"attachments[0].binary_attachment.name",
"attachments[1].key_value_attachment.name",
`attachments[1].key_value_attachment.data["key2"]`,
},
},
{
name: "nil message",
message: nil,
expectedPaths: []string{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
invalidFields, err := findNonUTF8StringFields(tt.message)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
sort.Strings(invalidFields)
sort.Strings(tt.expectedPaths)
if diff := cmp.Diff(tt.expectedPaths, invalidFields, cmpopts.EquateEmpty()); diff != "" {
t.Fatalf("unexpected invalid fields (-want +got):\n%s", diff)
}
})
}
}

View File

@ -2,8 +2,14 @@ package internalerrs
import (
"context"
"encoding/json"
"fmt"
"io"
"strings"
"github.com/sourcegraph/sourcegraph/lib/errors"
"google.golang.org/grpc/codes"
"google.golang.org/protobuf/proto"
"github.com/sourcegraph/log"
"google.golang.org/grpc"
@ -18,6 +24,9 @@ var (
envLoggingEnabled = env.MustGetBool("SRC_GRPC_INTERNAL_ERROR_LOGGING_ENABLED", true, "Enables logging of gRPC internal errors")
envLogStackTracesEnabled = env.MustGetBool("SRC_GRPC_INTERNAL_ERROR_LOGGING_LOG_STACK_TRACES", false, "Enables including stack traces in logs of gRPC internal errors")
envLogNonUTF8ProtobufMessages = env.MustGetBool("SRC_GRPC_INTERNAL_ERROR_LOGGING_LOG_NON_UTF8_PROTOBUF_MESSAGES_ENABLED", false, "Enables logging of non-UTF-8 protobuf messages")
envLogNonUTF8ProtobufMessagesMaxSize = env.MustGetInt("SRC_GRPC_INTERNAL_ERROR_LOGGING_LOG_NON_UTF8_PROTOBUF_MESSAGES_MAX_SIZE_BYTES", 1024, "Maximum size of non-UTF-8 protobuf messages to log before truncation, in bytes. Negative values disable truncation.")
)
// LoggingUnaryClientInterceptor returns a grpc.UnaryClientInterceptor that logs
@ -35,7 +44,7 @@ func LoggingUnaryClientInterceptor(l log.Logger) grpc.UnaryClientInterceptor {
err := invoker(ctx, fullMethod, req, reply, cc, opts...)
if err != nil {
serviceName, methodName := splitMethodName(fullMethod)
doLog(logger, serviceName, methodName, err)
doLog(logger, serviceName, methodName, req, err)
}
return err
@ -59,7 +68,11 @@ func LoggingStreamClientInterceptor(l log.Logger) grpc.StreamClientInterceptor {
stream, err := streamer(ctx, desc, cc, fullMethod, opts...)
if err != nil {
doLog(logger, serviceName, methodName, err)
// Note: This is a bit hacky, we provide a nil message here since the message isn't available
// until after the stream is created.
//
// This is fine since the error is already available, and the non-utf8 string check is robust against nil messages.
doLog(logger, serviceName, methodName, nil, err)
return nil, err
}
@ -71,20 +84,22 @@ func LoggingStreamClientInterceptor(l log.Logger) grpc.StreamClientInterceptor {
func newLoggingClientStream(s grpc.ClientStream, logger log.Logger, serviceName, methodName string) *callBackClientStream {
return &callBackClientStream{
ClientStream: s,
postMessageSend: func(err error) {
postMessageSend: func(m any, err error) {
if err != nil {
doLog(logger, serviceName, methodName, err)
doLog(logger, serviceName, methodName, m, err)
}
},
postMessageReceive: func(err error) {
postMessageReceive: func(m any, err error) {
if err != nil && err != io.EOF { // EOF is expected at the end of a stream, so no need to log an error
doLog(logger, serviceName, methodName, err)
doLog(logger, serviceName, methodName, m, err)
}
},
}
}
func doLog(logger log.Logger, serviceName, methodName string, err error) {
func doLog(logger log.Logger, serviceName, methodName string, payload any, err error) {
if err == nil {
return
}
@ -98,15 +113,77 @@ func doLog(logger log.Logger, serviceName, methodName string, err error) {
return
}
fields := []log.Field{
allFields := []log.Field{
log.String("grpcService", serviceName),
log.String("grpcMethod", methodName),
log.String("grpcCode", s.Code().String()),
}
if envLogStackTracesEnabled {
fields = append(fields, log.String("errWithStack", fmt.Sprintf("%+v", err)))
allFields = append(allFields, log.String("errWithStack", fmt.Sprintf("%+v", err)))
}
logger.Error(s.Message(), fields...)
if isNonUTF8StringError(s) {
if m, ok := payload.(proto.Message); ok {
allFields = append(
allFields,
additionalNonUTF8StringDebugFields(m, envLogNonUTF8ProtobufMessages, envLogNonUTF8ProtobufMessagesMaxSize)...,
)
}
}
logger.Error(s.Message(), allFields...)
}
// additionalNonUTF8StringDebugFields returns additional log fields that should be included when logging a non-UTF8 string error.
//
// By default, this includes the names of all fields that contain non-UTF8 strings.
// If shouldLogMessageJSON is true, then the JSON representation of the message is also included.
// The maxMessageSizeLogBytes parameter controls the maximum size of the message that will be logged, after which it will be truncated. Negative values disable truncation.
func additionalNonUTF8StringDebugFields(message proto.Message, shouldLogMessageJSON bool, maxMessageLogSizeBytes int) []log.Field {
var allFields []log.Field
// Add the names of all protobuf fields that contain non-UTF-8 strings to the log.
badFields, err := findNonUTF8StringFields(message)
if err != nil {
allFields = append(allFields, log.Error(errors.Wrapf(err, "failed to find non-UTF8 string allFields")))
return allFields
}
allFields = append(allFields, log.Strings("nonUTF8StringFields", badFields))
// Add the JSON representation of the message to the log.
if !shouldLogMessageJSON {
return allFields
}
// Note: we can't use the protojson library here since it doesn't support messages with non-UTF8 strings.
jsonBytes, err := json.Marshal(message)
if err != nil {
allFields = append(allFields, log.Error(errors.Wrapf(err, "failed to marshal protobuf message to bytes")))
return allFields
}
if maxMessageLogSizeBytes < 0 { // If truncation is disabled, set the max size to the full message size.
maxMessageLogSizeBytes = len(jsonBytes)
}
bytesToTruncate := len(jsonBytes) - maxMessageLogSizeBytes
if bytesToTruncate > 0 {
jsonBytes = jsonBytes[:maxMessageLogSizeBytes]
jsonBytes = append(jsonBytes, []byte(fmt.Sprintf("...(truncated %d bytes)", bytesToTruncate))...)
}
allFields = append(allFields, log.String("messageJSON", string(jsonBytes)))
return allFields
}
func isNonUTF8StringError(s *status.Status) bool {
if s.Code() != codes.Internal {
return false
}
return strings.Contains(s.Message(), "string field contains invalid UTF-8")
}

View File

@ -66,14 +66,14 @@ func newPrometheusServerStream(s grpc.ClientStream, serviceName, methodName stri
return &callBackClientStream{
ClientStream: s,
postMessageSend: func(err error) {
postMessageSend: func(_ any, err error) {
if err != nil {
observeOnce.Do(func() {
doObservation(serviceName, methodName, err)
})
}
},
postMessageReceive: func(err error) {
postMessageReceive: func(_ any, err error) {
if err != nil {
if err == io.EOF {
// EOF signals end of stream, not an error. We handle this by setting err to nil, because

View File

@ -0,0 +1,31 @@
load("@rules_proto//proto:defs.bzl", "proto_library")
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
proto_library(
name = "news_proto",
srcs = ["news.proto"],
visibility = ["//:__subpackages__"],
deps = ["@com_google_protobuf//:timestamp_proto"],
)
go_proto_library(
name = "news_go_proto",
importpath = "github.com/sourcegraph/sourcegraph/internal/grpc/testprotos/news",
proto = ":news_proto",
visibility = ["//:__subpackages__"],
)
go_library(
name = "news",
embed = [":v1_go_proto"],
importpath = "github.com/sourcegraph/sourcegraph/internal/grpc/testprotos/news/v1",
visibility = ["//:__subpackages__"],
)
go_proto_library(
name = "v1_go_proto",
importpath = "github.com/sourcegraph/sourcegraph/internal/grpc/testprotos/news/v1",
proto = ":news_proto",
visibility = ["//:__subpackages__"],
)

View File

@ -0,0 +1,7 @@
# Configuration file for https://buf.build/, which we use for Protobuf code generation.
version: v1
plugins:
- plugin: buf.build/protocolbuffers/go:v1.29.1
out: .
opt:
- paths=source_relative

View File

@ -0,0 +1,538 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// Note (@Sourcegraph): This file was copied / adapted from
// https://github.com/protocolbuffers/protobuf-go/blob/v1.30.0/internal/testprotos/news/news.proto to aid our testing.
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.29.1
// protoc (unknown)
// source: news.proto
package v1
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type Article_Status int32
const (
Article_STATUS_DRAFT_UNSPECIFIED Article_Status = 0
Article_STATUS_PUBLISHED Article_Status = 1
Article_STATUS_REVOKED Article_Status = 2
)
// Enum value maps for Article_Status.
var (
Article_Status_name = map[int32]string{
0: "STATUS_DRAFT_UNSPECIFIED",
1: "STATUS_PUBLISHED",
2: "STATUS_REVOKED",
}
Article_Status_value = map[string]int32{
"STATUS_DRAFT_UNSPECIFIED": 0,
"STATUS_PUBLISHED": 1,
"STATUS_REVOKED": 2,
}
)
func (x Article_Status) Enum() *Article_Status {
p := new(Article_Status)
*p = x
return p
}
func (x Article_Status) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (Article_Status) Descriptor() protoreflect.EnumDescriptor {
return file_news_proto_enumTypes[0].Descriptor()
}
func (Article_Status) Type() protoreflect.EnumType {
return &file_news_proto_enumTypes[0]
}
func (x Article_Status) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use Article_Status.Descriptor instead.
func (Article_Status) EnumDescriptor() ([]byte, []int) {
return file_news_proto_rawDescGZIP(), []int{0, 0}
}
type Article struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Author string `protobuf:"bytes,1,opt,name=author,proto3" json:"author,omitempty"`
Date *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=date,proto3" json:"date,omitempty"`
Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"`
Content string `protobuf:"bytes,4,opt,name=content,proto3" json:"content,omitempty"`
Status Article_Status `protobuf:"varint,8,opt,name=status,proto3,enum=grpc.testprotos.news.v1.Article_Status" json:"status,omitempty"`
Attachments []*Attachment `protobuf:"bytes,7,rep,name=attachments,proto3" json:"attachments,omitempty"`
}
func (x *Article) Reset() {
*x = Article{}
if protoimpl.UnsafeEnabled {
mi := &file_news_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Article) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Article) ProtoMessage() {}
func (x *Article) ProtoReflect() protoreflect.Message {
mi := &file_news_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Article.ProtoReflect.Descriptor instead.
func (*Article) Descriptor() ([]byte, []int) {
return file_news_proto_rawDescGZIP(), []int{0}
}
func (x *Article) GetAuthor() string {
if x != nil {
return x.Author
}
return ""
}
func (x *Article) GetDate() *timestamppb.Timestamp {
if x != nil {
return x.Date
}
return nil
}
func (x *Article) GetTitle() string {
if x != nil {
return x.Title
}
return ""
}
func (x *Article) GetContent() string {
if x != nil {
return x.Content
}
return ""
}
func (x *Article) GetStatus() Article_Status {
if x != nil {
return x.Status
}
return Article_STATUS_DRAFT_UNSPECIFIED
}
func (x *Article) GetAttachments() []*Attachment {
if x != nil {
return x.Attachments
}
return nil
}
type Attachment struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Types that are assignable to Contents:
//
// *Attachment_BinaryAttachment
// *Attachment_KeyValueAttachment
Contents isAttachment_Contents `protobuf_oneof:"contents"`
}
func (x *Attachment) Reset() {
*x = Attachment{}
if protoimpl.UnsafeEnabled {
mi := &file_news_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Attachment) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Attachment) ProtoMessage() {}
func (x *Attachment) ProtoReflect() protoreflect.Message {
mi := &file_news_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Attachment.ProtoReflect.Descriptor instead.
func (*Attachment) Descriptor() ([]byte, []int) {
return file_news_proto_rawDescGZIP(), []int{1}
}
func (m *Attachment) GetContents() isAttachment_Contents {
if m != nil {
return m.Contents
}
return nil
}
func (x *Attachment) GetBinaryAttachment() *BinaryAttachment {
if x, ok := x.GetContents().(*Attachment_BinaryAttachment); ok {
return x.BinaryAttachment
}
return nil
}
func (x *Attachment) GetKeyValueAttachment() *KeyValueAttachment {
if x, ok := x.GetContents().(*Attachment_KeyValueAttachment); ok {
return x.KeyValueAttachment
}
return nil
}
type isAttachment_Contents interface {
isAttachment_Contents()
}
type Attachment_BinaryAttachment struct {
BinaryAttachment *BinaryAttachment `protobuf:"bytes,1,opt,name=binary_attachment,json=binaryAttachment,proto3,oneof"`
}
type Attachment_KeyValueAttachment struct {
KeyValueAttachment *KeyValueAttachment `protobuf:"bytes,2,opt,name=key_value_attachment,json=keyValueAttachment,proto3,oneof"`
}
func (*Attachment_BinaryAttachment) isAttachment_Contents() {}
func (*Attachment_KeyValueAttachment) isAttachment_Contents() {}
type BinaryAttachment struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
}
func (x *BinaryAttachment) Reset() {
*x = BinaryAttachment{}
if protoimpl.UnsafeEnabled {
mi := &file_news_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BinaryAttachment) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BinaryAttachment) ProtoMessage() {}
func (x *BinaryAttachment) ProtoReflect() protoreflect.Message {
mi := &file_news_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BinaryAttachment.ProtoReflect.Descriptor instead.
func (*BinaryAttachment) Descriptor() ([]byte, []int) {
return file_news_proto_rawDescGZIP(), []int{2}
}
func (x *BinaryAttachment) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *BinaryAttachment) GetData() []byte {
if x != nil {
return x.Data
}
return nil
}
type KeyValueAttachment struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Data map[string]string `protobuf:"bytes,2,rep,name=data,proto3" json:"data,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}
func (x *KeyValueAttachment) Reset() {
*x = KeyValueAttachment{}
if protoimpl.UnsafeEnabled {
mi := &file_news_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *KeyValueAttachment) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*KeyValueAttachment) ProtoMessage() {}
func (x *KeyValueAttachment) ProtoReflect() protoreflect.Message {
mi := &file_news_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use KeyValueAttachment.ProtoReflect.Descriptor instead.
func (*KeyValueAttachment) Descriptor() ([]byte, []int) {
return file_news_proto_rawDescGZIP(), []int{3}
}
func (x *KeyValueAttachment) GetName() string {
if x != nil {
return x.Name
}
return ""
}
func (x *KeyValueAttachment) GetData() map[string]string {
if x != nil {
return x.Data
}
return nil
}
var File_news_proto protoreflect.FileDescriptor
var file_news_proto_rawDesc = []byte{
0x0a, 0x0a, 0x6e, 0x65, 0x77, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x17, 0x67, 0x72,
0x70, 0x63, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x6e, 0x65,
0x77, 0x73, 0x2e, 0x76, 0x31, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdb, 0x02, 0x0a, 0x07, 0x41, 0x72, 0x74, 0x69, 0x63,
0x6c, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01,
0x28, 0x09, 0x52, 0x06, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x2e, 0x0a, 0x04, 0x64, 0x61,
0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73,
0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x64, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69,
0x74, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65,
0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28,
0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x3f, 0x0a, 0x06, 0x73, 0x74,
0x61, 0x74, 0x75, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x67, 0x72, 0x70,
0x63, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x6e, 0x65, 0x77,
0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x72, 0x74, 0x69, 0x63, 0x6c, 0x65, 0x2e, 0x53, 0x74, 0x61,
0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x45, 0x0a, 0x0b, 0x61,
0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x23, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x73, 0x2e, 0x6e, 0x65, 0x77, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, 0x61, 0x63,
0x68, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e,
0x74, 0x73, 0x22, 0x50, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x0a, 0x18,
0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x44, 0x52, 0x41, 0x46, 0x54, 0x5f, 0x55, 0x4e, 0x53,
0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x53, 0x54,
0x41, 0x54, 0x55, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x53, 0x48, 0x45, 0x44, 0x10, 0x01,
0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x45, 0x56, 0x4f, 0x4b,
0x45, 0x44, 0x10, 0x02, 0x22, 0xd3, 0x01, 0x0a, 0x0a, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d,
0x65, 0x6e, 0x74, 0x12, 0x58, 0x0a, 0x11, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x5f, 0x61, 0x74,
0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29,
0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73,
0x2e, 0x6e, 0x65, 0x77, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x41,
0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x10, 0x62, 0x69, 0x6e,
0x61, 0x72, 0x79, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x5f, 0x0a,
0x14, 0x6b, 0x65, 0x79, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x61, 0x74, 0x74, 0x61, 0x63,
0x68, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x67, 0x72,
0x70, 0x63, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x2e, 0x6e, 0x65,
0x77, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x41, 0x74,
0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x12, 0x6b, 0x65, 0x79, 0x56,
0x61, 0x6c, 0x75, 0x65, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x0a,
0x0a, 0x08, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x3a, 0x0a, 0x10, 0x42, 0x69,
0x6e, 0x61, 0x72, 0x79, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12,
0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c,
0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0xac, 0x01, 0x0a, 0x12, 0x4b, 0x65, 0x79, 0x56, 0x61,
0x6c, 0x75, 0x65, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x12, 0x0a,
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
0x65, 0x12, 0x49, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x35, 0x2e, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x73, 0x2e, 0x6e, 0x65, 0x77, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x65, 0x79, 0x56, 0x61, 0x6c,
0x75, 0x65, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x44, 0x61, 0x74,
0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x37, 0x0a, 0x09,
0x44, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79,
0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x45, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2f,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x67, 0x72, 0x61, 0x70, 0x68, 0x2f, 0x69, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x73, 0x2f, 0x6e, 0x65, 0x77, 0x73, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
file_news_proto_rawDescOnce sync.Once
file_news_proto_rawDescData = file_news_proto_rawDesc
)
func file_news_proto_rawDescGZIP() []byte {
file_news_proto_rawDescOnce.Do(func() {
file_news_proto_rawDescData = protoimpl.X.CompressGZIP(file_news_proto_rawDescData)
})
return file_news_proto_rawDescData
}
var file_news_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
var file_news_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_news_proto_goTypes = []interface{}{
(Article_Status)(0), // 0: grpc.testprotos.news.v1.Article.Status
(*Article)(nil), // 1: grpc.testprotos.news.v1.Article
(*Attachment)(nil), // 2: grpc.testprotos.news.v1.Attachment
(*BinaryAttachment)(nil), // 3: grpc.testprotos.news.v1.BinaryAttachment
(*KeyValueAttachment)(nil), // 4: grpc.testprotos.news.v1.KeyValueAttachment
nil, // 5: grpc.testprotos.news.v1.KeyValueAttachment.DataEntry
(*timestamppb.Timestamp)(nil), // 6: google.protobuf.Timestamp
}
var file_news_proto_depIdxs = []int32{
6, // 0: grpc.testprotos.news.v1.Article.date:type_name -> google.protobuf.Timestamp
0, // 1: grpc.testprotos.news.v1.Article.status:type_name -> grpc.testprotos.news.v1.Article.Status
2, // 2: grpc.testprotos.news.v1.Article.attachments:type_name -> grpc.testprotos.news.v1.Attachment
3, // 3: grpc.testprotos.news.v1.Attachment.binary_attachment:type_name -> grpc.testprotos.news.v1.BinaryAttachment
4, // 4: grpc.testprotos.news.v1.Attachment.key_value_attachment:type_name -> grpc.testprotos.news.v1.KeyValueAttachment
5, // 5: grpc.testprotos.news.v1.KeyValueAttachment.data:type_name -> grpc.testprotos.news.v1.KeyValueAttachment.DataEntry
6, // [6:6] is the sub-list for method output_type
6, // [6:6] is the sub-list for method input_type
6, // [6:6] is the sub-list for extension type_name
6, // [6:6] is the sub-list for extension extendee
0, // [0:6] is the sub-list for field type_name
}
func init() { file_news_proto_init() }
func file_news_proto_init() {
if File_news_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_news_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Article); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_news_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Attachment); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_news_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BinaryAttachment); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_news_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*KeyValueAttachment); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_news_proto_msgTypes[1].OneofWrappers = []interface{}{
(*Attachment_BinaryAttachment)(nil),
(*Attachment_KeyValueAttachment)(nil),
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_news_proto_rawDesc,
NumEnums: 1,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_news_proto_goTypes,
DependencyIndexes: file_news_proto_depIdxs,
EnumInfos: file_news_proto_enumTypes,
MessageInfos: file_news_proto_msgTypes,
}.Build()
File_news_proto = out.File
file_news_proto_rawDesc = nil
file_news_proto_goTypes = nil
file_news_proto_depIdxs = nil
}

View File

@ -0,0 +1,46 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// Note (@Sourcegraph): This file was copied / adapted from
// https://github.com/protocolbuffers/protobuf-go/blob/v1.30.0/internal/testprotos/news/news.proto to aid our testing.
syntax = "proto3";
package grpc.testprotos.news.v1;
import "google/protobuf/timestamp.proto";
option go_package = "github.com/sourcegraph/sourcegraph/internal/grpc/testprotos/news/v1";
message Article {
enum Status {
STATUS_DRAFT_UNSPECIFIED = 0;
STATUS_PUBLISHED = 1;
STATUS_REVOKED = 2;
}
string author = 1;
google.protobuf.Timestamp date = 2;
string title = 3;
string content = 4;
Status status = 8;
repeated Attachment attachments = 7;
}
message Attachment {
oneof contents {
BinaryAttachment binary_attachment = 1;
KeyValueAttachment key_value_attachment = 2;
}
}
message BinaryAttachment {
string name = 1;
bytes data = 2;
}
message KeyValueAttachment {
string name = 1;
map<string, string> data = 2;
}

View File

@ -124,6 +124,10 @@ env:
# OTEL_EXPORTER_OTLP_ENDPOINT: http://127.0.0.1:4318
# OTEL_EXPORTER_OTLP_PROTOCOL: http/json
# Enable full protobuf message logging when a non-utf8 string error occurs
SRC_GRPC_INTERNAL_ERROR_LOGGING_LOG_NON_UTF8_PROTOBUF_MESSAGES_ENABLED: "true"
SRC_GRPC_INTERNAL_ERROR_LOGGING_LOG_NON_UTF8_PROTOBUF_MESSAGES_MAX_SIZE_BYTES: 1024
commands:
server:
description: Run an all-in-one sourcegraph/server image