Skip to content

Commit 763552b

Browse files
committed
fix!: address all gRPC deprecations
This addresses all deprecation warnings from `grpc-go` in tests and examples (e.g. use `NewClient` instead of `DialContext`, the context from `DialContext` was only useful if the [`grpc.WithBlock()`] option was used, this is also deprecated) The only exceptions is the change in `proxy/codec.go` to implement `encoding.Codec` instead `grpc.Codec` which is a breaking change. The clients must now use `grpc.NewClient` with the `grpc.WithDefaultCallOptions(grpc.ForceCodec(proxy.Codec()))` option. And servers must call `encoding.RegisterCodec(proxy.Codec())` before calling `grpc.NewServer()`. [`grpc.WithBlock()`]: https://pkg.go.dev/google.golang.org/grpc#WithBlock Signed-off-by: Maxime Brunet <max@brnt.mx>
1 parent 02f82db commit 763552b

File tree

7 files changed

+100
-77
lines changed

7 files changed

+100
-77
lines changed

.golangci.yml

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -94,17 +94,19 @@ linters-settings:
9494
cyclop:
9595
# the maximal code complexity to report
9696
max-complexity: 20
97-
# depguard:
98-
# Main:
99-
# deny:
100-
# - github.com/OpenPeeDeeP/depguard # this is just an example
97+
# depguard:
98+
# rules:
99+
# Main:
100+
# list-mode: lax
101+
# deny:
102+
# - pkg: github.com/OpenPeeDeeP/depguard
103+
# desc: this is just an example
101104

102105
linters:
103106
enable-all: true
104107
disable-all: false
105108
fast: false
106109
disable:
107-
- exhaustivestruct
108110
- exhaustruct
109111
- err113
110112
- forbidigo
@@ -120,27 +122,19 @@ linters:
120122
- mnd
121123
- nestif
122124
- nonamedreturns
123-
- nosnakecase
124125
- paralleltest
125126
- tagalign
126127
- tagliatelle
127128
- thelper
128129
- typecheck
129130
- varnamelen
130131
- wrapcheck
131-
- depguard # Disabled because starting with golangci-lint 1.53.0 it doesn't allow denylist alone anymore
132+
- depguard # an Allow and/or Deny package list must be configured
132133
- testifylint # complains about our assert recorder and has a number of false positives for assert.Greater(t, thing, 1)
133134
- protogetter # complains about us using Value field on typed spec, instead of GetValue which has a different signature
134135
- perfsprint # complains about us using fmt.Sprintf in non-performance critical code, updating just kres took too long
135136
# abandoned linters for which golangci shows the warning that the repo is archived by the owner
136-
- deadcode
137-
- golint
138-
- ifshort
139-
- interfacer
140-
- maligned
141-
- scopelint
142-
- structcheck
143-
- varcheck
137+
- execinquery
144138
# disabled as it seems to be broken - goes into imported libraries and reports issues there
145139
- musttag
146140
- goimports # same as gci

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ GRPC_GATEWAY_VERSION ?= 2.20.0
2323
VTPROTOBUF_VERSION ?= 0.6.0
2424
GOIMPORTS_VERSION ?= 0.21.0
2525
DEEPCOPY_VERSION ?= v0.5.6
26-
GOLANGCILINT_VERSION ?= v1.58.2
26+
GOLANGCILINT_VERSION ?= v1.59.1
2727
GOFUMPT_VERSION ?= v0.6.0
2828
GO_VERSION ?= 1.22.3
2929
GO_BUILDFLAGS ?=

README.md

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,48 +36,56 @@ First, define `Backend` implementation to identify specific upstream.
3636
For one to one proxying, `SingleBackend` might be used:
3737

3838
```go
39+
conn, err := grpc.NewClient(
40+
"api-service.staging.svc.local",
41+
grpc.WithDefaultCallOptions(grpc.ForceCodec(proxy.Codec())),
42+
)
43+
if err != nil {
44+
log.Fatal(err)
45+
}
46+
3947
backend := &proxy.SingleBackend{
4048
GetConn: func(ctx context.Context) (context.Context, *grpc.ClientConn, error) {
4149
md, _ := metadata.FromIncomingContext(ctx)
4250

4351
// Copy the inbound metadata explicitly.
4452
outCtx := metadata.NewOutgoingContext(ctx, md.Copy())
45-
// Make sure we use DialContext so the dialing can be cancelled/time out together with the context.
46-
conn, err := grpc.DialContext(ctx, "api-service.staging.svc.local", grpc.WithCodec(proxy.Codec())) // nolint: staticcheck
4753

48-
return outCtx, conn, err
54+
return outCtx, conn, nil
4955
},
5056
}
5157
```
5258

5359
Defining a `StreamDirector` that decides where (if at all) to send the request
5460

5561
```go
56-
director = func(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) {
62+
director = func(ctx context.Context, fullMethodName string) (proxy.Mode, []proxy.Backend, error) {
5763
// Make sure we never forward internal services.
5864
if strings.HasPrefix(fullMethodName, "/com.example.internal.") {
59-
return nil, nil, grpc.Errorf(codes.Unimplemented, "Unknown method")
65+
return proxy.One2One, nil, status.Errorf(codes.Unimplemented, "Unknown method")
6066
}
61-
md, ok := metadata.FromContext(ctx)
67+
68+
md, ok := metadata.FromIncomingContext(ctx)
69+
6270
if ok {
6371
// Decide on which backend to dial
6472
if val, exists := md[":authority"]; exists && val[0] == "staging.api.example.com" {
65-
// Make sure we use DialContext so the dialing can be cancelled/time out together with the context.
66-
return ctx, backend1, nil
73+
return proxy.One2One, []proxy.Backend{stagingBackend}, nil
6774
} else if val, exists := md[":authority"]; exists && val[0] == "api.example.com" {
68-
return ctx, backend2, nil
75+
return proxy.One2One, []proxy.Backend{prodBackend}, nil
6976
}
7077
}
71-
return nil, grpc.Errorf(codes.Unimplemented, "Unknown method")
78+
79+
return proxy.One2One, nil, status.Errorf(codes.Unimplemented, "Unknown method")
7280
}
7381
```
7482

7583
Then you need to register it with a `grpc.Server`.
7684
The server may have other handlers that will be served locally:
7785

7886
```go
87+
encoding.RegisterCodec(proxy.Codec())
7988
server := grpc.NewServer(
80-
grpc.CustomCodec(proxy.Codec()),
8189
grpc.UnknownServiceHandler(
8290
proxy.TransparentHandler(director),
8391
proxy.WithMode(proxy.One2One),

proxy/codec.go

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,29 @@
11
package proxy
22

33
import (
4-
"fmt"
5-
6-
"google.golang.org/grpc"
4+
"google.golang.org/grpc/encoding"
75
"google.golang.org/protobuf/proto"
86
)
97

10-
// Codec returns a proxying grpc.Codec with the default protobuf codec as parent.
8+
// Codec returns a proxying [encoding.Coder] with the default protobuf codec as parent.
119
//
1210
// See CodecWithParent.
13-
//
14-
//nolint:staticcheck
15-
func Codec() grpc.Codec { //nolint:ireturn
11+
func Codec() encoding.Codec {
1612
return CodecWithParent(&protoCodec{})
1713
}
1814

19-
// CodecWithParent returns a proxying grpc.Codec with a user provided codec as parent.
15+
// CodecWithParent returns a proxying [encoding.Codec] with a user provided codec as parent.
2016
//
2117
// This codec is *crucial* to the functioning of the proxy. It allows the proxy server to be oblivious
2218
// to the schema of the forwarded messages. It basically treats a gRPC message frame as raw bytes.
2319
// However, if the server handler, or the client caller are not proxy-internal functions it will fall back
2420
// to trying to decode the message using a fallback codec.
25-
//
26-
//nolint:staticcheck
27-
func CodecWithParent(fallback grpc.Codec) grpc.Codec { //nolint:ireturn
21+
func CodecWithParent(fallback encoding.Codec) encoding.Codec {
2822
return &rawCodec{fallback}
2923
}
3024

3125
type rawCodec struct {
32-
parentCodec grpc.Codec //nolint: staticcheck
26+
parentCodec encoding.Codec
3327
}
3428

3529
type frame struct {
@@ -61,8 +55,8 @@ func (c *rawCodec) Unmarshal(data []byte, v interface{}) error {
6155
return nil
6256
}
6357

64-
func (c *rawCodec) String() string {
65-
return fmt.Sprintf("proxy>%s", c.parentCodec.String())
58+
func (c *rawCodec) Name() string {
59+
return c.parentCodec.Name()
6660
}
6761

6862
// protoCodec is a Codec implementation with protobuf. It is the default rawCodec for gRPC.
@@ -76,6 +70,6 @@ func (protoCodec) Unmarshal(data []byte, v interface{}) error {
7670
return proto.Unmarshal(data, v.(proto.Message)) //nolint:forcetypeassert
7771
}
7872

79-
func (protoCodec) String() string {
73+
func (protoCodec) Name() string {
8074
return "proto"
8175
}

proxy/examples_test.go

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ package proxy_test
55

66
import (
77
"context"
8+
"log"
89
"strings"
910

1011
"google.golang.org/grpc"
1112
"google.golang.org/grpc/codes"
13+
"google.golang.org/grpc/credentials/insecure"
14+
"google.golang.org/grpc/encoding"
1215
"google.golang.org/grpc/metadata"
1316
"google.golang.org/grpc/status"
1417

@@ -19,8 +22,10 @@ var director proxy.StreamDirector
1922

2023
// ExampleRegisterService is a simple example of registering a service with the proxy.
2124
func ExampleRegisterService() {
25+
encoding.RegisterCodec(proxy.Codec())
26+
2227
// A gRPC server with the proxying codec enabled.
23-
server := grpc.NewServer(grpc.CustomCodec(proxy.Codec())) //nolint: staticcheck
28+
server := grpc.NewServer()
2429

2530
// Register a TestService with 4 of its methods explicitly.
2631
proxy.RegisterService(server, director,
@@ -34,29 +39,48 @@ func ExampleRegisterService() {
3439

3540
// ExampleTransparentHandler is an example of redirecting all requests to the proxy.
3641
func ExampleTransparentHandler() {
42+
encoding.RegisterCodec(proxy.Codec())
43+
3744
grpc.NewServer(
38-
grpc.CustomCodec(proxy.Codec()), //nolint: staticcheck
39-
grpc.UnknownServiceHandler(proxy.TransparentHandler(director)))
45+
grpc.UnknownServiceHandler(proxy.TransparentHandler(director)),
46+
)
4047

4148
// Output:
4249
}
4350

4451
// Provide sa simple example of a director that shields internal services and dials a staging or production backend.
4552
// This is a *very naive* implementation that creates a new connection on every request. Consider using pooling.
4653
func ExampleStreamDirector() {
47-
simpleBackendGen := func(hostname string) proxy.Backend {
54+
simpleBackendGen := func(hostname string) (proxy.Backend, error) {
55+
conn, err := grpc.NewClient(
56+
hostname,
57+
grpc.WithDefaultCallOptions(grpc.ForceCodec(proxy.Codec())),
58+
grpc.WithTransportCredentials(insecure.NewCredentials()),
59+
)
60+
if err != nil {
61+
return nil, err
62+
}
63+
4864
return &proxy.SingleBackend{
4965
GetConn: func(ctx context.Context) (context.Context, *grpc.ClientConn, error) {
5066
md, _ := metadata.FromIncomingContext(ctx)
5167

5268
// Copy the inbound metadata explicitly.
5369
outCtx := metadata.NewOutgoingContext(ctx, md.Copy())
54-
// Make sure we use DialContext so the dialing can be canceled/time out together with the context.
55-
conn, err := grpc.DialContext(ctx, hostname, grpc.WithCodec(proxy.Codec())) //nolint: staticcheck
5670

57-
return outCtx, conn, err
71+
return outCtx, conn, nil
5872
},
59-
}
73+
}, nil
74+
}
75+
76+
stagingBackend, err := simpleBackendGen("api-service.staging.svc.local")
77+
if err != nil {
78+
log.Fatal("failed to create staging backend:", err)
79+
}
80+
81+
prodBackend, err := simpleBackendGen("api-service.prod.svc.local")
82+
if err != nil {
83+
log.Fatal("failed to create production backend:", err)
6084
}
6185

6286
director = func(ctx context.Context, fullMethodName string) (proxy.Mode, []proxy.Backend, error) {
@@ -70,9 +94,9 @@ func ExampleStreamDirector() {
7094
if ok {
7195
// Decide on which backend to dial
7296
if val, exists := md[":authority"]; exists && val[0] == "staging.api.example.com" {
73-
return proxy.One2One, []proxy.Backend{simpleBackendGen("api-service.staging.svc.local")}, nil
97+
return proxy.One2One, []proxy.Backend{stagingBackend}, nil
7498
} else if val, exists := md[":authority"]; exists && val[0] == "api.example.com" {
75-
return proxy.One2One, []proxy.Backend{simpleBackendGen("api-service.prod.svc.local")}, nil
99+
return proxy.One2One, []proxy.Backend{prodBackend}, nil
76100
}
77101
}
78102

proxy/handler_one2many_test.go

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@ import (
99
"errors"
1010
"fmt"
1111
"io"
12-
"log"
1312
"net"
1413
"os"
1514
"strconv"
1615
"strings"
17-
"sync"
1816
"testing"
1917
"time"
2018

@@ -24,6 +22,7 @@ import (
2422
"google.golang.org/grpc"
2523
"google.golang.org/grpc/codes"
2624
"google.golang.org/grpc/credentials/insecure"
25+
"google.golang.org/grpc/encoding"
2726
"google.golang.org/grpc/grpclog"
2827
"google.golang.org/grpc/metadata"
2928
"google.golang.org/grpc/status"
@@ -141,11 +140,7 @@ func (s *assertingMultiService) PingStreamError(pb.MultiService_PingStreamErrorS
141140

142141
type assertingBackend struct {
143142
conn *grpc.ClientConn
144-
145-
addr string
146143
i int
147-
148-
mu sync.Mutex
149144
}
150145

151146
func (b *assertingBackend) String() string {
@@ -157,21 +152,11 @@ func (b *assertingBackend) GetConnection(ctx context.Context, _ string) (context
157152
// Explicitly copy the metadata, otherwise the tests will fail.
158153
outCtx := metadata.NewOutgoingContext(ctx, md.Copy())
159154

160-
if b.addr == "fail" {
155+
if b.conn == nil {
161156
return ctx, nil, status.Error(codes.Unavailable, "backend connection failed")
162157
}
163158

164-
b.mu.Lock()
165-
defer b.mu.Unlock()
166-
167-
if b.conn != nil {
168-
return outCtx, b.conn, nil
169-
}
170-
171-
var err error
172-
b.conn, err = grpc.DialContext(ctx, b.addr, grpc.WithInsecure(), grpc.WithCodec(proxy.Codec())) //nolint: staticcheck
173-
174-
return outCtx, b.conn, err
159+
return outCtx, b.conn, nil
175160
}
176161

177162
func (b *assertingBackend) AppendInfo(streaming bool, resp []byte) ([]byte, error) {
@@ -574,15 +559,23 @@ func (s *ProxyOne2ManySuite) SetupSuite() {
574559
backends := make([]*assertingBackend, numUpstreams)
575560

576561
for i := range backends {
562+
var conn *grpc.ClientConn
563+
conn, err = grpc.NewClient(
564+
s.serverListeners[i].Addr().String(),
565+
grpc.WithTransportCredentials(insecure.NewCredentials()),
566+
grpc.WithDefaultCallOptions(grpc.ForceCodec(proxy.Codec())),
567+
)
568+
require.NoError(s.T(), err)
569+
577570
backends[i] = &assertingBackend{
571+
conn: conn,
578572
i: i,
579-
addr: s.serverListeners[i].Addr().String(),
580573
}
581574
}
582575

583576
failingBackend := &assertingBackend{
577+
conn: nil,
584578
i: -1,
585-
addr: "fail",
586579
}
587580

588581
// Setup of the proxy's Director.
@@ -628,8 +621,9 @@ func (s *ProxyOne2ManySuite) SetupSuite() {
628621
return proxy.One2Many, result, nil
629622
}
630623

624+
encoding.RegisterCodec(proxy.Codec())
625+
631626
s.proxy = grpc.NewServer(
632-
grpc.CustomCodec(proxy.Codec()), //nolint: staticcheck
633627
grpc.UnknownServiceHandler(proxy.TransparentHandler(director)),
634628
)
635629
// Ping handler is handled as an explicit registration and not as a TransparentHandler.
@@ -694,5 +688,5 @@ func TestProxyOne2ManySuite(t *testing.T) {
694688
}
695689

696690
func init() {
697-
grpclog.SetLogger(log.New(os.Stderr, "grpc: ", log.LstdFlags)) //nolint: staticcheck
691+
grpclog.SetLoggerV2(grpclog.NewLoggerV2(os.Stderr, os.Stderr, os.Stderr))
698692
}

0 commit comments

Comments
 (0)