server: ensure that service-defaults meta is incorporated into the discovery chain response (#12511)
Also add a new "Default" field to the discovery chain response to clients
This commit is contained in:
parent
0fd6cdc900
commit
d4e80b8800
|
@ -0,0 +1,7 @@
|
||||||
|
```release-note:feature
|
||||||
|
server: ensure that service-defaults meta is incorporated into the discovery chain response
|
||||||
|
```
|
||||||
|
|
||||||
|
```release-note:feature
|
||||||
|
server: discovery chains now include a response field named "Default" to indicate if they were not constructed from any service-resolver, service-splitter, or service-router config entries
|
||||||
|
```
|
|
@ -98,6 +98,7 @@ func TestDiscoveryChainEndpoint_Get(t *testing.T) {
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
StartNode: "resolver:web.default.default.dc1",
|
StartNode: "resolver:web.default.default.dc1",
|
||||||
|
Default: true,
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"resolver:web.default.default.dc1": {
|
"resolver:web.default.default.dc1": {
|
||||||
Type: structs.DiscoveryGraphNodeTypeResolver,
|
Type: structs.DiscoveryGraphNodeTypeResolver,
|
||||||
|
@ -286,12 +287,7 @@ func TestDiscoveryChainEndpoint_Get_BlockOnNoChange(t *testing.T) {
|
||||||
args.QueryOptions.MinQueryIndex = minQueryIndex
|
args.QueryOptions.MinQueryIndex = minQueryIndex
|
||||||
|
|
||||||
var out structs.DiscoveryChainResponse
|
var out structs.DiscoveryChainResponse
|
||||||
errCh := channelCallRPC(s1, "DiscoveryChain.Get", &args, &out, func() error {
|
errCh := channelCallRPC(s1, "DiscoveryChain.Get", &args, &out, nil)
|
||||||
if !out.Chain.IsDefault() {
|
|
||||||
return fmt.Errorf("expected default chain")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return &out.QueryMeta, errCh
|
return &out.QueryMeta, errCh
|
||||||
},
|
},
|
||||||
func(i int) <-chan error {
|
func(i int) <-chan error {
|
||||||
|
|
|
@ -161,6 +161,12 @@ type compiler struct {
|
||||||
// This is an OUTPUT field.
|
// This is an OUTPUT field.
|
||||||
protocol string
|
protocol string
|
||||||
|
|
||||||
|
// serviceMeta is the Meta field from the service-defaults entry that
|
||||||
|
// shares a name with this discovery chain.
|
||||||
|
//
|
||||||
|
// This is an OUTPUT field.
|
||||||
|
serviceMeta map[string]string
|
||||||
|
|
||||||
// startNode is computed inside of assembleChain()
|
// startNode is computed inside of assembleChain()
|
||||||
//
|
//
|
||||||
// This is an OUTPUT field.
|
// This is an OUTPUT field.
|
||||||
|
@ -327,14 +333,47 @@ func (c *compiler) compile() (*structs.CompiledDiscoveryChain, error) {
|
||||||
Namespace: c.evaluateInNamespace,
|
Namespace: c.evaluateInNamespace,
|
||||||
Partition: c.evaluateInPartition,
|
Partition: c.evaluateInPartition,
|
||||||
Datacenter: c.evaluateInDatacenter,
|
Datacenter: c.evaluateInDatacenter,
|
||||||
|
Default: c.determineIfDefaultChain(),
|
||||||
CustomizationHash: customizationHash,
|
CustomizationHash: customizationHash,
|
||||||
Protocol: c.protocol,
|
Protocol: c.protocol,
|
||||||
|
ServiceMeta: c.serviceMeta,
|
||||||
StartNode: c.startNode,
|
StartNode: c.startNode,
|
||||||
Nodes: c.nodes,
|
Nodes: c.nodes,
|
||||||
Targets: c.loadedTargets,
|
Targets: c.loadedTargets,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// determineIfDefaultChain returns true if the compiled chain represents no
|
||||||
|
// routing, no splitting, and only the default resolution. We have to be
|
||||||
|
// careful here to avoid returning "yep this is default" when the only
|
||||||
|
// resolver action being applied is redirection to another resolver that is
|
||||||
|
// default, so we double check the resolver matches the requested resolver.
|
||||||
|
//
|
||||||
|
// NOTE: "default chain" mostly means that this is compatible with how things
|
||||||
|
// worked (roughly) in consul 1.5 pre-discovery chain, not that there are zero
|
||||||
|
// config entries in play (like service-defaults).
|
||||||
|
func (c *compiler) determineIfDefaultChain() bool {
|
||||||
|
if c.startNode == "" || len(c.nodes) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
node := c.nodes[c.startNode]
|
||||||
|
if node == nil {
|
||||||
|
panic("not possible: missing node named '" + c.startNode + "' in chain '" + c.serviceName + "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.Type != structs.DiscoveryGraphNodeTypeResolver {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if !node.Resolver.Default {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
target := c.loadedTargets[node.Resolver.Target]
|
||||||
|
|
||||||
|
return target.Service == c.serviceName && target.Namespace == c.evaluateInNamespace && target.Partition == c.evaluateInPartition
|
||||||
|
}
|
||||||
|
|
||||||
func (c *compiler) detectCircularReferences() error {
|
func (c *compiler) detectCircularReferences() error {
|
||||||
var (
|
var (
|
||||||
todo stringStack
|
todo stringStack
|
||||||
|
@ -515,6 +554,11 @@ func (c *compiler) assembleChain() error {
|
||||||
|
|
||||||
sid := structs.NewServiceID(c.serviceName, c.GetEnterpriseMeta())
|
sid := structs.NewServiceID(c.serviceName, c.GetEnterpriseMeta())
|
||||||
|
|
||||||
|
// Extract the service meta for the service named by this discovery chain.
|
||||||
|
if serviceDefault := c.entries.GetService(sid); serviceDefault != nil {
|
||||||
|
c.serviceMeta = serviceDefault.GetMeta()
|
||||||
|
}
|
||||||
|
|
||||||
// Check for short circuit path.
|
// Check for short circuit path.
|
||||||
if len(c.resolvers) == 0 && c.entries.IsChainEmpty() {
|
if len(c.resolvers) == 0 && c.entries.IsChainEmpty() {
|
||||||
// Materialize defaults and cache.
|
// Materialize defaults and cache.
|
||||||
|
|
|
@ -12,14 +12,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type compileTestCase struct {
|
type compileTestCase struct {
|
||||||
entries *configentry.DiscoveryChainSet
|
entries *configentry.DiscoveryChainSet
|
||||||
setup func(req *CompileRequest)
|
setup func(req *CompileRequest)
|
||||||
expect *structs.CompiledDiscoveryChain
|
expect *structs.CompiledDiscoveryChain
|
||||||
// expectIsDefault tests behavior of CompiledDiscoveryChain.IsDefault()
|
expectCustom bool
|
||||||
expectIsDefault bool
|
expectErr string
|
||||||
expectCustom bool
|
expectGraphErr bool
|
||||||
expectErr string
|
|
||||||
expectGraphErr bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompile(t *testing.T) {
|
func TestCompile(t *testing.T) {
|
||||||
|
@ -56,6 +54,8 @@ func TestCompile(t *testing.T) {
|
||||||
"loadbalancer splitter and resolver": testcase_LBSplitterAndResolver(),
|
"loadbalancer splitter and resolver": testcase_LBSplitterAndResolver(),
|
||||||
"loadbalancer resolver": testcase_LBResolver(),
|
"loadbalancer resolver": testcase_LBResolver(),
|
||||||
"service redirect to service with default resolver is not a default chain": testcase_RedirectToDefaultResolverIsNotDefaultChain(),
|
"service redirect to service with default resolver is not a default chain": testcase_RedirectToDefaultResolverIsNotDefaultChain(),
|
||||||
|
"service meta projection": testcase_ServiceMetaProjection(),
|
||||||
|
"service meta projection with redirect": testcase_ServiceMetaProjectionWithRedirect(),
|
||||||
|
|
||||||
"all the bells and whistles": testcase_AllBellsAndWhistles(),
|
"all the bells and whistles": testcase_AllBellsAndWhistles(),
|
||||||
"multi dc canary": testcase_MultiDatacenterCanary(),
|
"multi dc canary": testcase_MultiDatacenterCanary(),
|
||||||
|
@ -141,7 +141,6 @@ func TestCompile(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
require.Equal(t, tc.expect, res)
|
require.Equal(t, tc.expect, res)
|
||||||
require.Equal(t, tc.expectIsDefault, res.IsDefault())
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1429,6 +1428,7 @@ func testcase_DefaultResolver() compileTestCase {
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
|
Default: true,
|
||||||
StartNode: "resolver:main.default.default.dc1",
|
StartNode: "resolver:main.default.default.dc1",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"resolver:main.default.default.dc1": {
|
"resolver:main.default.default.dc1": {
|
||||||
|
@ -1446,7 +1446,7 @@ func testcase_DefaultResolver() compileTestCase {
|
||||||
"main.default.default.dc1": newTarget("main", "", "default", "default", "dc1", nil),
|
"main.default.default.dc1": newTarget("main", "", "default", "default", "dc1", nil),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return compileTestCase{entries: entries, expect: expect, expectIsDefault: true}
|
return compileTestCase{entries: entries, expect: expect}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testcase_DefaultResolver_WithProxyDefaults() compileTestCase {
|
func testcase_DefaultResolver_WithProxyDefaults() compileTestCase {
|
||||||
|
@ -1465,6 +1465,7 @@ func testcase_DefaultResolver_WithProxyDefaults() compileTestCase {
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "grpc",
|
Protocol: "grpc",
|
||||||
|
Default: true,
|
||||||
StartNode: "resolver:main.default.default.dc1",
|
StartNode: "resolver:main.default.default.dc1",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"resolver:main.default.default.dc1": {
|
"resolver:main.default.default.dc1": {
|
||||||
|
@ -1485,11 +1486,69 @@ func testcase_DefaultResolver_WithProxyDefaults() compileTestCase {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return compileTestCase{entries: entries, expect: expect, expectIsDefault: true}
|
return compileTestCase{entries: entries, expect: expect}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testcase_RedirectToDefaultResolverIsNotDefaultChain() compileTestCase {
|
func testcase_ServiceMetaProjection() compileTestCase {
|
||||||
entries := newEntries()
|
entries := newEntries()
|
||||||
|
entries.AddServices(
|
||||||
|
&structs.ServiceConfigEntry{
|
||||||
|
Kind: structs.ServiceDefaults,
|
||||||
|
Name: "main",
|
||||||
|
Meta: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"abc": "123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
|
Protocol: "tcp",
|
||||||
|
Default: true,
|
||||||
|
ServiceMeta: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"abc": "123",
|
||||||
|
},
|
||||||
|
StartNode: "resolver:main.default.default.dc1",
|
||||||
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
|
"resolver:main.default.default.dc1": {
|
||||||
|
Type: structs.DiscoveryGraphNodeTypeResolver,
|
||||||
|
Name: "main.default.default.dc1",
|
||||||
|
Resolver: &structs.DiscoveryResolver{
|
||||||
|
Default: true,
|
||||||
|
ConnectTimeout: 5 * time.Second,
|
||||||
|
Target: "main.default.default.dc1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Targets: map[string]*structs.DiscoveryTarget{
|
||||||
|
"main.default.default.dc1": newTarget("main", "", "default", "default", "dc1", nil),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return compileTestCase{entries: entries, expect: expect}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testcase_ServiceMetaProjectionWithRedirect() compileTestCase {
|
||||||
|
entries := newEntries()
|
||||||
|
entries.AddServices(
|
||||||
|
&structs.ServiceConfigEntry{
|
||||||
|
Kind: structs.ServiceDefaults,
|
||||||
|
Name: "main",
|
||||||
|
Meta: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"abc": "123",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
&structs.ServiceConfigEntry{
|
||||||
|
Kind: structs.ServiceDefaults,
|
||||||
|
Name: "other",
|
||||||
|
Meta: map[string]string{
|
||||||
|
"zim": "gir",
|
||||||
|
"abc": "456",
|
||||||
|
"xyz": "999",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
entries.AddResolvers(
|
entries.AddResolvers(
|
||||||
&structs.ServiceResolverConfigEntry{
|
&structs.ServiceResolverConfigEntry{
|
||||||
Kind: structs.ServiceResolver,
|
Kind: structs.ServiceResolver,
|
||||||
|
@ -1501,7 +1560,12 @@ func testcase_RedirectToDefaultResolverIsNotDefaultChain() compileTestCase {
|
||||||
)
|
)
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
|
ServiceMeta: map[string]string{
|
||||||
|
// Note this is main's meta, not other's.
|
||||||
|
"foo": "bar",
|
||||||
|
"abc": "123",
|
||||||
|
},
|
||||||
StartNode: "resolver:other.default.default.dc1",
|
StartNode: "resolver:other.default.default.dc1",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"resolver:other.default.default.dc1": {
|
"resolver:other.default.default.dc1": {
|
||||||
|
@ -1519,7 +1583,42 @@ func testcase_RedirectToDefaultResolverIsNotDefaultChain() compileTestCase {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return compileTestCase{entries: entries, expect: expect, expectIsDefault: false /*being explicit here because this is the whole point of this test*/}
|
return compileTestCase{entries: entries, expect: expect}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testcase_RedirectToDefaultResolverIsNotDefaultChain() compileTestCase {
|
||||||
|
entries := newEntries()
|
||||||
|
entries.AddResolvers(
|
||||||
|
&structs.ServiceResolverConfigEntry{
|
||||||
|
Kind: structs.ServiceResolver,
|
||||||
|
Name: "main",
|
||||||
|
Redirect: &structs.ServiceResolverRedirect{
|
||||||
|
Service: "other",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
|
Protocol: "tcp",
|
||||||
|
StartNode: "resolver:other.default.default.dc1",
|
||||||
|
Default: false, /*being explicit here because this is the whole point of this test*/
|
||||||
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
|
"resolver:other.default.default.dc1": {
|
||||||
|
Type: structs.DiscoveryGraphNodeTypeResolver,
|
||||||
|
Name: "other.default.default.dc1",
|
||||||
|
Resolver: &structs.DiscoveryResolver{
|
||||||
|
Default: true,
|
||||||
|
ConnectTimeout: 5 * time.Second,
|
||||||
|
Target: "other.default.default.dc1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Targets: map[string]*structs.DiscoveryTarget{
|
||||||
|
"other.default.default.dc1": newTarget("other", "", "default", "default", "dc1", nil),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return compileTestCase{entries: entries, expect: expect}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testcase_Resolve_WithDefaultSubset() compileTestCase {
|
func testcase_Resolve_WithDefaultSubset() compileTestCase {
|
||||||
|
@ -1570,6 +1669,7 @@ func testcase_DefaultResolver_ExternalSNI() compileTestCase {
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
|
Default: true,
|
||||||
StartNode: "resolver:main.default.default.dc1",
|
StartNode: "resolver:main.default.default.dc1",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"resolver:main.default.default.dc1": {
|
"resolver:main.default.default.dc1": {
|
||||||
|
@ -1589,7 +1689,7 @@ func testcase_DefaultResolver_ExternalSNI() compileTestCase {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return compileTestCase{entries: entries, expect: expect, expectIsDefault: true}
|
return compileTestCase{entries: entries, expect: expect}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testcase_Resolver_ExternalSNI_FailoverNotAllowed() compileTestCase {
|
func testcase_Resolver_ExternalSNI_FailoverNotAllowed() compileTestCase {
|
||||||
|
@ -2249,6 +2349,7 @@ func testcase_ResolverProtocolOverride() compileTestCase {
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http2",
|
Protocol: "http2",
|
||||||
|
Default: true,
|
||||||
StartNode: "resolver:main.default.default.dc1",
|
StartNode: "resolver:main.default.default.dc1",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"resolver:main.default.default.dc1": {
|
"resolver:main.default.default.dc1": {
|
||||||
|
@ -2266,7 +2367,7 @@ func testcase_ResolverProtocolOverride() compileTestCase {
|
||||||
"main.default.default.dc1": newTarget("main", "", "default", "default", "dc1", nil),
|
"main.default.default.dc1": newTarget("main", "", "default", "default", "dc1", nil),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return compileTestCase{entries: entries, expect: expect, expectIsDefault: true,
|
return compileTestCase{entries: entries, expect: expect,
|
||||||
expectCustom: true,
|
expectCustom: true,
|
||||||
setup: func(req *CompileRequest) {
|
setup: func(req *CompileRequest) {
|
||||||
req.OverrideProtocol = "http2"
|
req.OverrideProtocol = "http2"
|
||||||
|
@ -2282,6 +2383,7 @@ func testcase_ResolverProtocolOverrideIgnored() compileTestCase {
|
||||||
|
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "http2",
|
Protocol: "http2",
|
||||||
|
Default: true,
|
||||||
StartNode: "resolver:main.default.default.dc1",
|
StartNode: "resolver:main.default.default.dc1",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"resolver:main.default.default.dc1": {
|
"resolver:main.default.default.dc1": {
|
||||||
|
@ -2299,7 +2401,7 @@ func testcase_ResolverProtocolOverrideIgnored() compileTestCase {
|
||||||
"main.default.default.dc1": newTarget("main", "", "default", "default", "dc1", nil),
|
"main.default.default.dc1": newTarget("main", "", "default", "default", "dc1", nil),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return compileTestCase{entries: entries, expect: expect, expectIsDefault: true,
|
return compileTestCase{entries: entries, expect: expect,
|
||||||
setup: func(req *CompileRequest) {
|
setup: func(req *CompileRequest) {
|
||||||
req.OverrideProtocol = "http2"
|
req.OverrideProtocol = "http2"
|
||||||
},
|
},
|
||||||
|
@ -2320,6 +2422,7 @@ func testcase_RouterIgnored_ResolverProtocolOverride() compileTestCase {
|
||||||
expect := &structs.CompiledDiscoveryChain{
|
expect := &structs.CompiledDiscoveryChain{
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
StartNode: "resolver:main.default.default.dc1",
|
StartNode: "resolver:main.default.default.dc1",
|
||||||
|
Default: true,
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"resolver:main.default.default.dc1": {
|
"resolver:main.default.default.dc1": {
|
||||||
Type: structs.DiscoveryGraphNodeTypeResolver,
|
Type: structs.DiscoveryGraphNodeTypeResolver,
|
||||||
|
@ -2336,7 +2439,7 @@ func testcase_RouterIgnored_ResolverProtocolOverride() compileTestCase {
|
||||||
"main.default.default.dc1": newTarget("main", "", "default", "default", "dc1", nil),
|
"main.default.default.dc1": newTarget("main", "", "default", "default", "dc1", nil),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return compileTestCase{entries: entries, expect: expect, expectIsDefault: true,
|
return compileTestCase{entries: entries, expect: expect,
|
||||||
expectCustom: true,
|
expectCustom: true,
|
||||||
setup: func(req *CompileRequest) {
|
setup: func(req *CompileRequest) {
|
||||||
req.OverrideProtocol = "tcp"
|
req.OverrideProtocol = "tcp"
|
||||||
|
|
|
@ -8,11 +8,12 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/connect"
|
"github.com/hashicorp/consul/agent/connect"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
"github.com/hashicorp/consul/sdk/testutil/retry"
|
"github.com/hashicorp/consul/sdk/testutil/retry"
|
||||||
"github.com/hashicorp/consul/testrpc"
|
"github.com/hashicorp/consul/testrpc"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDiscoveryChainRead(t *testing.T) {
|
func TestDiscoveryChainRead(t *testing.T) {
|
||||||
|
@ -79,6 +80,7 @@ func TestDiscoveryChainRead(t *testing.T) {
|
||||||
Partition: "default",
|
Partition: "default",
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
|
Default: true,
|
||||||
StartNode: "resolver:web.default.default.dc1",
|
StartNode: "resolver:web.default.default.dc1",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
"resolver:web.default.default.dc1": {
|
"resolver:web.default.default.dc1": {
|
||||||
|
@ -122,6 +124,7 @@ func TestDiscoveryChainRead(t *testing.T) {
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
Partition: "default",
|
Partition: "default",
|
||||||
Datacenter: "dc2",
|
Datacenter: "dc2",
|
||||||
|
Default: true,
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
StartNode: "resolver:web.default.default.dc2",
|
StartNode: "resolver:web.default.default.dc2",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
|
@ -175,6 +178,7 @@ func TestDiscoveryChainRead(t *testing.T) {
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
Partition: "default",
|
Partition: "default",
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
|
Default: true,
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
StartNode: "resolver:web.default.default.dc1",
|
StartNode: "resolver:web.default.default.dc1",
|
||||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||||
|
|
|
@ -16,7 +16,7 @@ func TestConfigSnapshot(t testing.T, nsFn func(ns *structs.NodeService), extraUp
|
||||||
|
|
||||||
// no entries implies we'll get a default chain
|
// no entries implies we'll get a default chain
|
||||||
dbChain := discoverychain.TestCompileConfigEntries(t, "db", "default", "default", "dc1", connect.TestClusterID+".consul", nil)
|
dbChain := discoverychain.TestCompileConfigEntries(t, "db", "default", "default", "dc1", connect.TestClusterID+".consul", nil)
|
||||||
assert.True(t, dbChain.IsDefault())
|
assert.True(t, dbChain.Default)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
upstreams = structs.TestUpstreams(t)
|
upstreams = structs.TestUpstreams(t)
|
||||||
|
|
|
@ -685,10 +685,10 @@ func TestConfigSnapshotIngress_HTTPMultipleServices(t testing.T) *ConfigSnapshot
|
||||||
quxChain = discoverychain.TestCompileConfigEntries(t, "qux", "default", "default", "dc1", connect.TestClusterID+".consul", nil, entries...)
|
quxChain = discoverychain.TestCompileConfigEntries(t, "qux", "default", "default", "dc1", connect.TestClusterID+".consul", nil, entries...)
|
||||||
)
|
)
|
||||||
|
|
||||||
require.False(t, fooChain.IsDefault())
|
require.False(t, fooChain.Default)
|
||||||
require.False(t, barChain.IsDefault())
|
require.False(t, barChain.Default)
|
||||||
require.True(t, bazChain.IsDefault())
|
require.True(t, bazChain.Default)
|
||||||
require.True(t, quxChain.IsDefault())
|
require.True(t, quxChain.Default)
|
||||||
|
|
||||||
return TestConfigSnapshotIngressGateway(t, false, "http", "default", nil, func(entry *structs.IngressGatewayConfigEntry) {
|
return TestConfigSnapshotIngressGateway(t, false, "http", "default", nil, func(entry *structs.IngressGatewayConfigEntry) {
|
||||||
entry.Listeners = []structs.IngressListener{
|
entry.Listeners = []structs.IngressListener{
|
||||||
|
|
|
@ -26,9 +26,17 @@ type CompiledDiscoveryChain struct {
|
||||||
// non-customized versions.
|
// non-customized versions.
|
||||||
CustomizationHash string `json:",omitempty"`
|
CustomizationHash string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Default indicates if this discovery chain is based on no
|
||||||
|
// service-resolver, service-splitter, or service-router config entries.
|
||||||
|
Default bool `json:",omitempty"`
|
||||||
|
|
||||||
// Protocol is the overall protocol shared by everything in the chain.
|
// Protocol is the overall protocol shared by everything in the chain.
|
||||||
Protocol string `json:",omitempty"`
|
Protocol string `json:",omitempty"`
|
||||||
|
|
||||||
|
// ServiceMeta is the metadata from the underlying service-defaults config
|
||||||
|
// entry for the service named ServiceName.
|
||||||
|
ServiceMeta map[string]string `json:",omitempty"`
|
||||||
|
|
||||||
// StartNode is the first key into the Nodes map that should be followed
|
// StartNode is the first key into the Nodes map that should be followed
|
||||||
// when walking the discovery chain.
|
// when walking the discovery chain.
|
||||||
StartNode string `json:",omitempty"`
|
StartNode string `json:",omitempty"`
|
||||||
|
@ -62,33 +70,6 @@ func (c *CompiledDiscoveryChain) WillFailoverThroughMeshGateway(node *DiscoveryG
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsDefault returns true if the compiled chain represents no routing, no
|
|
||||||
// splitting, and only the default resolution. We have to be careful here to
|
|
||||||
// avoid returning "yep this is default" when the only resolver action being
|
|
||||||
// applied is redirection to another resolver that is default, so we double
|
|
||||||
// check the resolver matches the requested resolver.
|
|
||||||
func (c *CompiledDiscoveryChain) IsDefault() bool {
|
|
||||||
if c.StartNode == "" || len(c.Nodes) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
node := c.Nodes[c.StartNode]
|
|
||||||
if node == nil {
|
|
||||||
panic("not possible: missing node named '" + c.StartNode + "' in chain '" + c.ServiceName + "'")
|
|
||||||
}
|
|
||||||
|
|
||||||
if node.Type != DiscoveryGraphNodeTypeResolver {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !node.Resolver.Default {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
target := c.Targets[node.Resolver.Target]
|
|
||||||
|
|
||||||
return target.Service == c.ServiceName && target.Namespace == c.Namespace && target.Partition == c.Partition
|
|
||||||
}
|
|
||||||
|
|
||||||
// ID returns an ID that encodes the service, namespace, partition, and datacenter.
|
// ID returns an ID that encodes the service, namespace, partition, and datacenter.
|
||||||
// This ID allows us to compare a discovery chain target to the chain upstream itself.
|
// This ID allows us to compare a discovery chain target to the chain upstream itself.
|
||||||
func (c *CompiledDiscoveryChain) ID() string {
|
func (c *CompiledDiscoveryChain) ID() string {
|
||||||
|
|
|
@ -612,7 +612,7 @@ func (s *ResourceGenerator) makeUpstreamClustersForDiscoveryChain(
|
||||||
|
|
||||||
var escapeHatchCluster *envoy_cluster_v3.Cluster
|
var escapeHatchCluster *envoy_cluster_v3.Cluster
|
||||||
if cfg.EnvoyClusterJSON != "" {
|
if cfg.EnvoyClusterJSON != "" {
|
||||||
if chain.IsDefault() {
|
if chain.Default {
|
||||||
// If you haven't done anything to setup the discovery chain, then
|
// If you haven't done anything to setup the discovery chain, then
|
||||||
// you can use the envoy_cluster_json escape hatch.
|
// you can use the envoy_cluster_json escape hatch.
|
||||||
escapeHatchCluster, err = makeClusterFromUserConfig(cfg.EnvoyClusterJSON)
|
escapeHatchCluster, err = makeClusterFromUserConfig(cfg.EnvoyClusterJSON)
|
||||||
|
|
|
@ -393,7 +393,7 @@ func (s *ResourceGenerator) endpointsFromDiscoveryChain(
|
||||||
|
|
||||||
var escapeHatchCluster *envoy_cluster_v3.Cluster
|
var escapeHatchCluster *envoy_cluster_v3.Cluster
|
||||||
if cfg.EnvoyClusterJSON != "" {
|
if cfg.EnvoyClusterJSON != "" {
|
||||||
if chain.IsDefault() {
|
if chain.Default {
|
||||||
// If you haven't done anything to setup the discovery chain, then
|
// If you haven't done anything to setup the discovery chain, then
|
||||||
// you can use the envoy_cluster_json escape hatch.
|
// you can use the envoy_cluster_json escape hatch.
|
||||||
escapeHatchCluster, err = makeClusterFromUserConfig(cfg.EnvoyClusterJSON)
|
escapeHatchCluster, err = makeClusterFromUserConfig(cfg.EnvoyClusterJSON)
|
||||||
|
|
|
@ -115,7 +115,7 @@ func (s *ResourceGenerator) listenersFromSnapshotConnectProxy(cfgSnap *proxycfg.
|
||||||
}
|
}
|
||||||
|
|
||||||
// RDS, Envoy's Route Discovery Service, is only used for HTTP services with a customized discovery chain.
|
// RDS, Envoy's Route Discovery Service, is only used for HTTP services with a customized discovery chain.
|
||||||
useRDS := chain.Protocol != "tcp" && !chain.IsDefault()
|
useRDS := chain.Protocol != "tcp" && !chain.Default
|
||||||
|
|
||||||
var clusterName string
|
var clusterName string
|
||||||
if !useRDS {
|
if !useRDS {
|
||||||
|
@ -1303,7 +1303,7 @@ func (s *ResourceGenerator) getAndModifyUpstreamConfigForListener(
|
||||||
if u != nil {
|
if u != nil {
|
||||||
configMap = u.Config
|
configMap = u.Config
|
||||||
}
|
}
|
||||||
if chain == nil || chain.IsDefault() {
|
if chain == nil || chain.Default {
|
||||||
cfg, err = structs.ParseUpstreamConfigNoDefaults(configMap)
|
cfg, err = structs.ParseUpstreamConfigNoDefaults(configMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Don't hard fail on a config typo, just warn. The parse func returns
|
// Don't hard fail on a config typo, just warn. The parse func returns
|
||||||
|
|
|
@ -48,7 +48,7 @@ func (s *ResourceGenerator) makeIngressGatewayListeners(address string, cfgSnap
|
||||||
|
|
||||||
// RDS, Envoy's Route Discovery Service, is only used for HTTP services with a customized discovery chain.
|
// RDS, Envoy's Route Discovery Service, is only used for HTTP services with a customized discovery chain.
|
||||||
// TODO(freddy): Why can the protocol of the listener be overridden here?
|
// TODO(freddy): Why can the protocol of the listener be overridden here?
|
||||||
useRDS := cfg.Protocol != "tcp" && !chain.IsDefault()
|
useRDS := cfg.Protocol != "tcp" && !chain.Default
|
||||||
|
|
||||||
var clusterName string
|
var clusterName string
|
||||||
if !useRDS {
|
if !useRDS {
|
||||||
|
|
|
@ -45,7 +45,7 @@ func (s *ResourceGenerator) routesFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot)
|
||||||
func (s *ResourceGenerator) routesForConnectProxy(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
|
func (s *ResourceGenerator) routesForConnectProxy(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
|
||||||
var resources []proto.Message
|
var resources []proto.Message
|
||||||
for uid, chain := range cfgSnap.ConnectProxy.DiscoveryChain {
|
for uid, chain := range cfgSnap.ConnectProxy.DiscoveryChain {
|
||||||
if chain.IsDefault() {
|
if chain.Default {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,9 +109,17 @@ type CompiledDiscoveryChain struct {
|
||||||
// non-customized versions.
|
// non-customized versions.
|
||||||
CustomizationHash string
|
CustomizationHash string
|
||||||
|
|
||||||
|
// Default indicates if this discovery chain is based on no
|
||||||
|
// service-resolver, service-splitter, or service-router config entries.
|
||||||
|
Default bool
|
||||||
|
|
||||||
// Protocol is the overall protocol shared by everything in the chain.
|
// Protocol is the overall protocol shared by everything in the chain.
|
||||||
Protocol string
|
Protocol string
|
||||||
|
|
||||||
|
// ServiceMeta is the metadata from the underlying service-defaults config
|
||||||
|
// entry for the service named ServiceName.
|
||||||
|
ServiceMeta map[string]string
|
||||||
|
|
||||||
// StartNode is the first key into the Nodes map that should be followed
|
// StartNode is the first key into the Nodes map that should be followed
|
||||||
// when walking the discovery chain.
|
// when walking the discovery chain.
|
||||||
StartNode string
|
StartNode string
|
||||||
|
|
|
@ -32,6 +32,7 @@ func TestAPI_DiscoveryChain_Get(t *testing.T) {
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
Datacenter: "dc1",
|
Datacenter: "dc1",
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
|
Default: true,
|
||||||
StartNode: "resolver:web.default.default.dc1",
|
StartNode: "resolver:web.default.default.dc1",
|
||||||
Nodes: map[string]*DiscoveryGraphNode{
|
Nodes: map[string]*DiscoveryGraphNode{
|
||||||
"resolver:web.default.default.dc1": {
|
"resolver:web.default.default.dc1": {
|
||||||
|
@ -72,6 +73,7 @@ func TestAPI_DiscoveryChain_Get(t *testing.T) {
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
Datacenter: "dc2",
|
Datacenter: "dc2",
|
||||||
Protocol: "tcp",
|
Protocol: "tcp",
|
||||||
|
Default: true,
|
||||||
StartNode: "resolver:web.default.default.dc2",
|
StartNode: "resolver:web.default.default.dc2",
|
||||||
Nodes: map[string]*DiscoveryGraphNode{
|
Nodes: map[string]*DiscoveryGraphNode{
|
||||||
"resolver:web.default.default.dc2": {
|
"resolver:web.default.default.dc2": {
|
||||||
|
|
|
@ -121,9 +121,15 @@ resolved by name using the [`Targets`](#targets) field.
|
||||||
balancer data plane objects to avoid sharing customized and non-customized
|
balancer data plane objects to avoid sharing customized and non-customized
|
||||||
versions.
|
versions.
|
||||||
|
|
||||||
|
- `Default` `(bool: <optional>)` - Indicates if this discovery chain is based on no
|
||||||
|
`service-resolver`, `service-splitter`, or `service-router` config entries.
|
||||||
|
|
||||||
- `Protocol` `(string)` - The overall protocol shared by everything in the
|
- `Protocol` `(string)` - The overall protocol shared by everything in the
|
||||||
chain.
|
chain.
|
||||||
|
|
||||||
|
- `ServiceMeta` `(map<string|string>)` - The metadata from the underlying `service-defaults` config
|
||||||
|
entry for the service named `ServiceName`.
|
||||||
|
|
||||||
- `StartNode` `(string)` - The first key into the `Nodes` map that should be
|
- `StartNode` `(string)` - The first key into the `Nodes` map that should be
|
||||||
followed when traversing the discovery chain.
|
followed when traversing the discovery chain.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue