Skip to content

Commit a5b858b

Browse files
authored
fix: Replace deprecated grpc.Dial with grpc.NewClient (#745)
* Replace usages of grpc.Dial with grpc.NewClient * Propagate errors in rlp log client streaming Propogates errors received from the rlp via gRPC when sending requests. Now that we don't block on dialing the server, we need to handle errors at the sending requests level. * chore: Make pool tests pass Fixes the failing pool tests in `plumbing` and `rlp/internal/ingress` following the change to use `grpc.NewClient` instead of `grpc.Dial`. This is because they were testing that connections were initiated when a new doppler was added. However, with the change to NewClient, connections won't be made until the resulting grpc client connections are used for RPC. This also means that dopplers may be added to the pool map when in the past they would not be added due to the connection on Dial failing. One approach to getting the existing tests to pass would have been to call `Connect` on the grpc client connections to force them to leave idle mode. However, `Connect` is experimental, and really gRPC is encouraging us not to care about the connection state when creating client connections. To fix the tests we ended up just asserting on the pool size. One downside of this was that we couldn't see a nice way to assert that `Close` was called on the gRPC client connection when `Close` was called for a doppler address. We could have replaced the connection creation with an interface and mocked that but it didn't seem worth it. --------- Signed-off-by: Andrew Crump <[email protected]> Signed-off-by: Carson Long <[email protected]> Signed-off-by: Rebecca Roberts <[email protected]>
1 parent ed1b5fe commit a5b858b

File tree

15 files changed

+108
-168
lines changed

15 files changed

+108
-168
lines changed

src/integration_tests/fakes/doppler.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func DopplerEgressV1Client(addr string) (func(), plumbing.DopplerClient) {
2121
)
2222
Expect(err).ToNot(HaveOccurred())
2323

24-
out, err := grpc.Dial(addr, grpc.WithTransportCredentials(creds))
24+
out, err := grpc.NewClient(addr, grpc.WithTransportCredentials(creds))
2525
Expect(err).ToNot(HaveOccurred())
2626
return func() {
2727
_ = out.Close()
@@ -37,7 +37,7 @@ func DopplerEgressV2Client(addr string) (func(), loggregator_v2.EgressClient) {
3737
)
3838
Expect(err).ToNot(HaveOccurred())
3939

40-
out, err := grpc.Dial(addr, grpc.WithTransportCredentials(creds))
40+
out, err := grpc.NewClient(addr, grpc.WithTransportCredentials(creds))
4141
Expect(err).ToNot(HaveOccurred())
4242
return func() {
4343
_ = out.Close()
@@ -53,7 +53,7 @@ func DopplerIngressV1Client(addr string) (func(), plumbing.DopplerIngestor_Pushe
5353
)
5454
Expect(err).ToNot(HaveOccurred())
5555

56-
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(creds))
56+
conn, err := grpc.NewClient(addr, grpc.WithTransportCredentials(creds))
5757
Expect(err).ToNot(HaveOccurred())
5858
client := plumbing.NewDopplerIngestorClient(conn)
5959

@@ -91,7 +91,7 @@ func DopplerIngressV2Client(addr string) (func(), loggregator_v2.Ingress_SenderC
9191
)
9292
Expect(err).ToNot(HaveOccurred())
9393

94-
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(creds))
94+
conn, err := grpc.NewClient(addr, grpc.WithTransportCredentials(creds))
9595
Expect(err).ToNot(HaveOccurred())
9696
client := loggregator_v2.NewIngressClient(conn)
9797

src/metricemitter/client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func NewClient(addr string, opts ...ClientOption) (*Client, error) {
7979
opt(client)
8080
}
8181

82-
conn, err := grpc.Dial(addr, client.dialOpts...)
82+
conn, err := grpc.NewClient(addr, client.dialOpts...)
8383
if err != nil {
8484
return nil, err
8585
}

src/plumbing/pool.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func (p *Pool) connectToDoppler(addr string) {
6060
for {
6161
log.Printf("adding doppler %s", addr)
6262

63-
conn, err := grpc.Dial(addr, p.dialOpts...)
63+
conn, err := grpc.NewClient(addr, p.dialOpts...)
6464
if err != nil {
6565
// TODO: We don't yet understand how this could happen, we should.
6666
// TODO: Replace with exponential backoff.
@@ -79,3 +79,9 @@ func (p *Pool) connectToDoppler(addr string) {
7979
return
8080
}
8181
}
82+
83+
func (p *Pool) Size() int {
84+
p.mu.Lock()
85+
defer p.mu.Unlock()
86+
return len(p.dopplers)
87+
}

src/plumbing/pool_test.go

Lines changed: 15 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package plumbing_test
22

33
import (
4-
"net"
5-
64
"code.cloudfoundry.org/loggregator-release/src/plumbing"
75

86
"golang.org/x/net/context"
@@ -16,62 +14,33 @@ import (
1614
var _ = Describe("Pool", func() {
1715
var (
1816
pool *plumbing.Pool
19-
20-
listeners []net.Listener
21-
servers []*grpc.Server
2217
)
2318

2419
BeforeEach(func() {
2520
pool = plumbing.NewPool(grpc.WithTransportCredentials(insecure.NewCredentials()))
2621
})
2722

28-
AfterEach(func() {
29-
for _, lis := range listeners {
30-
lis.Close()
31-
}
32-
listeners = nil
33-
34-
for _, server := range servers {
35-
server.Stop()
36-
}
37-
servers = nil
38-
})
39-
40-
Describe("Register() & Close()", func() {
41-
var (
42-
lis1, lis2 net.Listener
43-
accepter1, accepter2 chan bool
44-
)
45-
23+
Describe("Register()", func() {
4624
BeforeEach(func() {
47-
lis1, accepter1 = accepter(startListener("127.0.0.1:0"))
48-
lis2, accepter2 = accepter(startListener("127.0.0.1:0"))
49-
listeners = append(listeners, lis1, lis2)
25+
pool.RegisterDoppler("192.0.2.10:8080")
26+
pool.RegisterDoppler("192.0.2.11:8080")
5027
})
5128

52-
Describe("Register()", func() {
53-
It("fills pool with connections to each doppler", func() {
54-
pool.RegisterDoppler(lis1.Addr().String())
55-
pool.RegisterDoppler(lis2.Addr().String())
56-
57-
Eventually(accepter1).Should(HaveLen(1))
58-
Eventually(accepter2).Should(HaveLen(1))
59-
})
29+
It("adds entries to the pool", func() {
30+
Eventually(pool.Size).Should(Equal(2))
6031
})
32+
})
6133

62-
Describe("Close()", func() {
63-
BeforeEach(func() {
64-
pool.RegisterDoppler(lis1.Addr().String())
65-
})
66-
67-
It("stops the gRPC connections", func() {
68-
pool.Close(lis1.Addr().String())
69-
lis1.Close()
34+
Describe("Close()", func() {
35+
BeforeEach(func() {
36+
pool.RegisterDoppler("192.0.2.10:8080")
37+
pool.RegisterDoppler("192.0.2.11:8080")
38+
})
7039

71-
// Drain the channel
72-
Eventually(accepter1, 5).ShouldNot(Receive())
73-
Consistently(accepter1).Should(HaveLen(0))
74-
})
40+
It("removes entries from the pool", func() {
41+
Eventually(pool.Size).Should(Equal(2))
42+
pool.Close("192.0.2.11:8080")
43+
Eventually(pool.Size).Should(Equal(1))
7544
})
7645
})
7746

@@ -173,20 +142,3 @@ func fetchRx(
173142
Eventually(f).ShouldNot(HaveOccurred())
174143
return rx
175144
}
176-
177-
func accepter(lis net.Listener) (net.Listener, chan bool) {
178-
c := make(chan bool, 100)
179-
go func() {
180-
var dontGC []net.Conn
181-
for {
182-
conn, err := lis.Accept()
183-
if err != nil {
184-
return
185-
}
186-
187-
dontGC = append(dontGC, conn) //nolint: staticcheck
188-
c <- true
189-
}
190-
}()
191-
return lis, c
192-
}

src/rlp-gateway/internal/ingress/log_client.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@ type LogClient struct {
1818

1919
// NewClient dials the logs provider and returns a new log client.
2020
func NewLogClient(creds credentials.TransportCredentials, logsProviderAddr string) *LogClient {
21-
conn, err := grpc.Dial(logsProviderAddr,
21+
conn, err := grpc.NewClient(logsProviderAddr,
2222
grpc.WithTransportCredentials(creds),
2323
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(10*1024*1024)),
24-
grpc.WithBlock(),
2524
)
2625
if err != nil {
2726
log.Fatalf("failed to dial logs provider: %s", err)
@@ -34,13 +33,14 @@ func NewLogClient(creds credentials.TransportCredentials, logsProviderAddr strin
3433
}
3534

3635
// Stream opens a new stream on the log client.
37-
func (c *LogClient) Stream(ctx context.Context, req *loggregator_v2.EgressBatchRequest) web.Receiver {
36+
func (c *LogClient) Stream(ctx context.Context, req *loggregator_v2.EgressBatchRequest) (web.Receiver, error) {
3837
receiver, err := c.c.BatchedReceiver(ctx, req)
3938
if err != nil {
4039
log.Printf("failed to open stream from logs provider: %s", err)
40+
return nil, err
4141
}
4242

43-
return receiver.Recv
43+
return receiver.Recv, nil
4444
}
4545

4646
func (c *LogClient) Close() error {

src/rlp-gateway/internal/web/handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type Receiver func() (*loggregator_v2.EnvelopeBatch, error)
1414
// LogsProvder defines the interface for opening streams to the
1515
// logs provider
1616
type LogsProvider interface {
17-
Stream(ctx context.Context, req *loggregator_v2.EgressBatchRequest) Receiver
17+
Stream(ctx context.Context, req *loggregator_v2.EgressBatchRequest) (Receiver, error)
1818
}
1919

2020
// Handler defines a struct for servering http endpoints

src/rlp-gateway/internal/web/json_error.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ var (
1313
errCounterNamePresentButEmpty = newJSONError(http.StatusBadRequest, "missing_counter_name", "counter.name is invalid without value")
1414
errGaugeNamePresentButEmpty = newJSONError(http.StatusBadRequest, "missing_gauge_name", "gauge.name is invalid without value")
1515
errStreamingUnsupported = newJSONError(http.StatusInternalServerError, "streaming_unsupported", "request does not support streaming")
16+
errStreamingUnavailable = newJSONError(http.StatusServiceUnavailable, "streaming_unavailable", "streaming is temporarily unavailable")
1617
errNotFound = newJSONError(http.StatusNotFound, "not_found", "resource not found")
1718
)
1819

src/rlp-gateway/internal/web/read.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,7 @@ func ReadHandler(
4747
return
4848
}
4949

50-
w.Header().Set("Content-Type", "text/event-stream")
51-
w.Header().Set("Cache-Control", "no-cache")
52-
w.Header().Set("Connection", "keep-alive")
53-
54-
flusher.Flush()
55-
56-
recv := lp.Stream(
50+
recv, err := lp.Stream(
5751
ctx,
5852
&loggregator_v2.EgressBatchRequest{
5953
ShardId: query.Get("shard_id"),
@@ -62,6 +56,16 @@ func ReadHandler(
6256
Selectors: s,
6357
},
6458
)
59+
if err != nil {
60+
errStreamingUnavailable.Write(w)
61+
return
62+
}
63+
64+
w.Header().Set("Content-Type", "text/event-stream")
65+
w.Header().Set("Cache-Control", "no-cache")
66+
w.Header().Set("Connection", "keep-alive")
67+
68+
flusher.Flush()
6569

6670
data := make(chan *loggregator_v2.EnvelopeBatch)
6771
errs := make(chan error, 1)

src/rlp-gateway/internal/web/read_test.go

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ var _ = Describe("Read", func() {
2727

2828
BeforeEach(func() {
2929
lp = newStubLogsProvider()
30-
lp._batchResponse = &loggregator_v2.EnvelopeBatch{
30+
lp.resp = &loggregator_v2.EnvelopeBatch{
3131
Batch: []*loggregator_v2.Envelope{
3232
{
3333
SourceId: "source-id-a",
@@ -150,7 +150,7 @@ var _ = Describe("Read", func() {
150150
})
151151

152152
It("contains zero values for gauge metrics", func() {
153-
lp._batchResponse = &loggregator_v2.EnvelopeBatch{
153+
lp.resp = &loggregator_v2.EnvelopeBatch{
154154
Batch: []*loggregator_v2.Envelope{
155155
{
156156
SourceId: "source-id-a",
@@ -310,9 +310,28 @@ var _ = Describe("Read", func() {
310310
}).Should(Equal(io.EOF))
311311
})
312312

313+
It("returns service unavailable when unable to stream from the logs provider", func() {
314+
lp.streamErr = errors.New("streaming unavailable")
315+
req, err := http.NewRequest(http.MethodGet, server.URL+"/v2/read?log", nil)
316+
Expect(err).ToNot(HaveOccurred())
317+
318+
req = req.WithContext(ctx)
319+
320+
resp, err := server.Client().Do(req)
321+
Expect(err).ToNot(HaveOccurred())
322+
body, err := io.ReadAll(resp.Body)
323+
Expect(err).ToNot(HaveOccurred())
324+
325+
Expect(resp.StatusCode).To(Equal(http.StatusServiceUnavailable))
326+
Expect(body).To(MatchJSON(`{
327+
"error": "streaming_unavailable",
328+
"message": "streaming is temporarily unavailable"
329+
}`))
330+
})
331+
313332
It("closes the SSE stream if the envelope stream returns any error", func() {
314-
lp._batchResponse = nil
315-
lp._errorResponse = errors.New("an error")
333+
lp.resp = nil
334+
lp.respErr = errors.New("an error")
316335

317336
req, err := http.NewRequest(http.MethodGet, server.URL+"/v2/read?log", nil)
318337
Expect(err).ToNot(HaveOccurred())
@@ -379,38 +398,43 @@ var _ = Describe("Read", func() {
379398
})
380399

381400
type stubLogsProvider struct {
382-
mu sync.Mutex
383-
_requests []*loggregator_v2.EgressBatchRequest
384-
_batchResponse *loggregator_v2.EnvelopeBatch
385-
_errorResponse error
386-
block bool
401+
mu sync.Mutex
402+
reqs []*loggregator_v2.EgressBatchRequest
403+
resp *loggregator_v2.EnvelopeBatch
404+
respErr error
405+
block bool
406+
streamErr error
387407
}
388408

389409
func newStubLogsProvider() *stubLogsProvider {
390410
return &stubLogsProvider{}
391411
}
392412

393-
func (s *stubLogsProvider) Stream(ctx context.Context, req *loggregator_v2.EgressBatchRequest) web.Receiver {
413+
func (s *stubLogsProvider) Stream(ctx context.Context, req *loggregator_v2.EgressBatchRequest) (web.Receiver, error) {
394414
s.mu.Lock()
395415
defer s.mu.Unlock()
396-
s._requests = append(s._requests, req)
416+
s.reqs = append(s.reqs, req)
417+
418+
if s.streamErr != nil {
419+
return nil, s.streamErr
420+
}
397421

398422
return func() (*loggregator_v2.EnvelopeBatch, error) {
399423
if s.block {
400424
var block chan int
401425
<-block
402426
}
403427

404-
return s._batchResponse, s._errorResponse
405-
}
428+
return s.resp, s.respErr
429+
}, nil
406430
}
407431

408432
func (s *stubLogsProvider) requests() []*loggregator_v2.EgressBatchRequest {
409433
s.mu.Lock()
410434
defer s.mu.Unlock()
411435

412-
result := make([]*loggregator_v2.EgressBatchRequest, len(s._requests))
413-
copy(result, s._requests)
436+
result := make([]*loggregator_v2.EgressBatchRequest, len(s.reqs))
437+
copy(result, s.reqs)
414438

415439
return result
416440
}

src/rlp/app/rlp_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ func setupRLPClient(egressAddr string, testCerts *testservers.TestCerts) (loggre
243243
)
244244
Expect(err).ToNot(HaveOccurred())
245245

246-
conn, err := grpc.Dial(
246+
conn, err := grpc.NewClient(
247247
egressAddr,
248248
grpc.WithTransportCredentials(ingressTLSCredentials),
249249
)

0 commit comments

Comments
 (0)