github.com/grafana/pyroscope@v1.18.0/pkg/metastore/client/server_mock_test.go (about)

     1  package metastoreclient
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sync"
     7  	"testing"
     8  
     9  	"github.com/go-kit/log"
    10  	"github.com/hashicorp/raft"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/mock"
    13  	"google.golang.org/grpc"
    14  	"google.golang.org/grpc/codes"
    15  	"google.golang.org/grpc/status"
    16  	"google.golang.org/grpc/test/bufconn"
    17  
    18  	metastorev1 "github.com/grafana/pyroscope/api/gen/proto/go/metastore/v1"
    19  	"github.com/grafana/pyroscope/pkg/metastore/discovery"
    20  	"github.com/grafana/pyroscope/pkg/metastore/raftnode/raftnodepb"
    21  	"github.com/grafana/pyroscope/pkg/test"
    22  	"github.com/grafana/pyroscope/pkg/test/mocks/mockmetastorev1"
    23  	"github.com/grafana/pyroscope/pkg/test/mocks/mockraftnodepb"
    24  )
    25  
    26  type mockServer struct {
    27  	metastore *mockmetastorev1.MockIndexServiceServer
    28  	compactor *mockmetastorev1.MockCompactionServiceServer
    29  	metadata  *mockmetastorev1.MockMetadataQueryServiceServer
    30  	tenant    *mockmetastorev1.MockTenantServiceServer
    31  	raftNode  *mockraftnodepb.MockRaftNodeServiceServer
    32  
    33  	metastorev1.UnsafeIndexServiceServer
    34  	metastorev1.UnsafeCompactionServiceServer
    35  	metastorev1.UnsafeMetadataQueryServiceServer
    36  	metastorev1.UnsafeTenantServiceServer
    37  	raftnodepb.UnsafeRaftNodeServiceServer
    38  
    39  	srv     *grpc.Server
    40  	id      raft.ServerID
    41  	index   int
    42  	address string
    43  }
    44  
    45  func (m *mockServer) GetTenants(ctx context.Context, request *metastorev1.GetTenantsRequest) (*metastorev1.GetTenantsResponse, error) {
    46  	return m.tenant.GetTenants(ctx, request)
    47  }
    48  
    49  func (m *mockServer) GetTenant(ctx context.Context, request *metastorev1.GetTenantRequest) (*metastorev1.GetTenantResponse, error) {
    50  	return m.tenant.GetTenant(ctx, request)
    51  }
    52  
    53  func (m *mockServer) DeleteTenant(ctx context.Context, request *metastorev1.DeleteTenantRequest) (*metastorev1.DeleteTenantResponse, error) {
    54  	return m.tenant.DeleteTenant(ctx, request)
    55  }
    56  
    57  func (m *mockServer) PollCompactionJobs(ctx context.Context, request *metastorev1.PollCompactionJobsRequest) (*metastorev1.PollCompactionJobsResponse, error) {
    58  	return m.compactor.PollCompactionJobs(ctx, request)
    59  }
    60  
    61  func (m *mockServer) AddBlock(ctx context.Context, request *metastorev1.AddBlockRequest) (*metastorev1.AddBlockResponse, error) {
    62  	return m.metastore.AddBlock(ctx, request)
    63  }
    64  
    65  func (m *mockServer) GetBlockMetadata(ctx context.Context, request *metastorev1.GetBlockMetadataRequest) (*metastorev1.GetBlockMetadataResponse, error) {
    66  	return m.metastore.GetBlockMetadata(ctx, request)
    67  }
    68  
    69  func (m *mockServer) QueryMetadata(ctx context.Context, request *metastorev1.QueryMetadataRequest) (*metastorev1.QueryMetadataResponse, error) {
    70  	return m.metadata.QueryMetadata(ctx, request)
    71  }
    72  
    73  func (m *mockServer) QueryMetadataLabels(ctx context.Context, request *metastorev1.QueryMetadataLabelsRequest) (*metastorev1.QueryMetadataLabelsResponse, error) {
    74  	return m.metadata.QueryMetadataLabels(ctx, request)
    75  }
    76  
    77  func (m *mockServer) ReadIndex(ctx context.Context, request *raftnodepb.ReadIndexRequest) (*raftnodepb.ReadIndexResponse, error) {
    78  	return m.raftNode.ReadIndex(ctx, request)
    79  }
    80  
    81  func (m *mockServer) NodeInfo(ctx context.Context, request *raftnodepb.NodeInfoRequest) (*raftnodepb.NodeInfoResponse, error) {
    82  	return m.raftNode.NodeInfo(ctx, request)
    83  }
    84  
    85  func (m *mockServer) RemoveNode(ctx context.Context, request *raftnodepb.RemoveNodeRequest) (*raftnodepb.RemoveNodeResponse, error) {
    86  	return m.raftNode.RemoveNode(ctx, request)
    87  }
    88  
    89  func (m *mockServer) AddNode(ctx context.Context, request *raftnodepb.AddNodeRequest) (*raftnodepb.AddNodeResponse, error) {
    90  	return m.raftNode.AddNode(ctx, request)
    91  }
    92  
    93  func (m *mockServer) DemoteLeader(ctx context.Context, request *raftnodepb.DemoteLeaderRequest) (*raftnodepb.DemoteLeaderResponse, error) {
    94  	return m.raftNode.DemoteLeader(ctx, request)
    95  }
    96  
    97  func (m *mockServer) PromoteToLeader(ctx context.Context, request *raftnodepb.PromoteToLeaderRequest) (*raftnodepb.PromoteToLeaderResponse, error) {
    98  	return m.raftNode.PromoteToLeader(ctx, request)
    99  }
   100  
   101  func createServers(ports []int) []discovery.Server {
   102  	var servers []discovery.Server
   103  	for i, p := range ports {
   104  		servers = append(servers, discovery.Server{
   105  			Raft: raft.Server{
   106  				ID:      testServerId(i),
   107  				Address: raft.ServerAddress(fmt.Sprintf("server-%d", i)),
   108  			},
   109  			ResolvedAddress: fmt.Sprintf("127.0.0.1:%d", p),
   110  		})
   111  	}
   112  	return servers
   113  }
   114  
   115  func testServerId(i int) raft.ServerID {
   116  	return raft.ServerID(fmt.Sprintf("id-%d", i))
   117  }
   118  
   119  var _ metastorev1.IndexServiceServer = (*mockServer)(nil)
   120  var _ metastorev1.CompactionServiceServer = (*mockServer)(nil)
   121  
   122  type mockServers struct {
   123  	t       *testing.T
   124  	l       log.Logger
   125  	servers []*mockServer
   126  }
   127  
   128  func (m *mockServers) Close() {
   129  	if m == nil {
   130  		return
   131  	}
   132  	for _, s := range m.servers {
   133  		s.srv.Stop()
   134  	}
   135  }
   136  
   137  func (m *mockServers) InitWrongLeader() func() {
   138  	type wrongLeaderState struct {
   139  		m            sync.Mutex
   140  		callNo       int
   141  		leaderIndex  int
   142  		leaderCalled int
   143  	}
   144  	s := new(wrongLeaderState)
   145  	s.leaderIndex = -1
   146  
   147  	nServers := len(m.servers)
   148  	for _, srv := range m.servers {
   149  		srv := srv
   150  		errf := func() error {
   151  			s.m.Lock()
   152  			defer s.m.Unlock()
   153  			s.callNo++
   154  			m.l.Log("called", srv.index, "leader", s.leaderIndex, "callno", s.callNo)
   155  			if s.callNo == 1 {
   156  				s.leaderIndex = (srv.index + 1) % nServers
   157  				s, err := status.New(codes.Unavailable, fmt.Sprintf("test error not leader, leader is %s", testServerId(s.leaderIndex))).
   158  					WithDetails(&raftnodepb.RaftNode{
   159  						Id: string(testServerId(s.leaderIndex)),
   160  					})
   161  				assert.NoError(m.t, err)
   162  				return s.Err()
   163  			}
   164  			if s.callNo == 2 {
   165  				if srv.index != s.leaderIndex {
   166  					m.t.Errorf("expected leader %d to be called, but %d called", s.leaderIndex, srv.index)
   167  				}
   168  				s.leaderCalled++
   169  				return nil
   170  			}
   171  			m.t.Errorf("unexpected call")
   172  			return fmt.Errorf("unexpected call")
   173  		}
   174  		srv.metastore.On("AddBlock", mock.Anything, mock.Anything).Maybe().Return(func(context.Context, *metastorev1.AddBlockRequest) (*metastorev1.AddBlockResponse, error) {
   175  			return errOrT(&metastorev1.AddBlockResponse{}, errf)
   176  		})
   177  		srv.metadata.On("QueryMetadata", mock.Anything, mock.Anything).Maybe().Return(func(context.Context, *metastorev1.QueryMetadataRequest) (*metastorev1.QueryMetadataResponse, error) {
   178  			return errOrT(&metastorev1.QueryMetadataResponse{}, errf)
   179  		})
   180  		srv.raftNode.On("ReadIndex", mock.Anything, mock.Anything).Maybe().Return(func(context.Context, *raftnodepb.ReadIndexRequest) (*raftnodepb.ReadIndexResponse, error) {
   181  			return errOrT(&raftnodepb.ReadIndexResponse{}, errf)
   182  		})
   183  		srv.compactor.On("PollCompactionJobs", mock.Anything, mock.Anything).Maybe().Return(func(context.Context, *metastorev1.PollCompactionJobsRequest) (*metastorev1.PollCompactionJobsResponse, error) {
   184  			return errOrT(&metastorev1.PollCompactionJobsResponse{}, errf)
   185  		})
   186  		srv.tenant.On("GetTenant", mock.Anything, mock.Anything).Maybe().Return(func(context.Context, *metastorev1.GetTenantRequest) (*metastorev1.GetTenantResponse, error) {
   187  			return errOrT(&metastorev1.GetTenantResponse{}, errf)
   188  		})
   189  	}
   190  	return func() {
   191  		s.m.Lock()
   192  		assert.Equal(m.t, 2, s.callNo)
   193  		assert.Equal(m.t, 1, s.leaderCalled)
   194  		s.m.Unlock()
   195  	}
   196  }
   197  
   198  func errOrT[T any](t *T, f func() error) (*T, error) {
   199  	if err := f(); err != nil {
   200  		return nil, err
   201  	}
   202  	return t, nil
   203  }
   204  
   205  // Returns the grpc.DialOptions needed for a client connection to the created mock servers.
   206  func createMockServers(t *testing.T, l log.Logger, dServers []discovery.Server) (*mockServers, []grpc.DialOption) {
   207  	var servers []*mockServer
   208  	listeners, dialOpt := createInMemoryListeners(dServers)
   209  	for idx, dserv := range dServers {
   210  		s := newMockServer(t)
   211  		s.index = idx
   212  		s.id = testServerId(idx)
   213  		s.address = dserv.ResolvedAddress
   214  		go func() {
   215  			if err := s.srv.Serve(listeners[s.address]); err != nil {
   216  				assert.NoError(t, err)
   217  			}
   218  		}()
   219  		servers = append(servers, s)
   220  	}
   221  
   222  	ms := &mockServers{
   223  		servers: servers,
   224  		t:       t,
   225  		l:       l,
   226  	}
   227  	return ms, []grpc.DialOption{dialOpt}
   228  }
   229  
   230  func createInMemoryListeners(dServers []discovery.Server) (map[string]*bufconn.Listener, grpc.DialOption) {
   231  	addrs := make([]string, 0, len(dServers))
   232  	for _, ds := range dServers {
   233  		addrs = append(addrs, ds.ResolvedAddress)
   234  	}
   235  	return test.CreateInMemoryListeners(addrs)
   236  }
   237  
   238  func newMockServer(t *testing.T) *mockServer {
   239  	res := &mockServer{
   240  		srv:       grpc.NewServer(),
   241  		metastore: mockmetastorev1.NewMockIndexServiceServer(t),
   242  		compactor: mockmetastorev1.NewMockCompactionServiceServer(t),
   243  		metadata:  mockmetastorev1.NewMockMetadataQueryServiceServer(t),
   244  		tenant:    mockmetastorev1.NewMockTenantServiceServer(t),
   245  		raftNode:  mockraftnodepb.NewMockRaftNodeServiceServer(t),
   246  	}
   247  	metastorev1.RegisterIndexServiceServer(res.srv, res)
   248  	metastorev1.RegisterCompactionServiceServer(res.srv, res)
   249  	metastorev1.RegisterMetadataQueryServiceServer(res.srv, res)
   250  	metastorev1.RegisterTenantServiceServer(res.srv, res)
   251  	raftnodepb.RegisterRaftNodeServiceServer(res.srv, res)
   252  	return res
   253  }