github.com/grafana/pyroscope@v1.18.0/pkg/metastore/test/create.go (about)

     1  package test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/go-kit/log"
    10  	"github.com/grafana/dskit/services"
    11  	"github.com/hashicorp/raft"
    12  	"github.com/prometheus/client_golang/prometheus"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/mock"
    15  	"github.com/stretchr/testify/require"
    16  	"github.com/thanos-io/objstore"
    17  	"google.golang.org/grpc"
    18  
    19  	metastorev1 "github.com/grafana/pyroscope/api/gen/proto/go/metastore/v1"
    20  	"github.com/grafana/pyroscope/pkg/metastore"
    21  	metastoreclient "github.com/grafana/pyroscope/pkg/metastore/client"
    22  	"github.com/grafana/pyroscope/pkg/metastore/discovery"
    23  	"github.com/grafana/pyroscope/pkg/metastore/raftnode/raftnodepb"
    24  	placement "github.com/grafana/pyroscope/pkg/segmentwriter/client/distributor/placement/adaptiveplacement"
    25  	"github.com/grafana/pyroscope/pkg/test"
    26  	"github.com/grafana/pyroscope/pkg/test/mocks/mockdiscovery"
    27  	"github.com/grafana/pyroscope/pkg/util/health"
    28  	"github.com/grafana/pyroscope/pkg/validation"
    29  )
    30  
    31  func NewMetastoreSet(t *testing.T, cfg *metastore.Config, n int, bucket objstore.Bucket) MetastoreSet {
    32  	l := test.NewTestingLogger(t)
    33  
    34  	grpcAddresses := make([]string, n)
    35  	raftAddresses := make([]string, n)
    36  	raftIds := make([]string, n)
    37  	bootstrapPeers := make([]string, n)
    38  	for i := 0; i < n; i++ {
    39  		grpcAddresses[i] = fmt.Sprintf("localhost:%d", 10500+i)
    40  		raftAddresses[i] = fmt.Sprintf("localhost:%d", 10500+2*i)
    41  		raftIds[i] = fmt.Sprintf("node-%d", i)
    42  		bootstrapPeers[i] = fmt.Sprintf("%s/%s", raftAddresses[i], raftIds[i])
    43  	}
    44  	l.Log("grpcAddresses", fmt.Sprintf("%+v", grpcAddresses), "raftAddresses", fmt.Sprintf("%+v", raftAddresses))
    45  
    46  	configs := make([]metastore.Config, n)
    47  	for i := 0; i < n; i++ {
    48  		icfg := *cfg
    49  		icfg.MinReadyDuration = 0
    50  		icfg.Address = grpcAddresses[i]
    51  		icfg.FSM.DataDir = t.TempDir()
    52  		icfg.Raft.ServerID = raftIds[i]
    53  		icfg.Raft.Dir = t.TempDir()
    54  		icfg.Raft.AdvertiseAddress = raftAddresses[i]
    55  		icfg.Raft.BindAddress = raftAddresses[i]
    56  		icfg.Raft.BootstrapPeers = bootstrapPeers
    57  		icfg.Raft.BootstrapExpectPeers = n
    58  		configs[i] = icfg
    59  	}
    60  
    61  	servers := make([]discovery.Server, n)
    62  	for i := 0; i < n; i++ {
    63  		srv := discovery.Server{
    64  			Raft: raft.Server{
    65  				ID:      raft.ServerID(raftIds[i]),
    66  				Address: raft.ServerAddress(raftAddresses[i]),
    67  			},
    68  			ResolvedAddress: grpcAddresses[i],
    69  		}
    70  		servers[i] = srv
    71  	}
    72  
    73  	listeners, dialOpt := test.CreateInMemoryListeners(grpcAddresses)
    74  	d := MockStaticDiscovery(t, servers)
    75  	client := metastoreclient.New(l, cfg.GRPCClientConfig, d, dialOpt)
    76  	err := client.Service().StartAsync(context.Background())
    77  	require.NoError(t, err)
    78  
    79  	res := MetastoreSet{
    80  		t: t,
    81  	}
    82  
    83  	for i := 0; i < n; i++ {
    84  		options, err := cfg.GRPCClientConfig.DialOption(nil, nil, nil)
    85  		require.NoError(t, err)
    86  		options = append(options, dialOpt)
    87  		cc, err := grpc.Dial(grpcAddresses[i], options...)
    88  		require.NoError(t, err)
    89  		logger := log.With(l, "idx", bootstrapPeers[i])
    90  		server := grpc.NewServer()
    91  		registry := prometheus.NewRegistry()
    92  		placementManager := placement.NewManager(
    93  			logger,
    94  			registry,
    95  			placement.DefaultConfig(),
    96  			validation.MockDefaultOverrides(),
    97  			placement.NewStore(bucket),
    98  		)
    99  		m, err := metastore.New(configs[i], validation.MockDefaultOverrides(), logger, registry, health.NoOpService, client, bucket, placementManager)
   100  		require.NoError(t, err)
   101  		m.Register(server)
   102  
   103  		lis := listeners[grpcAddresses[i]]
   104  		go func() {
   105  			err := server.Serve(lis)
   106  			assert.NoError(t, err)
   107  		}()
   108  		res.Instances = append(res.Instances, MetastoreInstance{
   109  			Metastore:  m,
   110  			Connection: cc,
   111  			Server:     server,
   112  
   113  			IndexServiceClient:         metastorev1.NewIndexServiceClient(cc),
   114  			CompactionServiceClient:    metastorev1.NewCompactionServiceClient(cc),
   115  			MetadataQueryServiceClient: metastorev1.NewMetadataQueryServiceClient(cc),
   116  			TenantServiceClient:        metastorev1.NewTenantServiceClient(cc),
   117  			RaftNodeServiceClient:      raftnodepb.NewRaftNodeServiceClient(cc),
   118  		})
   119  		service := m.Service()
   120  		ctx := context.Background()
   121  		require.NoError(t, service.StartAsync(ctx))
   122  		require.NoError(t, service.AwaitRunning(ctx))
   123  		logger.Log("msg", "service started")
   124  	}
   125  
   126  	require.Eventually(t, func() bool {
   127  		for i := 0; i < n; i++ {
   128  			if res.Instances[i].Metastore.Service().State() != services.Running {
   129  				return false
   130  			}
   131  			if res.Instances[i].Metastore.CheckReady(context.Background()) != nil {
   132  				return false
   133  			}
   134  		}
   135  		return true
   136  	}, 10*time.Second, 100*time.Millisecond)
   137  
   138  	res.Client = client
   139  
   140  	return res
   141  }
   142  
   143  func MockStaticDiscovery(t *testing.T, servers []discovery.Server) *mockdiscovery.MockDiscovery {
   144  	d := mockdiscovery.NewMockDiscovery(t)
   145  	d.On("Subscribe", mock.Anything).Run(func(args mock.Arguments) {
   146  		upd := args.Get(0).(discovery.Updates)
   147  		upd.Servers(servers)
   148  	})
   149  	d.On("Rediscover", mock.Anything).Return()
   150  	d.On("Close").Return(nil)
   151  	return d
   152  }
   153  
   154  type MetastoreInstance struct {
   155  	Metastore  *metastore.Metastore
   156  	Server     *grpc.Server
   157  	Connection *grpc.ClientConn
   158  
   159  	metastorev1.IndexServiceClient
   160  	metastorev1.CompactionServiceClient
   161  	metastorev1.MetadataQueryServiceClient
   162  	metastorev1.TenantServiceClient
   163  	raftnodepb.RaftNodeServiceClient
   164  }
   165  
   166  type MetastoreSet struct {
   167  	t         *testing.T
   168  	Instances []MetastoreInstance
   169  	Client    *metastoreclient.Client
   170  }
   171  
   172  func (m *MetastoreSet) Close() {
   173  	for _, i := range m.Instances {
   174  		i.Metastore.Service().StopAsync()
   175  		err := i.Metastore.Service().AwaitTerminated(context.Background())
   176  		require.NoError(m.t, err)
   177  		i.Connection.Close()
   178  		i.Server.Stop()
   179  	}
   180  	m.Client.Service().StopAsync()
   181  	err := m.Client.Service().AwaitTerminated(context.Background())
   182  	require.NoError(m.t, err)
   183  }