unified-storage: split resource index client (#106297)
* split resource server and index server grpc connection if defined in config
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
package unified
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
authlib "github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/options"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func TestUnifiedStorageClient(t *testing.T) {
|
||||
resourceServerAddress := ":11000"
|
||||
resourceServer := createTestGrpcServer(t, resourceServerAddress)
|
||||
defer resourceServer.s.Stop()
|
||||
indexServerAddress := ":11001"
|
||||
indexServer := createTestGrpcServer(t, indexServerAddress)
|
||||
defer indexServer.s.Stop()
|
||||
|
||||
t.Run("when storage type is unified-grpc", func(t *testing.T) {
|
||||
t.Run("should create a client that connects to the unified storage server address", func(t *testing.T) {
|
||||
resourceServer.resetCalls()
|
||||
indexServer.resetCalls()
|
||||
|
||||
client, err := newClient(
|
||||
options.StorageOptions{
|
||||
StorageType: options.StorageTypeUnifiedGrpc,
|
||||
Address: resourceServerAddress,
|
||||
},
|
||||
&setting.Cfg{},
|
||||
featuremgmt.WithFeatures(),
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
authlib.FixedAccessClient(true),
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCallAllMethods(client)
|
||||
// every method should hit resource server exactly once
|
||||
for method, count := range resourceServer.Calls {
|
||||
require.Equal(t, 1, count, "method was called more than once: "+method)
|
||||
}
|
||||
// no hits to the index server in this case
|
||||
for range indexServer.Calls {
|
||||
require.FailNow(t, "index server was called when it should have not")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("should connect to a separate index server if defined in the config", func(t *testing.T) {
|
||||
resourceServer.resetCalls()
|
||||
indexServer.resetCalls()
|
||||
|
||||
client, err := newClient(
|
||||
options.StorageOptions{
|
||||
StorageType: options.StorageTypeUnifiedGrpc,
|
||||
Address: resourceServerAddress,
|
||||
IndexServerAddress: indexServerAddress,
|
||||
},
|
||||
&setting.Cfg{},
|
||||
featuremgmt.WithFeatures(),
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
authlib.FixedAccessClient(true),
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCallAllMethods(client)
|
||||
// only resource store methods in this case
|
||||
for method, count := range resourceServer.Calls {
|
||||
require.Equal(t, 1, count, "method was called more than once: "+method)
|
||||
require.True(t, strings.Contains(method, "resource.ResourceStore"))
|
||||
}
|
||||
// index server methods should be called here
|
||||
for method, count := range indexServer.Calls {
|
||||
require.Equal(t, 1, count, "method was called more than once: "+method)
|
||||
require.True(t, strings.Contains(method, "resource.ResourceIndex") || strings.Contains(method, "resource.ManagedObjectIndex"))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func testCallAllMethods(client resource.ResourceClient) {
|
||||
_, _ = client.Read(identity.WithServiceIdentityContext(context.Background(), 1), &resourcepb.ReadRequest{})
|
||||
_, _ = client.Create(identity.WithServiceIdentityContext(context.Background(), 1), &resourcepb.CreateRequest{})
|
||||
_, _ = client.Delete(identity.WithServiceIdentityContext(context.Background(), 1), &resourcepb.DeleteRequest{})
|
||||
_, _ = client.Update(identity.WithServiceIdentityContext(context.Background(), 1), &resourcepb.UpdateRequest{})
|
||||
_, _ = client.List(identity.WithServiceIdentityContext(context.Background(), 1), &resourcepb.ListRequest{})
|
||||
_, _ = client.GetStats(identity.WithServiceIdentityContext(context.Background(), 1), &resourcepb.ResourceStatsRequest{})
|
||||
_, _ = client.Search(identity.WithServiceIdentityContext(context.Background(), 1), &resourcepb.ResourceSearchRequest{})
|
||||
_, _ = client.CountManagedObjects(identity.WithServiceIdentityContext(context.Background(), 1), &resourcepb.CountManagedObjectsRequest{})
|
||||
_, _ = client.ListManagedObjects(identity.WithServiceIdentityContext(context.Background(), 1), &resourcepb.ListManagedObjectsRequest{})
|
||||
}
|
||||
|
||||
func createTestGrpcServer(t *testing.T, address string) *testServer {
|
||||
listener, err := net.Listen("tcp", address)
|
||||
require.NoError(t, err, "failed to listen")
|
||||
|
||||
testServer := newTestServer()
|
||||
s := grpc.NewServer(
|
||||
grpc.UnknownServiceHandler(testServer.handler),
|
||||
)
|
||||
|
||||
go func() {
|
||||
_ = s.Serve(listener)
|
||||
}()
|
||||
|
||||
testServer.s = s
|
||||
|
||||
return testServer
|
||||
}
|
||||
|
||||
type testServer struct {
|
||||
resource.ResourceServer
|
||||
Calls map[string]int
|
||||
s *grpc.Server
|
||||
}
|
||||
|
||||
func newTestServer() *testServer {
|
||||
return &testServer{
|
||||
Calls: make(map[string]int),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *testServer) resetCalls() {
|
||||
s.Calls = make(map[string]int)
|
||||
}
|
||||
|
||||
func (s *testServer) handler(srv interface{}, serverStream grpc.ServerStream) error {
|
||||
fullMethodName, ok := grpc.MethodFromServerStream(serverStream)
|
||||
if ok {
|
||||
s.Calls[fullMethodName]++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user