consul/agent/grpc-external/services/configentry/server_test.go

237 lines
6.3 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package configentry
import (
"context"
"fmt"
"testing"
"time"
"github.com/armon/go-metrics"
"github.com/hashicorp/consul/acl"
"github.com/hashicorp/consul/acl/resolver"
"github.com/hashicorp/consul/agent/grpc-external/testutils"
"github.com/hashicorp/consul/agent/structs"
"github.com/hashicorp/consul/proto/private/pbconfigentry"
"github.com/hashicorp/go-hclog"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
)
type MockBackend struct {
mock.Mock
authorizer acl.Authorizer
}
func (m *MockBackend) ResolveTokenAndDefaultMeta(string, *acl.EnterpriseMeta, *acl.AuthorizerContext) (resolver.Result, error) {
return resolver.Result{Authorizer: m.authorizer}, nil
}
func (m *MockBackend) EnterpriseCheckPartitions(partition string) error {
called := m.Called(partition)
ret := called.Get(0)
if ret == nil {
return nil
} else {
return ret.(error)
}
}
func TestGetResolvedExportedServices_ACL_Deny(t *testing.T) {
authorizer := acl.MockAuthorizer{}
authorizer.On("MeshRead", mock.Anything).Return(acl.Deny)
backend := &MockBackend{authorizer: &authorizer}
backend.On("EnterpriseCheckPartitions", mock.Anything).Return(nil)
fakeFSM := testutils.NewFakeBlockingFSM(t)
c := Config{
Backend: backend,
Logger: hclog.New(nil),
ForwardRPC: doForwardRPC,
FSMServer: fakeFSM,
}
server := NewServer(c)
_, err := server.GetResolvedExportedServices(context.Background(), &pbconfigentry.GetResolvedExportedServicesRequest{})
require.Error(t, err)
}
func TestGetResolvedExportedServices_AC_Allow(t *testing.T) {
authorizer := acl.MockAuthorizer{}
authorizer.On("MeshRead", mock.Anything).Return(acl.Allow)
backend := &MockBackend{authorizer: &authorizer}
backend.On("EnterpriseCheckPartitions", mock.Anything).Return(nil)
fakeFSM := testutils.NewFakeBlockingFSM(t)
c := Config{
Backend: backend,
Logger: hclog.New(nil),
ForwardRPC: doForwardRPC,
FSMServer: fakeFSM,
}
server := NewServer(c)
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &testutils.MockServerTransportStream{})
_, err := server.GetResolvedExportedServices(ctx, &pbconfigentry.GetResolvedExportedServicesRequest{})
require.NoError(t, err)
}
func TestGetResolvedExportedServices_PartitionCheck(t *testing.T) {
authorizer := acl.MockAuthorizer{}
authorizer.On("MeshRead", mock.Anything).Return(acl.Allow)
backend := &MockBackend{authorizer: &authorizer}
backend.On("EnterpriseCheckPartitions", mock.Anything).Return(fmt.Errorf("partition not supported"))
fakeFSM := testutils.NewFakeBlockingFSM(t)
c := Config{
Backend: backend,
Logger: hclog.New(nil),
ForwardRPC: doForwardRPC,
FSMServer: fakeFSM,
}
server := NewServer(c)
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &testutils.MockServerTransportStream{})
resp, err := server.GetResolvedExportedServices(ctx, &pbconfigentry.GetResolvedExportedServicesRequest{})
require.EqualError(t, err, "rpc error: code = InvalidArgument desc = partition not supported")
require.Nil(t, resp)
}
func TestGetResolvedExportedServices_Index(t *testing.T) {
authorizer := acl.MockAuthorizer{}
authorizer.On("MeshRead", mock.Anything).Return(acl.Allow)
backend := &MockBackend{authorizer: &authorizer}
backend.On("EnterpriseCheckPartitions", mock.Anything).Return(nil)
fakeFSM := testutils.NewFakeBlockingFSM(t)
c := Config{
Backend: backend,
Logger: hclog.New(nil),
ForwardRPC: doForwardRPC,
FSMServer: fakeFSM,
}
server := NewServer(c)
// Add config entry
entry := &structs.ExportedServicesConfigEntry{
Name: "default",
Services: []structs.ExportedService{
{
Name: "db",
Consumers: []structs.ServiceConsumer{
{
Peer: "east",
},
{
Peer: "west",
},
},
},
{
Name: "cache",
Consumers: []structs.ServiceConsumer{
{
Peer: "east",
},
},
},
},
}
fakeFSM.GetState().EnsureConfigEntry(1, entry)
headerStream := &testutils.MockServerTransportStream{}
ctx := grpc.NewContextWithServerTransportStream(context.Background(), headerStream)
resp, err := server.GetResolvedExportedServices(ctx, &pbconfigentry.GetResolvedExportedServicesRequest{})
require.NoError(t, err)
require.Equal(t, 2, len(resp.Services))
require.Equal(t, []string{"1"}, headerStream.MD.Get("index"))
// Updating the index
fakeFSM.GetState().EnsureConfigEntry(2, entry)
headerStream = &testutils.MockServerTransportStream{}
ctx = grpc.NewContextWithServerTransportStream(context.Background(), headerStream)
resp, err = server.GetResolvedExportedServices(ctx, &pbconfigentry.GetResolvedExportedServicesRequest{})
require.NoError(t, err)
require.Equal(t, 2, len(resp.Services))
require.Equal(t, []string{"2"}, headerStream.MD.Get("index"))
}
func TestGetResolvedExportedServices_Metrics(t *testing.T) {
sink := metrics.NewInmemSink(5*time.Second, time.Minute)
cfg := metrics.DefaultConfig("consul")
metrics.NewGlobal(cfg, sink)
authorizer := acl.MockAuthorizer{}
authorizer.On("MeshRead", mock.Anything).Return(acl.Allow)
backend := &MockBackend{authorizer: &authorizer}
backend.On("EnterpriseCheckPartitions", mock.Anything).Return(nil)
fakeFSM := testutils.NewFakeBlockingFSM(t)
c := Config{
Backend: backend,
Logger: hclog.New(nil),
ForwardRPC: doForwardRPC,
FSMServer: fakeFSM,
}
server := NewServer(c)
// Add config entry
entry := &structs.ExportedServicesConfigEntry{
Name: "default",
Services: []structs.ExportedService{
{
Name: "db",
Consumers: []structs.ServiceConsumer{
{
Peer: "east",
},
{
Peer: "west",
},
},
},
{
Name: "cache",
Consumers: []structs.ServiceConsumer{
{
Peer: "east",
},
},
},
},
}
fakeFSM.GetState().EnsureConfigEntry(1, entry)
ctx := grpc.NewContextWithServerTransportStream(context.Background(), &testutils.MockServerTransportStream{})
resp, err := server.GetResolvedExportedServices(ctx, &pbconfigentry.GetResolvedExportedServicesRequest{})
require.NoError(t, err)
require.Equal(t, 2, len(resp.Services))
// Checking if metrics were added
require.NotNil(t, sink.Data()[0].Samples[`consul.configentry.get_resolved_exported_services`])
}
func doForwardRPC(structs.RPCInfo, func(*grpc.ClientConn) error) (bool, error) {
return false, nil
}