2023-03-28 18:39:22 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
2023-08-11 13:12:13 +00:00
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
2023-03-28 18:39:22 +00:00
|
|
|
|
2021-04-19 21:51:21 +00:00
|
|
|
package health
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"testing"
|
2021-07-27 21:55:00 +00:00
|
|
|
"time"
|
2021-04-19 21:51:21 +00:00
|
|
|
|
2023-04-14 16:24:46 +00:00
|
|
|
"github.com/hashicorp/consul/agent/rpcclient"
|
2021-04-19 21:51:21 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
|
|
|
"github.com/hashicorp/consul/agent/cache"
|
2021-07-27 21:55:00 +00:00
|
|
|
"github.com/hashicorp/consul/agent/config"
|
2021-04-19 21:51:21 +00:00
|
|
|
"github.com/hashicorp/consul/agent/structs"
|
|
|
|
"github.com/hashicorp/consul/agent/submatview"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestClient_ServiceNodes_BackendRouting(t *testing.T) {
|
|
|
|
type testCase struct {
|
|
|
|
name string
|
|
|
|
req structs.ServiceSpecificRequest
|
|
|
|
expected func(t *testing.T, c *Client)
|
|
|
|
}
|
|
|
|
|
|
|
|
run := func(t *testing.T, tc testCase) {
|
|
|
|
c := &Client{
|
2023-04-14 16:24:46 +00:00
|
|
|
Client: rpcclient.Client{
|
|
|
|
NetRPC: &fakeNetRPC{},
|
|
|
|
Cache: &fakeCache{},
|
|
|
|
ViewStore: &fakeViewStore{},
|
|
|
|
CacheName: "cache-no-streaming",
|
|
|
|
UseStreamingBackend: true,
|
|
|
|
QueryOptionDefaults: config.ApplyDefaultQueryOptions(&config.RuntimeConfig{}),
|
|
|
|
},
|
2021-04-19 21:51:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
_, _, err := c.ServiceNodes(context.Background(), tc.req)
|
|
|
|
require.NoError(t, err)
|
|
|
|
tc.expected(t, c)
|
|
|
|
}
|
|
|
|
|
|
|
|
var testCases = []testCase{
|
|
|
|
{
|
|
|
|
name: "rpc by default",
|
|
|
|
req: structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "web1",
|
|
|
|
},
|
|
|
|
expected: useRPC,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "use streaming instead of cache",
|
|
|
|
req: structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "web1",
|
|
|
|
QueryOptions: structs.QueryOptions{UseCache: true},
|
|
|
|
},
|
|
|
|
expected: useStreaming,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "use streaming for MinQueryIndex",
|
|
|
|
req: structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "web1",
|
|
|
|
QueryOptions: structs.QueryOptions{MinQueryIndex: 22},
|
|
|
|
},
|
|
|
|
expected: useStreaming,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "use cache for ingress request",
|
|
|
|
req: structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "web1",
|
|
|
|
QueryOptions: structs.QueryOptions{UseCache: true},
|
|
|
|
Ingress: true,
|
|
|
|
},
|
|
|
|
expected: useCache,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "use cache for near request",
|
|
|
|
req: structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "web1",
|
|
|
|
QueryOptions: structs.QueryOptions{UseCache: true},
|
|
|
|
Source: structs.QuerySource{Node: "node1"},
|
|
|
|
},
|
|
|
|
expected: useCache,
|
|
|
|
},
|
2022-05-25 20:20:17 +00:00
|
|
|
{
|
|
|
|
name: "rpc if merge-central-config",
|
|
|
|
req: structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "web1",
|
|
|
|
MergeCentralConfig: true,
|
|
|
|
QueryOptions: structs.QueryOptions{MinQueryIndex: 22},
|
|
|
|
},
|
|
|
|
expected: useRPC,
|
|
|
|
},
|
2021-04-19 21:51:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range testCases {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
run(t, tc)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func useRPC(t *testing.T, c *Client) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
rpc, ok := c.NetRPC.(*fakeNetRPC)
|
|
|
|
require.True(t, ok, "test setup error, expected *fakeNetRPC, got %T", c.NetRPC)
|
|
|
|
|
|
|
|
cache, ok := c.Cache.(*fakeCache)
|
|
|
|
require.True(t, ok, "test setup error, expected *fakeCache, got %T", c.Cache)
|
|
|
|
|
|
|
|
store, ok := c.ViewStore.(*fakeViewStore)
|
|
|
|
require.True(t, ok, "test setup error, expected *fakeViewSTore, got %T", c.ViewStore)
|
|
|
|
|
|
|
|
require.Len(t, cache.calls, 0)
|
|
|
|
require.Len(t, store.calls, 0)
|
|
|
|
require.Equal(t, []string{"Health.ServiceNodes"}, rpc.calls)
|
|
|
|
}
|
|
|
|
|
|
|
|
func useStreaming(t *testing.T, c *Client) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
rpc, ok := c.NetRPC.(*fakeNetRPC)
|
|
|
|
require.True(t, ok, "test setup error, expected *fakeNetRPC, got %T", c.NetRPC)
|
|
|
|
|
|
|
|
cache, ok := c.Cache.(*fakeCache)
|
|
|
|
require.True(t, ok, "test setup error, expected *fakeCache, got %T", c.Cache)
|
|
|
|
|
|
|
|
store, ok := c.ViewStore.(*fakeViewStore)
|
|
|
|
require.True(t, ok, "test setup error, expected *fakeViewSTore, got %T", c.ViewStore)
|
|
|
|
|
|
|
|
require.Len(t, cache.calls, 0)
|
|
|
|
require.Len(t, rpc.calls, 0)
|
|
|
|
require.Len(t, store.calls, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
func useCache(t *testing.T, c *Client) {
|
|
|
|
t.Helper()
|
|
|
|
|
|
|
|
rpc, ok := c.NetRPC.(*fakeNetRPC)
|
|
|
|
require.True(t, ok, "test setup error, expected *fakeNetRPC, got %T", c.NetRPC)
|
|
|
|
|
|
|
|
cache, ok := c.Cache.(*fakeCache)
|
|
|
|
require.True(t, ok, "test setup error, expected *fakeCache, got %T", c.Cache)
|
|
|
|
|
|
|
|
store, ok := c.ViewStore.(*fakeViewStore)
|
|
|
|
require.True(t, ok, "test setup error, expected *fakeViewSTore, got %T", c.ViewStore)
|
|
|
|
|
|
|
|
require.Len(t, rpc.calls, 0)
|
|
|
|
require.Len(t, store.calls, 0)
|
|
|
|
require.Equal(t, []string{"cache-no-streaming"}, cache.calls)
|
|
|
|
}
|
|
|
|
|
|
|
|
type fakeCache struct {
|
|
|
|
calls []string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *fakeCache) Get(_ context.Context, t string, _ cache.Request) (interface{}, cache.ResultMeta, error) {
|
|
|
|
f.calls = append(f.calls, t)
|
|
|
|
result := &structs.IndexedCheckServiceNodes{}
|
|
|
|
return result, cache.ResultMeta{}, nil
|
|
|
|
}
|
|
|
|
|
2022-05-20 14:47:40 +00:00
|
|
|
func (f *fakeCache) NotifyCallback(_ context.Context, t string, _ cache.Request, _ string, _ cache.Callback) error {
|
2021-04-19 21:51:21 +00:00
|
|
|
f.calls = append(f.calls, t)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type fakeNetRPC struct {
|
|
|
|
calls []string
|
|
|
|
}
|
|
|
|
|
2022-12-14 15:24:22 +00:00
|
|
|
func (f *fakeNetRPC) RPC(ctx context.Context, method string, _ interface{}, _ interface{}) error {
|
2021-04-19 21:51:21 +00:00
|
|
|
f.calls = append(f.calls, method)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type fakeViewStore struct {
|
|
|
|
calls []submatview.Request
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *fakeViewStore) Get(_ context.Context, req submatview.Request) (submatview.Result, error) {
|
|
|
|
f.calls = append(f.calls, req)
|
|
|
|
return submatview.Result{Value: &structs.IndexedCheckServiceNodes{}}, nil
|
|
|
|
}
|
|
|
|
|
2022-05-20 14:47:40 +00:00
|
|
|
func (f *fakeViewStore) NotifyCallback(_ context.Context, req submatview.Request, _ string, _ cache.Callback) error {
|
2021-04-19 21:51:21 +00:00
|
|
|
f.calls = append(f.calls, req)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestClient_Notify_BackendRouting(t *testing.T) {
|
|
|
|
type testCase struct {
|
|
|
|
name string
|
|
|
|
req structs.ServiceSpecificRequest
|
|
|
|
expected func(t *testing.T, c *Client)
|
|
|
|
}
|
|
|
|
|
|
|
|
run := func(t *testing.T, tc testCase) {
|
|
|
|
c := &Client{
|
2023-04-14 16:24:46 +00:00
|
|
|
Client: rpcclient.Client{
|
|
|
|
NetRPC: &fakeNetRPC{},
|
|
|
|
Cache: &fakeCache{},
|
|
|
|
ViewStore: &fakeViewStore{},
|
|
|
|
CacheName: "cache-no-streaming",
|
|
|
|
UseStreamingBackend: true,
|
|
|
|
},
|
2021-04-19 21:51:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
err := c.Notify(context.Background(), tc.req, "cid", nil)
|
|
|
|
require.NoError(t, err)
|
|
|
|
tc.expected(t, c)
|
|
|
|
}
|
|
|
|
|
|
|
|
var testCases = []testCase{
|
|
|
|
{
|
|
|
|
name: "streaming by default",
|
|
|
|
req: structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "web1",
|
|
|
|
},
|
|
|
|
expected: useStreaming,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "use cache for ingress request",
|
|
|
|
req: structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "web1",
|
|
|
|
Ingress: true,
|
|
|
|
},
|
|
|
|
expected: useCache,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "use cache for near request",
|
|
|
|
req: structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "web1",
|
|
|
|
Source: structs.QuerySource{Node: "node1"},
|
|
|
|
},
|
|
|
|
expected: useCache,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, tc := range testCases {
|
|
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
|
|
run(t, tc)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2021-07-27 21:55:00 +00:00
|
|
|
|
|
|
|
func TestClient_ServiceNodes_SetsDefaults(t *testing.T) {
|
|
|
|
store := &fakeViewStore{}
|
|
|
|
c := &Client{
|
2023-04-14 16:24:46 +00:00
|
|
|
Client: rpcclient.Client{
|
|
|
|
ViewStore: store,
|
|
|
|
CacheName: "cache-no-streaming",
|
|
|
|
UseStreamingBackend: true,
|
|
|
|
QueryOptionDefaults: config.ApplyDefaultQueryOptions(&config.RuntimeConfig{
|
|
|
|
MaxQueryTime: 200 * time.Second,
|
|
|
|
DefaultQueryTime: 100 * time.Second,
|
|
|
|
}),
|
|
|
|
},
|
2021-07-27 21:55:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
req := structs.ServiceSpecificRequest{
|
|
|
|
Datacenter: "dc1",
|
|
|
|
ServiceName: "web1",
|
|
|
|
QueryOptions: structs.QueryOptions{MinQueryIndex: 22},
|
|
|
|
}
|
|
|
|
|
|
|
|
_, _, err := c.ServiceNodes(context.Background(), req)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
require.Len(t, store.calls, 1)
|
|
|
|
require.Equal(t, 100*time.Second, store.calls[0].CacheInfo().Timeout)
|
|
|
|
}
|