@@ -43,26 +43,29 @@ import (
4343 "google.golang.org/grpc"
4444 "google.golang.org/grpc/credentials"
4545 "google.golang.org/grpc/credentials/insecure"
46+ "google.golang.org/grpc/metadata"
4647)
4748
4849const (
49- LogLevelDebug = "debug"
50+ LogLevelDebug = "debug"
51+ headerKeyValuePairElements = 2
5052)
5153
5254type flags struct {
5355 LogLevel string `kong:"enum='error,warn,info,debug',help='Log level.',default='info'"`
5456
5557 Upload struct {
56- StoreAddress string `kong:"required,help='gRPC address to sends symbols to.'"`
57- BearerToken string `kong:"help='Bearer token to authenticate with store.',env='PARCA_DEBUGINFO_BEARER_TOKEN'"`
58- BearerTokenFile string `kong:"help='File to read bearer token from to authenticate with store.'"`
59- Insecure bool `kong:"help='Send gRPC requests via plaintext instead of TLS.'"`
60- InsecureSkipVerify bool `kong:"help='Skip TLS certificate verification.'"`
61- NoExtract bool `kong:"help='Do not extract debug information from binaries, just upload the binary as is.'"`
62- NoInitiate bool `kong:"help='Do not initiate the upload, just check if it should be initiated.'"`
63- Force bool `kong:"help='Force upload even if the Build ID is already uploaded.'"`
64- Type string `kong:"enum='debuginfo,executable,sources',help='Type of the debug information to upload.',default='debuginfo'"`
65- BuildID string `kong:"help='Build ID of the binary to upload.'"`
58+ StoreAddress string `kong:"required,help='gRPC address to sends symbols to.'"`
59+ BearerToken string `kong:"help='Bearer token to authenticate with store.',env='PARCA_DEBUGINFO_BEARER_TOKEN'"`
60+ BearerTokenFile string `kong:"help='File to read bearer token from to authenticate with store.'"`
61+ Insecure bool `kong:"help='Send gRPC requests via plaintext instead of TLS.'"`
62+ InsecureSkipVerify bool `kong:"help='Skip TLS certificate verification.'"`
63+ GRPCHeaders map [string ]string `kong:"help='Additional gRPC headers to send with each request (key=value pairs).'"`
64+ NoExtract bool `kong:"help='Do not extract debug information from binaries, just upload the binary as is.'"`
65+ NoInitiate bool `kong:"help='Do not initiate the upload, just check if it should be initiated.'"`
66+ Force bool `kong:"help='Force upload even if the Build ID is already uploaded.'"`
67+ Type string `kong:"enum='debuginfo,executable,sources',help='Type of the debug information to upload.',default='debuginfo'"`
68+ BuildID string `kong:"help='Build ID of the binary to upload.'"`
6669
6770 Path string `kong:"required,arg,name='path',help='Paths to upload.',type:'path'"`
6871 } `cmd:"" help:"Upload debug information files."`
@@ -429,10 +432,25 @@ func grpcConn(reg prometheus.Registerer, flags flags) (*grpc.ClientConn, error)
429432 met .EnableClientHandlingTimeHistogram ()
430433 reg .MustRegister (met )
431434
435+ unaryInterceptors := []grpc.UnaryClientInterceptor {
436+ met .UnaryClientInterceptor (),
437+ }
438+ streamInterceptors := []grpc.StreamClientInterceptor {}
439+
440+ if len (flags .Upload .GRPCHeaders ) > 0 {
441+ unaryInterceptors = append (
442+ []grpc.UnaryClientInterceptor {customHeadersUnaryInterceptor (flags .Upload .GRPCHeaders )},
443+ unaryInterceptors ... ,
444+ )
445+ streamInterceptors = append (
446+ []grpc.StreamClientInterceptor {customHeadersStreamInterceptor (flags .Upload .GRPCHeaders )},
447+ streamInterceptors ... ,
448+ )
449+ }
450+
432451 opts := []grpc.DialOption {
433- grpc .WithUnaryInterceptor (
434- met .UnaryClientInterceptor (),
435- ),
452+ grpc .WithChainUnaryInterceptor (unaryInterceptors ... ),
453+ grpc .WithChainStreamInterceptor (streamInterceptors ... ),
436454 }
437455 if flags .Upload .Insecure {
438456 opts = append (opts , grpc .WithTransportCredentials (insecure .NewCredentials ()))
@@ -480,6 +498,28 @@ func (t *perRequestBearerToken) RequireTransportSecurity() bool {
480498 return ! t .insecure
481499}
482500
501+ func customHeadersUnaryInterceptor (headers map [string ]string ) grpc.UnaryClientInterceptor {
502+ return func (ctx context.Context , method string , req , reply interface {}, cc * grpc.ClientConn , invoker grpc.UnaryInvoker , opts ... grpc.CallOption ) error {
503+ kvPairs := make ([]string , 0 , len (headers )* headerKeyValuePairElements )
504+ for key , value := range headers {
505+ kvPairs = append (kvPairs , key , value )
506+ }
507+ newCtx := metadata .AppendToOutgoingContext (ctx , kvPairs ... )
508+ return invoker (newCtx , method , req , reply , cc , opts ... )
509+ }
510+ }
511+
512+ func customHeadersStreamInterceptor (headers map [string ]string ) grpc.StreamClientInterceptor {
513+ return func (ctx context.Context , desc * grpc.StreamDesc , cc * grpc.ClientConn , method string , streamer grpc.Streamer , opts ... grpc.CallOption ) (grpc.ClientStream , error ) {
514+ kvPairs := make ([]string , 0 , len (headers )* headerKeyValuePairElements )
515+ for key , value := range headers {
516+ kvPairs = append (kvPairs , key , value )
517+ }
518+ newCtx := metadata .AppendToOutgoingContext (ctx , kvPairs ... )
519+ return streamer (newCtx , desc , cc , method , opts ... )
520+ }
521+ }
522+
483523func uploadViaSignedURL (ctx context.Context , url string , r io.Reader ) error {
484524 req , err := http .NewRequestWithContext (ctx , http .MethodPut , url , r )
485525 if err != nil {
@@ -587,7 +627,7 @@ func getNoteHexString(sectionBytes []byte, name string, noteType uint32) (string
587627
588628 // read descsz and compute the last index of the note data
589629 dataSize := binary .LittleEndian .Uint32 (sectionBytes [idx - 4 : idx ])
590- idxDataEnd := uint64 (idxDataStart ) + uint64 (dataSize ) //nolint:gosec
630+ idxDataEnd := uint64 (idxDataStart ) + uint64 (dataSize )
591631
592632 // Check sanity (64 is totally arbitrary, as we only use it for Linux ID and Build ID)
593633 if idxDataEnd > uint64 (len (sectionBytes )) || dataSize > 64 {
0 commit comments