8cd6c9f95e
This is the OSS portion of enterprise PR 2460. Introduces a server-local implementation of the proxycfg.ResolvedServiceConfig interface that sources data from a blocking query against the server's state store. It moves the service config resolution logic into the agent/configentry package so that it can be used in both the RPC handler and data source. I've also done a little re-arranging and adding comments to call out data sources for which there is to be no server-local equivalent.
117 lines
3.5 KiB
Go
117 lines
3.5 KiB
Go
package proxycfgglue
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/hashicorp/consul/acl"
|
|
"github.com/hashicorp/consul/agent/consul/state"
|
|
"github.com/hashicorp/consul/agent/proxycfg"
|
|
"github.com/hashicorp/consul/agent/structs"
|
|
"github.com/hashicorp/consul/sdk/testutil"
|
|
)
|
|
|
|
func TestServerResolvedServiceConfig(t *testing.T) {
|
|
t.Run("remote queries are delegated to the remote source", func(t *testing.T) {
|
|
var (
|
|
ctx = context.Background()
|
|
req = &structs.ServiceConfigRequest{Datacenter: "dc2"}
|
|
correlationID = "correlation-id"
|
|
ch = make(chan<- proxycfg.UpdateEvent)
|
|
result = errors.New("KABOOM")
|
|
)
|
|
|
|
remoteSource := newMockResolvedServiceConfig(t)
|
|
remoteSource.On("Notify", ctx, req, correlationID, ch).Return(result)
|
|
|
|
dataSource := ServerResolvedServiceConfig(ServerDataSourceDeps{Datacenter: "dc1"}, remoteSource)
|
|
err := dataSource.Notify(ctx, req, correlationID, ch)
|
|
require.Equal(t, result, err)
|
|
})
|
|
|
|
t.Run("local queries are served from the state store", func(t *testing.T) {
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
t.Cleanup(cancel)
|
|
|
|
const (
|
|
serviceName = "web"
|
|
datacenter = "dc1"
|
|
)
|
|
|
|
store := state.NewStateStore(nil)
|
|
nextIndex := indexGenerator()
|
|
|
|
require.NoError(t, store.EnsureConfigEntry(nextIndex(), &structs.ServiceConfigEntry{
|
|
Name: serviceName,
|
|
Protocol: "http",
|
|
}))
|
|
|
|
authz := newStaticResolver(
|
|
policyAuthorizer(t, fmt.Sprintf(`service "%s" { policy = "read" }`, serviceName)),
|
|
)
|
|
|
|
dataSource := ServerResolvedServiceConfig(ServerDataSourceDeps{
|
|
Datacenter: datacenter,
|
|
ACLResolver: authz,
|
|
GetStore: func() Store { return store },
|
|
}, nil)
|
|
|
|
eventCh := make(chan proxycfg.UpdateEvent)
|
|
require.NoError(t, dataSource.Notify(ctx, &structs.ServiceConfigRequest{Datacenter: datacenter, Name: serviceName}, "", eventCh))
|
|
|
|
testutil.RunStep(t, "initial state", func(t *testing.T) {
|
|
result := getEventResult[*structs.ServiceConfigResponse](t, eventCh)
|
|
require.Equal(t, map[string]any{"protocol": "http"}, result.ProxyConfig)
|
|
})
|
|
|
|
testutil.RunStep(t, "write proxy defaults", func(t *testing.T) {
|
|
require.NoError(t, store.EnsureConfigEntry(nextIndex(), &structs.ProxyConfigEntry{
|
|
Name: structs.ProxyConfigGlobal,
|
|
Mode: structs.ProxyModeDirect,
|
|
}))
|
|
result := getEventResult[*structs.ServiceConfigResponse](t, eventCh)
|
|
require.Equal(t, structs.ProxyModeDirect, result.Mode)
|
|
})
|
|
|
|
testutil.RunStep(t, "delete service config", func(t *testing.T) {
|
|
require.NoError(t, store.DeleteConfigEntry(nextIndex(), structs.ServiceDefaults, serviceName, nil))
|
|
|
|
result := getEventResult[*structs.ServiceConfigResponse](t, eventCh)
|
|
require.Empty(t, result.ProxyConfig)
|
|
})
|
|
|
|
testutil.RunStep(t, "revoke access", func(t *testing.T) {
|
|
authz.SwapAuthorizer(acl.DenyAll())
|
|
|
|
require.NoError(t, store.EnsureConfigEntry(nextIndex(), &structs.ServiceConfigEntry{
|
|
Name: serviceName,
|
|
Protocol: "http",
|
|
}))
|
|
|
|
expectNoEvent(t, eventCh)
|
|
})
|
|
})
|
|
}
|
|
|
|
func newMockResolvedServiceConfig(t *testing.T) *mockResolvedServiceConfig {
|
|
mock := &mockResolvedServiceConfig{}
|
|
mock.Mock.Test(t)
|
|
|
|
t.Cleanup(func() { mock.AssertExpectations(t) })
|
|
|
|
return mock
|
|
}
|
|
|
|
type mockResolvedServiceConfig struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *mockResolvedServiceConfig) Notify(ctx context.Context, req *structs.ServiceConfigRequest, correlationID string, ch chan<- proxycfg.UpdateEvent) error {
|
|
return m.Called(ctx, req, correlationID, ch).Error(0)
|
|
}
|