mirror of
https://github.com/status-im/consul.git
synced 2025-01-12 23:05:28 +00:00
73b9b407ba
Registering gRPC balancers is thread-unsafe because they are stored in a global map variable that is accessed without holding a lock. Therefore, it's expected that balancers are registered _once_ at the beginning of your program (e.g. in a package `init` function) and certainly not after you've started dialing connections, etc. > NOTE: this function must only be called during initialization time > (i.e. in an init() function), and is not thread-safe. While this is fine for us in production, it's challenging for tests that spin up multiple agents in-memory. We currently register a balancer per- agent which holds agent-specific state that cannot safely be shared. This commit introduces our own registry that _is_ thread-safe, and implements the Builder interface such that we can call gRPC's `Register` method once, on start-up. It uses the same pattern as our resolver registry where we use the dial target's host (aka "authority"), which is unique per-agent, to determine which builder to use.
66 lines
1.9 KiB
Go
66 lines
1.9 KiB
Go
package internal
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/hashicorp/consul/sdk/testutil"
|
|
"github.com/hashicorp/consul/types"
|
|
|
|
"github.com/hashicorp/go-hclog"
|
|
"github.com/stretchr/testify/require"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/grpc/status"
|
|
|
|
"github.com/hashicorp/consul/agent/grpc-internal/balancer"
|
|
"github.com/hashicorp/consul/agent/grpc-internal/resolver"
|
|
"github.com/hashicorp/consul/agent/grpc-middleware/testutil/testservice"
|
|
)
|
|
|
|
func TestHandler_PanicRecoveryInterceptor(t *testing.T) {
|
|
// Prepare a logger with output to a buffer
|
|
// so we can check what it writes.
|
|
var buf bytes.Buffer
|
|
|
|
logger := hclog.New(&hclog.LoggerOptions{
|
|
Output: &buf,
|
|
})
|
|
|
|
res := resolver.NewServerResolverBuilder(newConfig(t))
|
|
bb := balancer.NewBuilder(res.Authority(), testutil.Logger(t))
|
|
registerWithGRPC(t, res, bb)
|
|
|
|
srv := newPanicTestServer(t, logger, "server-1", "dc1", nil)
|
|
res.AddServer(types.AreaWAN, srv.Metadata())
|
|
t.Cleanup(srv.shutdown)
|
|
|
|
pool := NewClientConnPool(ClientConnPoolConfig{
|
|
Servers: res,
|
|
UseTLSForDC: useTLSForDcAlwaysTrue,
|
|
DialingFromServer: true,
|
|
DialingFromDatacenter: "dc1",
|
|
})
|
|
|
|
conn, err := pool.ClientConn("dc1")
|
|
require.NoError(t, err)
|
|
client := testservice.NewSimpleClient(conn)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
|
t.Cleanup(cancel)
|
|
|
|
resp, err := client.Something(ctx, &testservice.Req{})
|
|
expectedErr := status.Errorf(codes.Internal, "grpc: panic serving request")
|
|
require.Equal(t, expectedErr.Error(), err.Error())
|
|
require.Nil(t, resp)
|
|
|
|
// Read the log
|
|
strLog := buf.String()
|
|
// Checking the entire stack trace is not possible, let's
|
|
// make sure that it contains a couple of expected strings.
|
|
require.Contains(t, strLog, `[ERROR] panic serving grpc request: panic="panic from Something`)
|
|
require.Contains(t, strLog, `github.com/hashicorp/consul/agent/grpc-middleware/testutil/testservice.(*SimplePanic).Something`)
|
|
|
|
}
|