diff --git a/.changelog/15833.txt b/.changelog/15833.txt new file mode 100644 index 000000000..df91a7708 --- /dev/null +++ b/.changelog/15833.txt @@ -0,0 +1,3 @@ +```release-note:bug +connect: Fix issue where service-resolver protocol checks incorrectly errored for failover peer targets. +``` diff --git a/agent/consul/discoverychain/compile.go b/agent/consul/discoverychain/compile.go index 8bc3f4d78..7af98bc06 100644 --- a/agent/consul/discoverychain/compile.go +++ b/agent/consul/discoverychain/compile.go @@ -889,8 +889,14 @@ RESOLVE_AGAIN: targetID := target.ServiceID() - if err := c.recordServiceProtocol(targetID); err != nil { - return nil, err + // Only validate protocol if it is not a peered service. + // TODO: Add in remote peer protocol validation when building a chain. + // This likely would require querying the imported services, which + // shouldn't belong in the discovery chain compilation here. + if target.Peer == "" { + if err := c.recordServiceProtocol(targetID); err != nil { + return nil, err + } } // Fetch the config entry. diff --git a/agent/consul/discoverychain/compile_test.go b/agent/consul/discoverychain/compile_test.go index b2867d574..1e6690233 100644 --- a/agent/consul/discoverychain/compile_test.go +++ b/agent/consul/discoverychain/compile_test.go @@ -40,6 +40,8 @@ func TestCompile(t *testing.T) { "service and subset redirect": testcase_ServiceAndSubsetRedirect(), "datacenter redirect": testcase_DatacenterRedirect(), "redirect to cluster peer": testcase_PeerRedirect(), + "redirect to cluster peer http proxy-defaults": testcase_PeerRedirectProxyDefHTTP(), + "redirect to cluster peer http service-defaults": testcase_PeerRedirectSvcDefHTTP(), "datacenter redirect with mesh gateways": testcase_DatacenterRedirect_WithMeshGateways(), "service failover": testcase_ServiceFailover(), "service failover through redirect": testcase_ServiceFailoverThroughRedirect(), @@ -1132,6 +1134,102 @@ func testcase_PeerRedirect() compileTestCase { return compileTestCase{entries: entries, expect: expect} } +func testcase_PeerRedirectProxyDefHTTP() compileTestCase { + entries := newEntries() + entries.AddProxyDefaults(&structs.ProxyConfigEntry{ + Kind: structs.ProxyDefaults, + Name: structs.ProxyConfigGlobal, + Config: map[string]interface{}{ + "Protocol": "http", + }, + }) + entries.AddResolvers( + &structs.ServiceResolverConfigEntry{ + Kind: "service-resolver", + Name: "main", + Redirect: &structs.ServiceResolverRedirect{ + Service: "other", + Peer: "cluster-01", + }, + }, + ) + + expect := &structs.CompiledDiscoveryChain{ + Protocol: "http", + StartNode: "resolver:other.default.default.external.cluster-01", + Nodes: map[string]*structs.DiscoveryGraphNode{ + "resolver:other.default.default.external.cluster-01": { + Type: structs.DiscoveryGraphNodeTypeResolver, + Name: "other.default.default.external.cluster-01", + Resolver: &structs.DiscoveryResolver{ + Default: true, + ConnectTimeout: 5 * time.Second, + Target: "other.default.default.external.cluster-01", + }, + }, + }, + Targets: map[string]*structs.DiscoveryTarget{ + "other.default.default.external.cluster-01": newTarget(structs.DiscoveryTargetOpts{ + Service: "other", + Peer: "cluster-01", + }, func(t *structs.DiscoveryTarget) { + t.SNI = "" + t.Name = "" + t.Datacenter = "" + }), + }, + } + return compileTestCase{entries: entries, expect: expect} +} + +func testcase_PeerRedirectSvcDefHTTP() compileTestCase { + entries := newEntries() + entries.AddServices( + &structs.ServiceConfigEntry{ + Kind: structs.ServiceDefaults, + Name: "main", + Protocol: "http", + }, + ) + entries.AddResolvers( + &structs.ServiceResolverConfigEntry{ + Kind: "service-resolver", + Name: "main", + Redirect: &structs.ServiceResolverRedirect{ + Service: "other", + Peer: "cluster-01", + }, + }, + ) + + expect := &structs.CompiledDiscoveryChain{ + Protocol: "http", + StartNode: "resolver:other.default.default.external.cluster-01", + Nodes: map[string]*structs.DiscoveryGraphNode{ + "resolver:other.default.default.external.cluster-01": { + Type: structs.DiscoveryGraphNodeTypeResolver, + Name: "other.default.default.external.cluster-01", + Resolver: &structs.DiscoveryResolver{ + Default: true, + ConnectTimeout: 5 * time.Second, + Target: "other.default.default.external.cluster-01", + }, + }, + }, + Targets: map[string]*structs.DiscoveryTarget{ + "other.default.default.external.cluster-01": newTarget(structs.DiscoveryTargetOpts{ + Service: "other", + Peer: "cluster-01", + }, func(t *structs.DiscoveryTarget) { + t.SNI = "" + t.Name = "" + t.Datacenter = "" + }), + }, + } + return compileTestCase{entries: entries, expect: expect} +} + func testcase_DatacenterRedirect_WithMeshGateways() compileTestCase { entries := newEntries() entries.AddProxyDefaults(&structs.ProxyConfigEntry{