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 }