github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/execinfrapb/testutils.go (about)

     1  // Copyright 2019 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package execinfrapb
    12  
    13  import (
    14  	"context"
    15  	"net"
    16  	"time"
    17  
    18  	"github.com/cockroachdb/cockroach/pkg/base"
    19  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    20  	"github.com/cockroachdb/cockroach/pkg/rpc"
    21  	"github.com/cockroachdb/cockroach/pkg/settings/cluster"
    22  	"github.com/cockroachdb/cockroach/pkg/util"
    23  	"github.com/cockroachdb/cockroach/pkg/util/hlc"
    24  	"github.com/cockroachdb/cockroach/pkg/util/log"
    25  	"github.com/cockroachdb/cockroach/pkg/util/netutil"
    26  	"github.com/cockroachdb/cockroach/pkg/util/stop"
    27  	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
    28  	"github.com/cockroachdb/cockroach/pkg/util/tracing"
    29  	"github.com/cockroachdb/cockroach/pkg/util/uuid"
    30  	"google.golang.org/grpc"
    31  )
    32  
    33  // CallbackMetadataSource is a utility struct that implements the MetadataSource
    34  // interface by calling a provided callback.
    35  type CallbackMetadataSource struct {
    36  	DrainMetaCb func(context.Context) []ProducerMetadata
    37  }
    38  
    39  // DrainMeta is part of the MetadataSource interface.
    40  func (s CallbackMetadataSource) DrainMeta(ctx context.Context) []ProducerMetadata {
    41  	return s.DrainMetaCb(ctx)
    42  }
    43  
    44  func newInsecureRPCContext(stopper *stop.Stopper) *rpc.Context {
    45  	return rpc.NewContext(
    46  		log.AmbientContext{Tracer: tracing.NewTracer()},
    47  		&base.Config{Insecure: true},
    48  		hlc.NewClock(hlc.UnixNano, time.Nanosecond),
    49  		stopper,
    50  		cluster.MakeTestingClusterSettings(),
    51  	)
    52  }
    53  
    54  // StartMockDistSQLServer starts a MockDistSQLServer and returns the address on
    55  // which it's listening.
    56  func StartMockDistSQLServer(
    57  	clock *hlc.Clock, stopper *stop.Stopper, nodeID roachpb.NodeID,
    58  ) (uuid.UUID, *MockDistSQLServer, net.Addr, error) {
    59  	rpcContext := newInsecureRPCContext(stopper)
    60  	rpcContext.NodeID.Set(context.TODO(), nodeID)
    61  	server := rpc.NewServer(rpcContext)
    62  	mock := newMockDistSQLServer()
    63  	RegisterDistSQLServer(server, mock)
    64  	ln, err := netutil.ListenAndServeGRPC(stopper, server, util.IsolatedTestAddr)
    65  	if err != nil {
    66  		return uuid.Nil, nil, nil, err
    67  	}
    68  	return rpcContext.ClusterID.Get(), mock, ln.Addr(), nil
    69  }
    70  
    71  // MockDistSQLServer implements the DistSQLServer (gRPC) interface and allows
    72  // clients to control the inbound streams.
    73  type MockDistSQLServer struct {
    74  	InboundStreams   chan InboundStreamNotification
    75  	RunSyncFlowCalls chan RunSyncFlowCall
    76  }
    77  
    78  // InboundStreamNotification is the MockDistSQLServer's way to tell its clients
    79  // that a new gRPC call has arrived and thus a stream has arrived. The rpc
    80  // handler is blocked until Donec is signaled.
    81  type InboundStreamNotification struct {
    82  	Stream DistSQL_FlowStreamServer
    83  	Donec  chan<- error
    84  }
    85  
    86  // RunSyncFlowCall is the MockDistSQLServer's way to tell its clients that a
    87  // RunSyncFlowCall has arrived. The rpc handler is blocked until Donec is
    88  // signaled.
    89  type RunSyncFlowCall struct {
    90  	Stream DistSQL_RunSyncFlowServer
    91  	Donec  chan<- error
    92  }
    93  
    94  // MockDistSQLServer implements the DistSQLServer interface.
    95  var _ DistSQLServer = &MockDistSQLServer{}
    96  
    97  func newMockDistSQLServer() *MockDistSQLServer {
    98  	return &MockDistSQLServer{
    99  		InboundStreams:   make(chan InboundStreamNotification),
   100  		RunSyncFlowCalls: make(chan RunSyncFlowCall),
   101  	}
   102  }
   103  
   104  // RunSyncFlow is part of the DistSQLServer interface.
   105  func (ds *MockDistSQLServer) RunSyncFlow(stream DistSQL_RunSyncFlowServer) error {
   106  	donec := make(chan error)
   107  	ds.RunSyncFlowCalls <- RunSyncFlowCall{Stream: stream, Donec: donec}
   108  	return <-donec
   109  }
   110  
   111  // SetupFlow is part of the DistSQLServer interface.
   112  func (ds *MockDistSQLServer) SetupFlow(
   113  	_ context.Context, req *SetupFlowRequest,
   114  ) (*SimpleResponse, error) {
   115  	return nil, nil
   116  }
   117  
   118  // FlowStream is part of the DistSQLServer interface.
   119  func (ds *MockDistSQLServer) FlowStream(stream DistSQL_FlowStreamServer) error {
   120  	donec := make(chan error)
   121  	ds.InboundStreams <- InboundStreamNotification{Stream: stream, Donec: donec}
   122  	return <-donec
   123  }
   124  
   125  // MockDialer is a mocked implementation of the Outbox's `Dialer` interface.
   126  // Used to create a connection with a client stream.
   127  type MockDialer struct {
   128  	// Addr is assumed to be obtained from execinfrapb.StartMockDistSQLServer.
   129  	Addr net.Addr
   130  	mu   struct {
   131  		syncutil.Mutex
   132  		conn *grpc.ClientConn
   133  	}
   134  }
   135  
   136  // Dial establishes a grpc connection once.
   137  func (d *MockDialer) Dial(
   138  	context.Context, roachpb.NodeID, rpc.ConnectionClass,
   139  ) (*grpc.ClientConn, error) {
   140  	d.mu.Lock()
   141  	defer d.mu.Unlock()
   142  	if d.mu.conn != nil {
   143  		return d.mu.conn, nil
   144  	}
   145  	var err error
   146  	d.mu.conn, err = grpc.Dial(d.Addr.String(), grpc.WithInsecure(), grpc.WithBlock())
   147  	return d.mu.conn, err
   148  }
   149  
   150  // Close must be called after the test is done.
   151  func (d *MockDialer) Close() {
   152  	if err := d.mu.conn.Close(); err != nil {
   153  		panic(err)
   154  	}
   155  }