mesh: compute more of the xRoute features into ComputedRoutes (#18980)

Convert more of the xRoutes features that were skipped in an earlier PR into ComputedRoutes and make them work:

- DestinationPolicy defaults
- more timeouts
- load balancer policy
- request/response header mutations
- urlrewrite
- GRPCRoute matches
This commit is contained in:
R.B. Boyer 2023-09-22 16:13:24 -05:00 committed by GitHub
parent d3bb5ff21a
commit 9e48607893
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 880 additions and 335 deletions

View File

@ -11,8 +11,6 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1"
svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing" svctest "github.com/hashicorp/consul/agent/grpc-external/services/resource/testing"
"github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/catalog"
"github.com/hashicorp/consul/internal/controller" "github.com/hashicorp/consul/internal/controller"
@ -20,6 +18,7 @@ import (
"github.com/hashicorp/consul/internal/resource" "github.com/hashicorp/consul/internal/resource"
rtest "github.com/hashicorp/consul/internal/resource/resourcetest" rtest "github.com/hashicorp/consul/internal/resource/resourcetest"
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1" pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1"
"github.com/hashicorp/consul/proto-public/pbresource" "github.com/hashicorp/consul/proto-public/pbresource"
"github.com/hashicorp/consul/proto/private/prototest" "github.com/hashicorp/consul/proto/private/prototest"
"github.com/hashicorp/consul/sdk/testutil" "github.com/hashicorp/consul/sdk/testutil"
@ -112,9 +111,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_TCP, Protocol: pbcatalog.Protocol_PROTOCOL_TCP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("api", "tcp"): { backendName("api", "tcp"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(apiServiceRef, "tcp", ""), BackendRef: newBackendRef(apiServiceRef, "tcp", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -184,9 +184,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_TCP, Protocol: pbcatalog.Protocol_PROTOCOL_TCP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("api", "tcp"): { backendName("api", "tcp"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(apiServiceRef, "tcp", ""), BackendRef: newBackendRef(apiServiceRef, "tcp", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -211,9 +212,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("api", "http"): { backendName("api", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(apiServiceRef, "http", ""), BackendRef: newBackendRef(apiServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -238,9 +240,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("api", "http2"): { backendName("api", "http2"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(apiServiceRef, "http2", ""), BackendRef: newBackendRef(apiServiceRef, "http2", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -260,9 +263,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_GRPC, Protocol: pbcatalog.Protocol_PROTOCOL_GRPC,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("api", "grpc"): { backendName("api", "grpc"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(apiServiceRef, "grpc", ""), BackendRef: newBackendRef(apiServiceRef, "grpc", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -347,9 +351,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_TCP, Protocol: pbcatalog.Protocol_PROTOCOL_TCP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "tcp"): { backendName("foo", "tcp"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "tcp", ""), BackendRef: newBackendRef(fooServiceRef, "tcp", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -376,9 +381,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "http"): { backendName("foo", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "http", ""), BackendRef: newBackendRef(fooServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -405,9 +411,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_GRPC, Protocol: pbcatalog.Protocol_PROTOCOL_GRPC,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "grpc"): { backendName("foo", "grpc"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "grpc", ""), BackendRef: newBackendRef(fooServiceRef, "grpc", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -434,9 +441,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "http2"): { backendName("foo", "http2"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "http2", ""), BackendRef: newBackendRef(fooServiceRef, "http2", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -548,9 +556,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_TCP, Protocol: pbcatalog.Protocol_PROTOCOL_TCP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "tcp"): { backendName("foo", "tcp"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "tcp", ""), BackendRef: newBackendRef(fooServiceRef, "tcp", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -588,9 +597,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "http"): { backendName("foo", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "http", ""), BackendRef: newBackendRef(fooServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -629,9 +639,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_GRPC, Protocol: pbcatalog.Protocol_PROTOCOL_GRPC,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "grpc"): { backendName("foo", "grpc"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "grpc", ""), BackendRef: newBackendRef(fooServiceRef, "grpc", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -669,9 +680,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "http2"): { backendName("foo", "http2"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "http2", ""), BackendRef: newBackendRef(fooServiceRef, "http2", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -786,9 +798,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_TCP, Protocol: pbcatalog.Protocol_PROTOCOL_TCP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "tcp"): { backendName("foo", "tcp"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "tcp", ""), BackendRef: newBackendRef(fooServiceRef, "tcp", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -815,9 +828,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "http"): { backendName("foo", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "http", ""), BackendRef: newBackendRef(fooServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -844,9 +858,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_GRPC, Protocol: pbcatalog.Protocol_PROTOCOL_GRPC,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "grpc"): { backendName("foo", "grpc"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "grpc", ""), BackendRef: newBackendRef(fooServiceRef, "grpc", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -873,9 +888,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "http2"): { backendName("foo", "http2"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "http2", ""), BackendRef: newBackendRef(fooServiceRef, "http2", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -952,9 +968,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_TCP, Protocol: pbcatalog.Protocol_PROTOCOL_TCP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "tcp"): { backendName("foo", "tcp"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "tcp", ""), BackendRef: newBackendRef(fooServiceRef, "tcp", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -981,9 +998,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "http"): { backendName("foo", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "http", ""), BackendRef: newBackendRef(fooServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -1010,9 +1028,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_GRPC, Protocol: pbcatalog.Protocol_PROTOCOL_GRPC,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "grpc"): { backendName("foo", "grpc"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "grpc", ""), BackendRef: newBackendRef(fooServiceRef, "grpc", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -1039,9 +1058,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "http2"): { backendName("foo", "http2"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "http2", ""), BackendRef: newBackendRef(fooServiceRef, "http2", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -1166,9 +1186,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("api", "http"): { backendName("api", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(apiServiceRef, "http", ""), BackendRef: newBackendRef(apiServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -1204,9 +1225,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("api", "http"): { backendName("api", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(apiServiceRef, "http", ""), BackendRef: newBackendRef(apiServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -1260,9 +1282,10 @@ func (suite *controllerSuite) TestController() {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("bar", "http"): { backendName("bar", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(barServiceRef, "http", ""), BackendRef: newBackendRef(barServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },

View File

@ -5,6 +5,10 @@ package routes
import ( import (
"fmt" "fmt"
"time"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/durationpb"
"github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/catalog"
"github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/loader" "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/loader"
@ -334,6 +338,72 @@ func compile(
details.DestinationConfig = portDestConfig details.DestinationConfig = portDestConfig
} }
} }
details.DestinationConfig = fillInDefaultDestConfig(details.DestinationConfig)
}
// Pull target information up to the level of the rules.
switch x := mc.Config.(type) {
case *pbmesh.ComputedPortRoutes_Http:
route := x.Http
for _, rule := range route.Rules {
// If there are multiple legs (split) then choose the first actually set value.
var requestTimeoutFallback *durationpb.Duration
for _, backendRef := range rule.BackendRefs {
if backendRef.BackendTarget == types.NullRouteBackend {
continue
}
details, ok := mc.Targets[backendRef.BackendTarget]
if !ok {
continue
}
if details.DestinationConfig.RequestTimeout != nil {
requestTimeoutFallback = details.DestinationConfig.RequestTimeout
break
}
}
if requestTimeoutFallback == nil {
continue // nothing to do
}
if rule.Timeouts == nil {
rule.Timeouts = &pbmesh.HTTPRouteTimeouts{}
}
if rule.Timeouts.Request == nil {
rule.Timeouts.Request = requestTimeoutFallback
}
}
case *pbmesh.ComputedPortRoutes_Grpc:
route := x.Grpc
for _, rule := range route.Rules {
// If there are multiple legs (split) then choose the first actually set value.
var requestTimeoutFallback *durationpb.Duration
for _, backendRef := range rule.BackendRefs {
if backendRef.BackendTarget == types.NullRouteBackend {
continue
}
details, ok := mc.Targets[backendRef.BackendTarget]
if !ok {
continue
}
if details.DestinationConfig.RequestTimeout != nil {
requestTimeoutFallback = details.DestinationConfig.RequestTimeout
break
}
}
if requestTimeoutFallback == nil {
continue // nothing to do
}
if rule.Timeouts == nil {
rule.Timeouts = &pbmesh.HTTPRouteTimeouts{}
}
if rule.Timeouts.Request == nil {
rule.Timeouts.Request = requestTimeoutFallback
}
}
case *pbmesh.ComputedPortRoutes_Tcp:
} }
computedRoutes.PortedConfigs[port] = mc computedRoutes.PortedConfigs[port] = mc
@ -412,6 +482,28 @@ func compileFailoverConfig(
return cfc return cfc
} }
func fillInDefaultDestConfig(target *pbmesh.DestinationConfig) *pbmesh.DestinationConfig {
base := defaultDestConfig()
if target == nil {
return proto.Clone(base).(*pbmesh.DestinationConfig)
}
out := proto.Clone(target).(*pbmesh.DestinationConfig)
if out.ConnectTimeout == nil {
out.ConnectTimeout = base.GetConnectTimeout()
}
return out
}
func defaultDestConfig() *pbmesh.DestinationConfig {
return &pbmesh.DestinationConfig{
ConnectTimeout: durationpb.New(5 * time.Second),
}
}
func compileHTTPRouteNode( func compileHTTPRouteNode(
port string, port string,
res *pbresource.Resource, res *pbresource.Resource,

View File

@ -222,9 +222,10 @@ func TestGenerateComputedRoutes(t *testing.T) {
Protocol: pbcatalog.Protocol_PROTOCOL_TCP, Protocol: pbcatalog.Protocol_PROTOCOL_TCP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("api", "tcp"): { backendName("api", "tcp"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(apiServiceRef, "tcp", ""), BackendRef: newBackendRef(apiServiceRef, "tcp", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -282,9 +283,10 @@ func TestGenerateComputedRoutes(t *testing.T) {
Protocol: protocol, Protocol: protocol,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("api", protoName): { backendName("api", protoName): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(apiServiceRef, protoName, ""), BackendRef: newBackendRef(apiServiceRef, protoName, ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -334,9 +336,10 @@ func TestGenerateComputedRoutes(t *testing.T) {
Protocol: pbcatalog.Protocol_PROTOCOL_GRPC, Protocol: pbcatalog.Protocol_PROTOCOL_GRPC,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("api", "grpc"): { backendName("api", "grpc"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(apiServiceRef, "grpc", ""), BackendRef: newBackendRef(apiServiceRef, "grpc", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -443,9 +446,10 @@ func TestGenerateComputedRoutes(t *testing.T) {
Protocol: pbcatalog.Protocol_PROTOCOL_TCP, Protocol: pbcatalog.Protocol_PROTOCOL_TCP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "tcp"): { backendName("foo", "tcp"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "tcp", ""), BackendRef: newBackendRef(fooServiceRef, "tcp", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -472,9 +476,10 @@ func TestGenerateComputedRoutes(t *testing.T) {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "http"): { backendName("foo", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "http", ""), BackendRef: newBackendRef(fooServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -501,9 +506,10 @@ func TestGenerateComputedRoutes(t *testing.T) {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP2,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "http2"): { backendName("foo", "http2"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "http2", ""), BackendRef: newBackendRef(fooServiceRef, "http2", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -530,9 +536,10 @@ func TestGenerateComputedRoutes(t *testing.T) {
Protocol: pbcatalog.Protocol_PROTOCOL_GRPC, Protocol: pbcatalog.Protocol_PROTOCOL_GRPC,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "grpc"): { backendName("foo", "grpc"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "grpc", ""), BackendRef: newBackendRef(fooServiceRef, "grpc", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -612,9 +619,10 @@ func TestGenerateComputedRoutes(t *testing.T) {
Protocol: protocol, Protocol: protocol,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", portName): { backendName("foo", portName): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, portName, ""), BackendRef: newBackendRef(fooServiceRef, portName, ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
} }
@ -721,9 +729,10 @@ func TestGenerateComputedRoutes(t *testing.T) {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("api", "http"): { backendName("api", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(apiServiceRef, "http", ""), BackendRef: newBackendRef(apiServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -803,9 +812,10 @@ func TestGenerateComputedRoutes(t *testing.T) {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("api", "http"): { backendName("api", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(apiServiceRef, "http", ""), BackendRef: newBackendRef(apiServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -910,9 +920,10 @@ func TestGenerateComputedRoutes(t *testing.T) {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "http"): { backendName("foo", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "http", ""), BackendRef: newBackendRef(fooServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -1062,14 +1073,16 @@ func TestGenerateComputedRoutes(t *testing.T) {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "http"): { backendName("foo", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "http", ""), BackendRef: newBackendRef(fooServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
backendName("bar", "http"): { backendName("bar", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(barServiceRef, "http", ""), BackendRef: newBackendRef(barServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -1155,9 +1168,10 @@ func TestGenerateComputedRoutes(t *testing.T) {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "http"): { backendName("foo", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "http", ""), BackendRef: newBackendRef(fooServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },
@ -1648,15 +1662,17 @@ func TestGenerateComputedRoutes(t *testing.T) {
Protocol: pbcatalog.Protocol_PROTOCOL_HTTP, Protocol: pbcatalog.Protocol_PROTOCOL_HTTP,
Targets: map[string]*pbmesh.BackendTargetDetails{ Targets: map[string]*pbmesh.BackendTargetDetails{
backendName("foo", "http"): { backendName("foo", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(fooServiceRef, "http", ""), BackendRef: newBackendRef(fooServiceRef, "http", ""),
FailoverConfig: portFailoverConfig, FailoverConfig: portFailoverConfig,
DestinationConfig: defaultDestConfig(),
}, },
backendName("bar", "http"): { backendName("bar", "http"): {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_INDIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_INDIRECT,
MeshPort: "mesh", MeshPort: "mesh",
BackendRef: newBackendRef(barServiceRef, "http", ""), BackendRef: newBackendRef(barServiceRef, "http", ""),
DestinationConfig: defaultDestConfig(),
}, },
}, },
}, },

View File

@ -5,19 +5,16 @@ package routes
import ( import (
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"github.com/hashicorp/consul/internal/protoutil"
) )
// Deprecated: see protoutil.Clone
func protoClone[T proto.Message](v T) T { func protoClone[T proto.Message](v T) T {
return proto.Clone(v).(T) return protoutil.Clone(v)
} }
// Deprecated: see protoutil.CloneSlice
func protoSliceClone[T proto.Message](in []T) []T { func protoSliceClone[T proto.Message](in []T) []T {
if in == nil { return protoutil.CloneSlice(in)
return nil
}
out := make([]T, 0, len(in))
for _, v := range in {
out = append(out, protoClone[T](v))
}
return out
} }

View File

@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"time" "time"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/wrapperspb" "google.golang.org/protobuf/types/known/wrapperspb"
@ -15,6 +14,7 @@ import (
"github.com/hashicorp/consul/envoyextensions/xdscommon" "github.com/hashicorp/consul/envoyextensions/xdscommon"
"github.com/hashicorp/consul/internal/mesh/internal/types" "github.com/hashicorp/consul/internal/mesh/internal/types"
"github.com/hashicorp/consul/internal/mesh/internal/types/intermediate" "github.com/hashicorp/consul/internal/mesh/internal/types/intermediate"
"github.com/hashicorp/consul/internal/protoutil"
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1" pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1" pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1"
"github.com/hashicorp/consul/proto-public/pbmesh/v2beta1/pbproxystate" "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1/pbproxystate"
@ -109,6 +109,8 @@ func (b *Builder) buildDestination(
} }
} }
destConfig := b.makeDestinationConfiguration(routeRule.Timeouts, routeRule.Retries) destConfig := b.makeDestinationConfiguration(routeRule.Timeouts, routeRule.Retries)
headerMutations := applyRouteFilters(destConfig, routeRule.Filters)
applyLoadBalancerPolicy(destConfig, cpr, routeRule.BackendRefs)
dest := b.makeHTTPRouteDestination( dest := b.makeHTTPRouteDestination(
routeRule.BackendRefs, routeRule.BackendRefs,
@ -117,17 +119,14 @@ func (b *Builder) buildDestination(
defaultDC, defaultDC,
) )
// TODO(rb/v2): Filters []*HTTPRouteFilter
// TODO(rb/v2): BackendRefs []*ComputedHTTPBackendRef
// Explode out by matches // Explode out by matches
for _, match := range routeRule.Matches { for _, match := range routeRule.Matches {
routeMatch := makeHTTPRouteMatch(match) routeMatch := makeHTTPRouteMatch(match)
proxyRouteRules = append(proxyRouteRules, &pbproxystate.RouteRule{ proxyRouteRules = append(proxyRouteRules, &pbproxystate.RouteRule{
Match: routeMatch, Match: routeMatch,
Destination: proto.Clone(dest).(*pbproxystate.RouteDestination), Destination: protoutil.Clone(dest),
// TODO: mutations HeaderMutations: protoutil.CloneSlice(headerMutations),
}) })
} }
} }
@ -153,6 +152,8 @@ func (b *Builder) buildDestination(
} }
} }
destConfig := b.makeDestinationConfiguration(routeRule.Timeouts, routeRule.Retries) destConfig := b.makeDestinationConfiguration(routeRule.Timeouts, routeRule.Retries)
headerMutations := applyRouteFilters(destConfig, routeRule.Filters)
applyLoadBalancerPolicy(destConfig, cpr, routeRule.BackendRefs)
// nolint:staticcheck // nolint:staticcheck
dest := b.makeGRPCRouteDestination( dest := b.makeGRPCRouteDestination(
@ -162,16 +163,14 @@ func (b *Builder) buildDestination(
defaultDC, defaultDC,
) )
// TODO(rb/v2): Filters []*HTTPRouteFilter
// Explode out by matches // Explode out by matches
for _, match := range routeRule.Matches { for _, match := range routeRule.Matches {
routeMatch := makeGRPCRouteMatch(match) routeMatch := makeGRPCRouteMatch(match)
proxyRouteRules = append(proxyRouteRules, &pbproxystate.RouteRule{ proxyRouteRules = append(proxyRouteRules, &pbproxystate.RouteRule{
Match: routeMatch, Match: routeMatch,
Destination: proto.Clone(dest).(*pbproxystate.RouteDestination), Destination: protoutil.Clone(dest),
// TODO: mutations HeaderMutations: protoutil.CloneSlice(headerMutations),
}) })
} }
} }
@ -267,7 +266,8 @@ func (b *Builder) buildDestination(
continue continue
} }
connectTimeout := durationpb.New(5 * time.Second) connectTimeout := details.DestinationConfig.ConnectTimeout
loadBalancer := details.DestinationConfig.LoadBalancer
// NOTE: we collect both DIRECT and INDIRECT target information here. // NOTE: we collect both DIRECT and INDIRECT target information here.
dc := defaultDC(details.BackendRef.Datacenter) dc := defaultDC(details.BackendRef.Datacenter)
@ -280,7 +280,7 @@ func (b *Builder) buildDestination(
) )
clusterName := fmt.Sprintf("%s.%s", portName, sni) clusterName := fmt.Sprintf("%s.%s", portName, sni)
egBase := b.newClusterEndpointGroup("", sni, portName, details.IdentityRefs, connectTimeout) egBase := b.newClusterEndpointGroup("", sni, portName, details.IdentityRefs, connectTimeout, loadBalancer)
var endpointGroups []*pbproxystate.EndpointGroup var endpointGroups []*pbproxystate.EndpointGroup
@ -301,6 +301,9 @@ func (b *Builder) buildDestination(
continue // not possible continue // not possible
} }
destConnectTimeout := destDetails.DestinationConfig.ConnectTimeout
destLoadBalancer := destDetails.DestinationConfig.LoadBalancer
destDC := defaultDC(destDetails.BackendRef.Datacenter) destDC := defaultDC(destDetails.BackendRef.Datacenter)
destPortName := destDetails.BackendRef.Port destPortName := destDetails.BackendRef.Port
@ -311,7 +314,7 @@ func (b *Builder) buildDestination(
) )
destClusterName := fmt.Sprintf("%s%d~%s", xdscommon.FailoverClusterNamePrefix, i, clusterName) destClusterName := fmt.Sprintf("%s%d~%s", xdscommon.FailoverClusterNamePrefix, i, clusterName)
egDest := b.newClusterEndpointGroup(destClusterName, destSNI, destPortName, destDetails.IdentityRefs, connectTimeout) egDest := b.newClusterEndpointGroup(destClusterName, destSNI, destPortName, destDetails.IdentityRefs, destConnectTimeout, destLoadBalancer)
endpointGroups = append(endpointGroups, egDest) endpointGroups = append(endpointGroups, egDest)
b.addEndpointsRef(destClusterName, destDetails.ServiceEndpointsId, destDetails.MeshPort) b.addEndpointsRef(destClusterName, destDetails.ServiceEndpointsId, destDetails.MeshPort)
@ -389,7 +392,7 @@ func (b *ListenerBuilder) addL4RouterForSplit(
}, },
}, },
StatPrefix: statPrefix, StatPrefix: statPrefix,
// TODO: can we use RDS for TCPRoute split? // TODO(rb/v2): can we use RDS for TCPRoute split?
}, },
} }
@ -565,24 +568,71 @@ func (b *Builder) newClusterEndpointGroup(
portName string, portName string,
destinationIdentities []*pbresource.Reference, destinationIdentities []*pbresource.Reference,
connectTimeout *durationpb.Duration, connectTimeout *durationpb.Duration,
loadBalancer *pbmesh.LoadBalancer,
) *pbproxystate.EndpointGroup { ) *pbproxystate.EndpointGroup {
var spiffeIDs []string var spiffeIDs []string
for _, identity := range destinationIdentities { for _, identity := range destinationIdentities {
spiffeIDs = append(spiffeIDs, connect.SpiffeIDFromIdentityRef(b.trustDomain, identity)) spiffeIDs = append(spiffeIDs, connect.SpiffeIDFromIdentityRef(b.trustDomain, identity))
} }
// TODO(v2): DestinationPolicy: connect timeout, lb policy, cluster discovery type, circuit breakers, outlier detection // TODO(v2): DestinationPolicy: circuit breakers, outlier detection
// TODO(v2): if http2/grpc then set http2protocol options // TODO(v2): if http2/grpc then set http2protocol options
degConfig := &pbproxystate.DynamicEndpointGroupConfig{
DisablePanicThreshold: true,
ConnectTimeout: connectTimeout,
}
if loadBalancer != nil {
// enumcover:pbmesh.LoadBalancerPolicy
switch loadBalancer.Policy {
case pbmesh.LoadBalancerPolicy_LOAD_BALANCER_POLICY_RANDOM:
degConfig.LbPolicy = &pbproxystate.DynamicEndpointGroupConfig_Random{}
case pbmesh.LoadBalancerPolicy_LOAD_BALANCER_POLICY_ROUND_ROBIN:
degConfig.LbPolicy = &pbproxystate.DynamicEndpointGroupConfig_RoundRobin{}
case pbmesh.LoadBalancerPolicy_LOAD_BALANCER_POLICY_LEAST_REQUEST:
var choiceCount uint32
cfg, ok := loadBalancer.Config.(*pbmesh.LoadBalancer_LeastRequestConfig)
if ok {
choiceCount = cfg.LeastRequestConfig.GetChoiceCount()
}
degConfig.LbPolicy = &pbproxystate.DynamicEndpointGroupConfig_LeastRequest{
LeastRequest: &pbproxystate.LBPolicyLeastRequest{
ChoiceCount: wrapperspb.UInt32(choiceCount),
},
}
case pbmesh.LoadBalancerPolicy_LOAD_BALANCER_POLICY_MAGLEV:
degConfig.LbPolicy = &pbproxystate.DynamicEndpointGroupConfig_Maglev{}
case pbmesh.LoadBalancerPolicy_LOAD_BALANCER_POLICY_RING_HASH:
policy := &pbproxystate.DynamicEndpointGroupConfig_RingHash{}
cfg, ok := loadBalancer.Config.(*pbmesh.LoadBalancer_RingHashConfig)
if ok {
policy.RingHash = &pbproxystate.LBPolicyRingHash{
MinimumRingSize: wrapperspb.UInt64(cfg.RingHashConfig.MinimumRingSize),
MaximumRingSize: wrapperspb.UInt64(cfg.RingHashConfig.MaximumRingSize),
}
}
degConfig.LbPolicy = policy
case pbmesh.LoadBalancerPolicy_LOAD_BALANCER_POLICY_UNSPECIFIED:
// fallthrough to default
default:
// do nothing
}
}
return &pbproxystate.EndpointGroup{ return &pbproxystate.EndpointGroup{
Name: clusterName, Name: clusterName,
Group: &pbproxystate.EndpointGroup_Dynamic{ Group: &pbproxystate.EndpointGroup_Dynamic{
Dynamic: &pbproxystate.DynamicEndpointGroup{ Dynamic: &pbproxystate.DynamicEndpointGroup{
Config: &pbproxystate.DynamicEndpointGroupConfig{ Config: degConfig,
DisablePanicThreshold: true,
ConnectTimeout: connectTimeout,
},
OutboundTls: &pbproxystate.TransportSocket{ OutboundTls: &pbproxystate.TransportSocket{
ConnectionTls: &pbproxystate.TransportSocket_OutboundMesh{ ConnectionTls: &pbproxystate.TransportSocket_OutboundMesh{
OutboundMesh: &pbproxystate.OutboundMeshMTLS{ OutboundMesh: &pbproxystate.OutboundMeshMTLS{

View File

@ -5,8 +5,11 @@ package builder
import ( import (
"testing" "testing"
"time"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/wrapperspb"
"github.com/hashicorp/consul/internal/catalog" "github.com/hashicorp/consul/internal/catalog"
"github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/routestest" "github.com/hashicorp/consul/internal/mesh/internal/controllers/routes/routestest"
@ -130,6 +133,19 @@ func TestBuildExplicitDestinations(t *testing.T) {
Tenancy: backup1Endpoints.Id.Tenancy, Tenancy: backup1Endpoints.Id.Tenancy,
} }
api1DestPolicy := resourcetest.Resource(types.DestinationPolicyType, api1Service.Id.Name).
WithTenancy(api1Service.Id.GetTenancy()).
WithData(t, &pbmesh.DestinationPolicy{
PortConfigs: map[string]*pbmesh.DestinationConfig{
"http": {
ConnectTimeout: durationpb.New(55 * time.Second),
RequestTimeout: durationpb.New(77 * time.Second),
// LoadBalancer *LoadBalancer `protobuf:"bytes,3,opt,name=load_balancer,json=loadBalancer,proto3" json:"load_balancer,omitempty"`
},
},
}).
Build()
api1HTTPRoute := resourcetest.Resource(types.HTTPRouteType, "api-1-http-route"). api1HTTPRoute := resourcetest.Resource(types.HTTPRouteType, "api-1-http-route").
WithTenancy(resource.DefaultNamespacedTenancy()). WithTenancy(resource.DefaultNamespacedTenancy()).
WithData(t, &pbmesh.HTTPRoute{ WithData(t, &pbmesh.HTTPRoute{
@ -137,28 +153,56 @@ func TestBuildExplicitDestinations(t *testing.T) {
Ref: resource.Reference(api1Service.Id, ""), Ref: resource.Reference(api1Service.Id, ""),
Port: "http", Port: "http",
}}, }},
Rules: []*pbmesh.HTTPRouteRule{{ Rules: []*pbmesh.HTTPRouteRule{
BackendRefs: []*pbmesh.HTTPBackendRef{ {
{ Matches: []*pbmesh.HTTPRouteMatch{{
BackendRef: &pbmesh.BackendReference{ Path: &pbmesh.HTTPPathMatch{
Ref: resource.Reference(api2Service.Id, ""), Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX,
Value: "/split",
},
}},
BackendRefs: []*pbmesh.HTTPBackendRef{
{
BackendRef: &pbmesh.BackendReference{
Ref: resource.Reference(api2Service.Id, ""),
},
Weight: 60,
},
{
BackendRef: &pbmesh.BackendReference{
Ref: resource.Reference(api1Service.Id, ""),
},
Weight: 40,
},
{
BackendRef: &pbmesh.BackendReference{
Ref: resource.Reference(api3Service.Id, ""),
},
Weight: 10,
}, },
Weight: 60,
}, },
{ },
{
Matches: []*pbmesh.HTTPRouteMatch{{
Path: &pbmesh.HTTPPathMatch{
Type: pbmesh.PathMatchType_PATH_MATCH_TYPE_PREFIX,
Value: "/",
},
}},
BackendRefs: []*pbmesh.HTTPBackendRef{{
BackendRef: &pbmesh.BackendReference{ BackendRef: &pbmesh.BackendReference{
Ref: resource.Reference(api1Service.Id, ""), Ref: resource.Reference(api1Service.Id, ""),
}, },
Weight: 40, }},
Timeouts: &pbmesh.HTTPRouteTimeouts{
Request: durationpb.New(606 * time.Second), // differnet than the 77s
}, },
{ Retries: &pbmesh.HTTPRouteRetries{
BackendRef: &pbmesh.BackendReference{ Number: wrapperspb.UInt32(4),
Ref: resource.Reference(api3Service.Id, ""), OnConnectFailure: true,
},
Weight: 10,
}, },
}, },
}}, },
}). }).
Build() Build()
resourcetest.ValidateAndNormalize(t, registry, api1HTTPRoute) resourcetest.ValidateAndNormalize(t, registry, api1HTTPRoute)
@ -249,6 +293,7 @@ func TestBuildExplicitDestinations(t *testing.T) {
resourcetest.MustDecode[*pbcatalog.Service](t, api2Service), resourcetest.MustDecode[*pbcatalog.Service](t, api2Service),
resourcetest.MustDecode[*pbcatalog.Service](t, backup1Service), resourcetest.MustDecode[*pbcatalog.Service](t, backup1Service),
// notably we do NOT include api3Service here so we trigger a null route to be generated // notably we do NOT include api3Service here so we trigger a null route to be generated
resourcetest.MustDecode[*pbmesh.DestinationPolicy](t, api1DestPolicy),
resourcetest.MustDecode[*pbmesh.HTTPRoute](t, api1HTTPRoute), resourcetest.MustDecode[*pbmesh.HTTPRoute](t, api1HTTPRoute),
resourcetest.MustDecode[*pbmesh.TCPRoute](t, api1TCPRoute), resourcetest.MustDecode[*pbmesh.TCPRoute](t, api1TCPRoute),
resourcetest.MustDecode[*pbcatalog.FailoverPolicy](t, api1FailoverPolicy), resourcetest.MustDecode[*pbcatalog.FailoverPolicy](t, api1FailoverPolicy),

View File

@ -6,7 +6,9 @@ package builder
import ( import (
"fmt" "fmt"
"strings" "strings"
"time"
"google.golang.org/protobuf/types/known/durationpb"
"google.golang.org/protobuf/types/known/wrapperspb" "google.golang.org/protobuf/types/known/wrapperspb"
"github.com/hashicorp/consul/internal/mesh/internal/types" "github.com/hashicorp/consul/internal/mesh/internal/types"
@ -131,8 +133,6 @@ func (b *Builder) makeDestinationConfiguration(
timeouts *pbmesh.HTTPRouteTimeouts, timeouts *pbmesh.HTTPRouteTimeouts,
retries *pbmesh.HTTPRouteRetries, retries *pbmesh.HTTPRouteRetries,
) *pbproxystate.DestinationConfiguration { ) *pbproxystate.DestinationConfiguration {
// TODO: prefix rewrite, lb config
cfg := &pbproxystate.DestinationConfiguration{ cfg := &pbproxystate.DestinationConfiguration{
TimeoutConfig: translateTimeouts(timeouts), TimeoutConfig: translateTimeouts(timeouts),
RetryPolicy: translateRetries(retries), RetryPolicy: translateRetries(retries),
@ -144,8 +144,202 @@ func (b *Builder) makeDestinationConfiguration(
return cfg return cfg
} }
func makeGRPCRouteMatch(match *pbmesh.GRPCRouteMatch) *pbproxystate.RouteMatch { func applyRouteFilters[V interface {
panic("TODO") GetRequestHeaderModifier() *pbmesh.HTTPHeaderFilter
GetResponseHeaderModifier() *pbmesh.HTTPHeaderFilter
GetUrlRewrite() *pbmesh.HTTPURLRewriteFilter
}](
psDestConfig *pbproxystate.DestinationConfiguration,
filters []V,
) []*pbproxystate.HeaderMutation {
var headerMutations []*pbproxystate.HeaderMutation
for _, filter := range filters {
switch {
case filter.GetRequestHeaderModifier() != nil:
mod := filter.GetRequestHeaderModifier()
for _, hdr := range mod.Set {
headerMutations = append(headerMutations, &pbproxystate.HeaderMutation{
Action: &pbproxystate.HeaderMutation_RequestHeaderAdd{
RequestHeaderAdd: &pbproxystate.RequestHeaderAdd{
Header: &pbproxystate.Header{
Key: hdr.Name,
Value: hdr.Value,
},
AppendAction: pbproxystate.AppendAction_APPEND_ACTION_OVERWRITE_IF_EXISTS_OR_ADD,
},
},
})
}
for _, hdr := range mod.Add {
headerMutations = append(headerMutations, &pbproxystate.HeaderMutation{
Action: &pbproxystate.HeaderMutation_RequestHeaderAdd{
RequestHeaderAdd: &pbproxystate.RequestHeaderAdd{
Header: &pbproxystate.Header{
Key: hdr.Name,
Value: hdr.Value,
},
AppendAction: pbproxystate.AppendAction_APPEND_ACTION_APPEND_IF_EXISTS_OR_ADD,
},
},
})
}
if len(mod.Remove) > 0 {
headerMutations = append(headerMutations, &pbproxystate.HeaderMutation{
Action: &pbproxystate.HeaderMutation_RequestHeaderRemove{
RequestHeaderRemove: &pbproxystate.RequestHeaderRemove{
HeaderKeys: mod.Remove,
},
},
})
}
case filter.GetResponseHeaderModifier() != nil:
mod := filter.GetResponseHeaderModifier()
for _, hdr := range mod.Set {
headerMutations = append(headerMutations, &pbproxystate.HeaderMutation{
Action: &pbproxystate.HeaderMutation_ResponseHeaderAdd{
ResponseHeaderAdd: &pbproxystate.ResponseHeaderAdd{
Header: &pbproxystate.Header{
Key: hdr.Name,
Value: hdr.Value,
},
AppendAction: pbproxystate.AppendAction_APPEND_ACTION_OVERWRITE_IF_EXISTS_OR_ADD,
},
},
})
}
for _, hdr := range mod.Add {
headerMutations = append(headerMutations, &pbproxystate.HeaderMutation{
Action: &pbproxystate.HeaderMutation_ResponseHeaderAdd{
ResponseHeaderAdd: &pbproxystate.ResponseHeaderAdd{
Header: &pbproxystate.Header{
Key: hdr.Name,
Value: hdr.Value,
},
AppendAction: pbproxystate.AppendAction_APPEND_ACTION_APPEND_IF_EXISTS_OR_ADD,
},
},
})
}
if len(mod.Remove) > 0 {
headerMutations = append(headerMutations, &pbproxystate.HeaderMutation{
Action: &pbproxystate.HeaderMutation_ResponseHeaderRemove{
ResponseHeaderRemove: &pbproxystate.ResponseHeaderRemove{
HeaderKeys: mod.Remove,
},
},
})
}
case filter.GetUrlRewrite() != nil:
prefix := filter.GetUrlRewrite().PathPrefix
if prefix != "" {
psDestConfig.PrefixRewrite = prefix
}
}
}
return headerMutations
}
func applyLoadBalancerPolicy[V interface {
GetBackendTarget() string
}](
psDestConfig *pbproxystate.DestinationConfiguration,
cpr *pbmesh.ComputedPortRoutes,
backendRefs []V,
) {
var lb *pbmesh.LoadBalancer
// If there are multiple targets, just pick the lb policy from
// the first one configured.
for _, backendRef := range backendRefs {
if backendRef.GetBackendTarget() == types.NullRouteBackend {
continue
}
details, ok := cpr.Targets[backendRef.GetBackendTarget()]
if !ok {
continue
}
thisLB := details.DestinationConfig.LoadBalancer
if thisLB != nil {
lb = thisLB
break
}
}
if lb == nil {
return
}
for _, policy := range lb.HashPolicies {
if policy.SourceIp {
psDestConfig.HashPolicies = append(psDestConfig.HashPolicies, &pbproxystate.LoadBalancerHashPolicy{
Policy: &pbproxystate.LoadBalancerHashPolicy_ConnectionProperties{
ConnectionProperties: &pbproxystate.ConnectionPropertiesPolicy{
SourceIp: true,
Terminal: policy.Terminal,
},
},
})
continue
}
// enumcover:pbmesh.HashPolicyField
switch policy.Field {
case pbmesh.HashPolicyField_HASH_POLICY_FIELD_HEADER:
psDestConfig.HashPolicies = append(psDestConfig.HashPolicies, &pbproxystate.LoadBalancerHashPolicy{
Policy: &pbproxystate.LoadBalancerHashPolicy_Header{
Header: &pbproxystate.HeaderPolicy{
Name: policy.FieldValue,
Terminal: policy.Terminal,
},
},
})
case pbmesh.HashPolicyField_HASH_POLICY_FIELD_COOKIE:
cookie := &pbproxystate.CookiePolicy{
Name: policy.FieldValue,
Terminal: policy.Terminal,
}
if policy.CookieConfig != nil {
cookie.Path = policy.CookieConfig.Path
if policy.CookieConfig.Ttl != nil {
if policy.CookieConfig.Ttl.AsDuration() != 0 {
cookie.Ttl = policy.CookieConfig.Ttl
}
}
// Envoy will generate a session cookie if the ttl is present and zero.
if policy.CookieConfig.Session {
cookie.Ttl = durationpb.New(0 * time.Second)
}
}
psDestConfig.HashPolicies = append(psDestConfig.HashPolicies, &pbproxystate.LoadBalancerHashPolicy{
Policy: &pbproxystate.LoadBalancerHashPolicy_Cookie{
Cookie: cookie,
},
})
case pbmesh.HashPolicyField_HASH_POLICY_FIELD_QUERY_PARAMETER:
psDestConfig.HashPolicies = append(psDestConfig.HashPolicies, &pbproxystate.LoadBalancerHashPolicy{
Policy: &pbproxystate.LoadBalancerHashPolicy_QueryParameter{
QueryParameter: &pbproxystate.QueryParameterPolicy{
Name: policy.FieldValue,
Terminal: policy.Terminal,
},
},
})
case pbmesh.HashPolicyField_HASH_POLICY_FIELD_UNSPECIFIED:
// fallthrough to default
default:
// not possible from validation
}
}
} }
func makeHTTPRouteMatch(match *pbmesh.HTTPRouteMatch) *pbproxystate.RouteMatch { func makeHTTPRouteMatch(match *pbmesh.HTTPRouteMatch) *pbproxystate.RouteMatch {
@ -185,48 +379,7 @@ func makeHTTPRouteMatch(match *pbmesh.HTTPRouteMatch) *pbproxystate.RouteMatch {
} }
} }
if len(match.Headers) > 0 { em.HeaderMatches = translateHeaderMatches(match.Headers, (*pbmesh.HTTPHeaderMatch).GetInvert)
em.HeaderMatches = make([]*pbproxystate.HeaderMatch, 0, len(match.Headers))
for _, hdr := range match.Headers {
eh := &pbproxystate.HeaderMatch{
Name: hdr.Name,
}
// enumcover:pbmesh.HeaderMatchType
switch hdr.Type {
case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_EXACT:
eh.Match = &pbproxystate.HeaderMatch_Exact{
Exact: hdr.Value,
}
case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_REGEX:
eh.Match = &pbproxystate.HeaderMatch_Regex{
Regex: hdr.Value,
}
case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PREFIX:
eh.Match = &pbproxystate.HeaderMatch_Prefix{
Prefix: hdr.Value,
}
case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_SUFFIX:
eh.Match = &pbproxystate.HeaderMatch_Suffix{
Suffix: hdr.Value,
}
case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PRESENT:
eh.Match = &pbproxystate.HeaderMatch_Present{
Present: true,
}
case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_UNSPECIFIED:
fallthrough // to default
default:
panic(fmt.Sprintf("unknown header match type: %v", hdr.Type))
}
if hdr.Invert {
eh.InvertMatch = true
}
em.HeaderMatches = append(em.HeaderMatches, eh)
}
}
if match.Method != "" { if match.Method != "" {
em.MethodMatches = []string{match.Method} em.MethodMatches = []string{match.Method}
@ -267,6 +420,122 @@ func makeHTTPRouteMatch(match *pbmesh.HTTPRouteMatch) *pbproxystate.RouteMatch {
return em return em
} }
func makeGRPCRouteMatch(match *pbmesh.GRPCRouteMatch) *pbproxystate.RouteMatch {
em := &pbproxystate.RouteMatch{}
if match.Method != nil {
mm := match.Method
switch mm.Type {
case pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_EXACT:
switch {
case mm.Method == "":
em.PathMatch = &pbproxystate.PathMatch{
PathMatch: &pbproxystate.PathMatch_Prefix{
Prefix: fmt.Sprintf("/%s/", mm.Service),
},
}
case mm.Service == "":
em.PathMatch = &pbproxystate.PathMatch{
PathMatch: &pbproxystate.PathMatch_Regex{
Regex: fmt.Sprintf("/[^/]+/%s", mm.Method),
},
}
default:
em.PathMatch = &pbproxystate.PathMatch{
PathMatch: &pbproxystate.PathMatch_Exact{
Exact: fmt.Sprintf("/%s/%s", mm.Service, mm.Method),
},
}
}
case pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_REGEX:
switch {
case mm.Method == "":
em.PathMatch = &pbproxystate.PathMatch{
PathMatch: &pbproxystate.PathMatch_Regex{
Regex: fmt.Sprintf("/%s/.+", mm.Service),
},
}
case mm.Service == "":
em.PathMatch = &pbproxystate.PathMatch{
PathMatch: &pbproxystate.PathMatch_Regex{
Regex: fmt.Sprintf("/[^/]+/%s", mm.Method),
},
}
default:
em.PathMatch = &pbproxystate.PathMatch{
PathMatch: &pbproxystate.PathMatch_Regex{
Regex: fmt.Sprintf("/%s/%s", mm.Service, mm.Method),
},
}
}
case pbmesh.GRPCMethodMatchType_GRPC_METHOD_MATCH_TYPE_UNSPECIFIED:
fallthrough // to default
default:
panic(fmt.Sprintf("unknown method match type: %v", match.Method.Type))
}
}
em.HeaderMatches = translateHeaderMatches(match.Headers, nil)
return em
}
func translateHeaderMatches[V interface {
GetType() pbmesh.HeaderMatchType
GetName() string
GetValue() string
}](
headers []V,
getInvert func(v V) bool,
) []*pbproxystate.HeaderMatch {
if len(headers) == 0 {
return nil
}
var out []*pbproxystate.HeaderMatch
out = make([]*pbproxystate.HeaderMatch, 0, len(headers))
for _, hdr := range headers {
eh := &pbproxystate.HeaderMatch{
Name: hdr.GetName(),
}
// enumcover:pbmesh.HeaderMatchType
switch hdr.GetType() {
case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_EXACT:
eh.Match = &pbproxystate.HeaderMatch_Exact{
Exact: hdr.GetValue(),
}
case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_REGEX:
eh.Match = &pbproxystate.HeaderMatch_Regex{
Regex: hdr.GetValue(),
}
case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PREFIX:
eh.Match = &pbproxystate.HeaderMatch_Prefix{
Prefix: hdr.GetValue(),
}
case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_SUFFIX:
eh.Match = &pbproxystate.HeaderMatch_Suffix{
Suffix: hdr.GetValue(),
}
case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_PRESENT:
eh.Match = &pbproxystate.HeaderMatch_Present{
Present: true,
}
case pbmesh.HeaderMatchType_HEADER_MATCH_TYPE_UNSPECIFIED:
fallthrough // to default
default:
panic(fmt.Sprintf("unknown header match type: %v", hdr.GetType()))
}
// HTTPHeaderMatch only
if getInvert != nil && getInvert(hdr) {
eh.InvertMatch = true
}
out = append(out, eh)
}
return out
}
func translateTimeouts(timeouts *pbmesh.HTTPRouteTimeouts) *pbproxystate.TimeoutConfig { func translateTimeouts(timeouts *pbmesh.HTTPRouteTimeouts) *pbproxystate.TimeoutConfig {
if timeouts == nil || (timeouts.Request == nil && timeouts.Idle == nil) { if timeouts == nil || (timeouts.Request == nil && timeouts.Idle == nil) {
return nil return nil

View File

@ -5,14 +5,14 @@
"altStatName": "http.api-1.default.dc1.internal.foo.consul", "altStatName": "http.api-1.default.dc1.internal.foo.consul",
"failoverGroup": { "failoverGroup": {
"config": { "config": {
"connectTimeout": "5s", "connectTimeout": "55s",
"useAltStatName": true "useAltStatName": true
}, },
"endpointGroups": [ "endpointGroups": [
{ {
"dynamic": { "dynamic": {
"config": { "config": {
"connectTimeout": "5s", "connectTimeout": "55s",
"disablePanicThreshold": true "disablePanicThreshold": true
}, },
"outboundTls": { "outboundTls": {
@ -235,6 +235,11 @@
"routeRules": [ "routeRules": [
{ {
"destination": { "destination": {
"destinationConfiguration": {
"timeoutConfig": {
"timeout": "77s"
}
},
"weightedClusters": { "weightedClusters": {
"clusters": [ "clusters": [
{ {
@ -252,6 +257,27 @@
] ]
} }
}, },
"match": {
"pathMatch": {
"prefix": "/split"
}
}
},
{
"destination": {
"cluster": {
"name": "http.api-1.default.dc1.internal.foo.consul"
},
"destinationConfiguration": {
"retryPolicy": {
"numRetries": 4,
"retryOn": "connect-failure"
},
"timeoutConfig": {
"timeout": "606s"
}
}
},
"match": { "match": {
"pathMatch": { "pathMatch": {
"prefix": "/" "prefix": "/"

View File

@ -111,6 +111,36 @@ func ValidateComputedRoutes(res *pbresource.Resource) error {
)) ))
} }
if target.DestinationConfig == nil {
merr = multierror.Append(merr, wrapTargetErr(resource.ErrInvalidField{
Name: "destination_config",
Wrapped: resource.ErrMissing,
}))
} else {
wrapDestConfigErr := func(err error) error {
return wrapTargetErr(resource.ErrInvalidField{
Name: "destination_config",
Wrapped: err,
})
}
destConfig := target.DestinationConfig
if destConfig.ConnectTimeout == nil {
merr = multierror.Append(merr, wrapDestConfigErr(resource.ErrInvalidField{
Name: "connect_timeout",
Wrapped: resource.ErrMissing,
}))
} else {
connectTimeout := destConfig.ConnectTimeout.AsDuration()
if connectTimeout < 0 {
merr = multierror.Append(merr, wrapDestConfigErr(resource.ErrInvalidField{
Name: "connect_timeout",
Wrapped: errTimeoutCannotBeNegative(connectTimeout),
}))
}
}
}
if target.MeshPort == "" { if target.MeshPort == "" {
merr = multierror.Append(merr, wrapTargetErr(resource.ErrInvalidField{ merr = multierror.Append(merr, wrapTargetErr(resource.ErrInvalidField{
Name: "mesh_port", Name: "mesh_port",

View File

@ -5,8 +5,10 @@ package types
import ( import (
"testing" "testing"
"time"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/durationpb"
"github.com/hashicorp/consul/internal/resource/resourcetest" "github.com/hashicorp/consul/internal/resource/resourcetest"
pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1" pbcatalog "github.com/hashicorp/consul/proto-public/pbcatalog/v2beta1"
@ -197,6 +199,9 @@ func TestValidateComputedRoutes(t *testing.T) {
"foo": { "foo": {
Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT, Type: pbmesh.BackendTargetDetailsType_BACKEND_TARGET_DETAILS_TYPE_DIRECT,
MeshPort: "mesh", MeshPort: "mesh",
DestinationConfig: &pbmesh.DestinationConfig{
ConnectTimeout: durationpb.New(5 * time.Second),
},
}, },
}, },
}, },

View File

@ -249,6 +249,10 @@ func ValidateHTTPRoute(res *pbresource.Resource) error {
} }
} }
var (
hasReqMod bool
hasUrlRewrite bool
)
for j, filter := range rule.Filters { for j, filter := range rule.Filters {
wrapFilterErr := func(err error) error { wrapFilterErr := func(err error) error {
return wrapRuleErr(resource.ErrInvalidListElement{ return wrapRuleErr(resource.ErrInvalidListElement{
@ -260,12 +264,14 @@ func ValidateHTTPRoute(res *pbresource.Resource) error {
set := 0 set := 0
if filter.RequestHeaderModifier != nil { if filter.RequestHeaderModifier != nil {
set++ set++
hasReqMod = true
} }
if filter.ResponseHeaderModifier != nil { if filter.ResponseHeaderModifier != nil {
set++ set++
} }
if filter.UrlRewrite != nil { if filter.UrlRewrite != nil {
set++ set++
hasUrlRewrite = true
if filter.UrlRewrite.PathPrefix == "" { if filter.UrlRewrite.PathPrefix == "" {
merr = multierror.Append(merr, wrapFilterErr( merr = multierror.Append(merr, wrapFilterErr(
resource.ErrInvalidField{ resource.ErrInvalidField{
@ -285,6 +291,12 @@ func ValidateHTTPRoute(res *pbresource.Resource) error {
} }
} }
if hasReqMod && hasUrlRewrite {
merr = multierror.Append(merr, wrapRuleErr(
errors.New("exactly one of request_header_modifier or url_rewrite can be set at a time"),
))
}
if len(rule.BackendRefs) == 0 { if len(rule.BackendRefs) == 0 {
merr = multierror.Append(merr, wrapRuleErr( merr = multierror.Append(merr, wrapRuleErr(
resource.ErrInvalidField{ resource.ErrInvalidField{

View File

@ -733,6 +733,29 @@ func TestValidateHTTPRoute(t *testing.T) {
}, },
expectErr: `invalid element at index 0 of list "rules": invalid element at index 0 of list "filters": exactly one of request_header_modifier, response_header_modifier, or url_rewrite`, expectErr: `invalid element at index 0 of list "rules": invalid element at index 0 of list "filters": exactly one of request_header_modifier, response_header_modifier, or url_rewrite`,
}, },
"filter req+rewrite on two rules is not allowed": {
route: &pbmesh.HTTPRoute{
ParentRefs: []*pbmesh.ParentReference{
newParentRef(catalog.ServiceType, "web", ""),
},
Rules: []*pbmesh.HTTPRouteRule{{
Filters: []*pbmesh.HTTPRouteFilter{
{
RequestHeaderModifier: &pbmesh.HTTPHeaderFilter{},
},
{
UrlRewrite: &pbmesh.HTTPURLRewriteFilter{
PathPrefix: "/blah",
},
},
},
BackendRefs: []*pbmesh.HTTPBackendRef{{
BackendRef: newBackendRef(catalog.ServiceType, "api", ""),
}},
}},
},
expectErr: `invalid element at index 0 of list "rules": exactly one of request_header_modifier or url_rewrite can be set at a time`,
},
"filter req+resp+rewrite header mod is bad": { "filter req+resp+rewrite header mod is bad": {
route: &pbmesh.HTTPRoute{ route: &pbmesh.HTTPRoute{
ParentRefs: []*pbmesh.ParentReference{ ParentRefs: []*pbmesh.ParentReference{
@ -1081,12 +1104,6 @@ func getXRouteTimeoutsTestCases() map[string]xRouteTimeoutsTestcase {
}, },
expectErr: `invalid element at index 0 of list "rules": invalid "timeouts" field: invalid "request" field: timeout cannot be negative: -1s`, expectErr: `invalid element at index 0 of list "rules": invalid "timeouts" field: invalid "request" field: timeout cannot be negative: -1s`,
}, },
"bad backend request": {
timeouts: &pbmesh.HTTPRouteTimeouts{
BackendRequest: durationpb.New(-1 * time.Second),
},
expectErr: `invalid element at index 0 of list "rules": invalid "timeouts" field: invalid "backend_request" field: timeout cannot be negative: -1s`,
},
"bad idle": { "bad idle": {
timeouts: &pbmesh.HTTPRouteTimeouts{ timeouts: &pbmesh.HTTPRouteTimeouts{
Idle: durationpb.New(-1 * time.Second), Idle: durationpb.New(-1 * time.Second),
@ -1095,9 +1112,8 @@ func getXRouteTimeoutsTestCases() map[string]xRouteTimeoutsTestcase {
}, },
"good all": { "good all": {
timeouts: &pbmesh.HTTPRouteTimeouts{ timeouts: &pbmesh.HTTPRouteTimeouts{
Request: durationpb.New(1 * time.Second), Request: durationpb.New(1 * time.Second),
BackendRequest: durationpb.New(2 * time.Second), Idle: durationpb.New(3 * time.Second),
Idle: durationpb.New(3 * time.Second),
}, },
}, },
} }

View File

@ -6,6 +6,7 @@ package types
import ( import (
"errors" "errors"
"fmt" "fmt"
"time"
"github.com/hashicorp/go-multierror" "github.com/hashicorp/go-multierror"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
@ -206,6 +207,10 @@ func validateHeaderMatchType(typ pbmesh.HeaderMatchType) error {
return nil return nil
} }
func errTimeoutCannotBeNegative(d time.Duration) error {
return fmt.Errorf("timeout cannot be negative: %v", d)
}
func validateHTTPTimeouts(timeouts *pbmesh.HTTPRouteTimeouts) []error { func validateHTTPTimeouts(timeouts *pbmesh.HTTPRouteTimeouts) []error {
if timeouts == nil { if timeouts == nil {
return nil return nil
@ -218,16 +223,7 @@ func validateHTTPTimeouts(timeouts *pbmesh.HTTPRouteTimeouts) []error {
if val < 0 { if val < 0 {
errs = append(errs, resource.ErrInvalidField{ errs = append(errs, resource.ErrInvalidField{
Name: "request", Name: "request",
Wrapped: fmt.Errorf("timeout cannot be negative: %v", val), Wrapped: errTimeoutCannotBeNegative(val),
})
}
}
if timeouts.BackendRequest != nil {
val := timeouts.BackendRequest.AsDuration()
if val < 0 {
errs = append(errs, resource.ErrInvalidField{
Name: "backend_request",
Wrapped: fmt.Errorf("timeout cannot be negative: %v", val),
}) })
} }
} }
@ -236,7 +232,7 @@ func validateHTTPTimeouts(timeouts *pbmesh.HTTPRouteTimeouts) []error {
if val < 0 { if val < 0 {
errs = append(errs, resource.ErrInvalidField{ errs = append(errs, resource.ErrInvalidField{
Name: "idle", Name: "idle",
Wrapped: fmt.Errorf("timeout cannot be negative: %v", val), Wrapped: errTimeoutCannotBeNegative(val),
}) })
} }
} }

View File

@ -0,0 +1,23 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package protoutil
import (
"google.golang.org/protobuf/proto"
)
func Clone[T proto.Message](v T) T {
return proto.Clone(v).(T)
}
func CloneSlice[T proto.Message](in []T) []T {
if in == nil {
return nil
}
out := make([]T, 0, len(in))
for _, v := range in {
out = append(out, Clone[T](v))
}
return out
}

View File

@ -24,39 +24,18 @@ const (
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
) )
// HTTPRouteTimeouts defines timeouts that can be configured for an HTTPRoute. // HTTPRouteTimeouts defines timeouts that can be configured for an HTTPRoute
// Timeout values are formatted like 1h/1m/1s/1ms as parsed by Golang time.ParseDuration // or GRPCRoute.
// and MUST BE >= 1ms.
//
// ALTERNATIVE: not using policy attachment semantics
type HTTPRouteTimeouts struct { type HTTPRouteTimeouts struct {
state protoimpl.MessageState state protoimpl.MessageState
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
// Request specifies the duration for processing an HTTP client request after which the // RequestTimeout is the total amount of time permitted for the entire
// gateway will time out if unable to send a response. // downstream request (and retries) to be processed.
// Whether the gateway starts the timeout before or after the entire client request stream
// has been received, is implementation-dependent.
//
// For example, setting the `rules.timeouts.request` field to the value `10s` in an
// `HTTPRoute` will cause a timeout if a client request is taking longer than 10 seconds
// to complete.
//
// When this field is unspecified, request timeout behavior is implementation-dependent.
Request *durationpb.Duration `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` Request *durationpb.Duration `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"`
// BackendRequest specifies a timeout for an individual request from the gateway // Idle specifies the total amount of time permitted for the request stream to be idle.
// to a backend service. Typically used in conjuction with retry configuration, Idle *durationpb.Duration `protobuf:"bytes,2,opt,name=idle,proto3" json:"idle,omitempty"`
// if supported by an implementation.
//
// The value of BackendRequest defaults to and must be <= the value of Request timeout.
//
// Support: Extended
//
// TODO(rb): net-new feature
BackendRequest *durationpb.Duration `protobuf:"bytes,2,opt,name=backend_request,json=backendRequest,proto3" json:"backend_request,omitempty"`
// TODO(RB): this is a consul-only feature
Idle *durationpb.Duration `protobuf:"bytes,3,opt,name=idle,proto3" json:"idle,omitempty"`
} }
func (x *HTTPRouteTimeouts) Reset() { func (x *HTTPRouteTimeouts) Reset() {
@ -98,13 +77,6 @@ func (x *HTTPRouteTimeouts) GetRequest() *durationpb.Duration {
return nil return nil
} }
func (x *HTTPRouteTimeouts) GetBackendRequest() *durationpb.Duration {
if x != nil {
return x.BackendRequest
}
return nil
}
func (x *HTTPRouteTimeouts) GetIdle() *durationpb.Duration { func (x *HTTPRouteTimeouts) GetIdle() *durationpb.Duration {
if x != nil { if x != nil {
return x.Idle return x.Idle
@ -121,37 +93,33 @@ var file_pbmesh_v2beta1_http_route_timeouts_proto_rawDesc = []byte{
0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73,
0x68, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x68, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbb, 0x01, 0x0a, 0x11, 0x48, 0x54, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x77, 0x0a, 0x11, 0x48, 0x54, 0x54,
0x54, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x73, 0x12, 0x50, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x73, 0x12, 0x33,
0x33, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x0a, 0x07, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x72, 0x65, 0x71, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x72, 0x65, 0x71, 0x75,
0x75, 0x65, 0x73, 0x74, 0x12, 0x42, 0x0a, 0x0f, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x64, 0x5f, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x04, 0x69, 0x64, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x04, 0x69, 0x64,
0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x62, 0x61, 0x63, 0x6b, 0x65, 0x6e, 0x6c, 0x65, 0x42, 0x97, 0x02, 0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x69,
0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x04, 0x69, 0x64, 0x6c, 0x65, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x6d, 0x65, 0x73, 0x68,
0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x42, 0x16, 0x48, 0x74, 0x74, 0x70, 0x52, 0x6f,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f,
0x6e, 0x52, 0x04, 0x69, 0x64, 0x6c, 0x65, 0x42, 0x97, 0x02, 0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x2e, 0x50, 0x01, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68,
0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f,
0x2e, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x42, 0x16, 0x48, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2f, 0x70, 0x62, 0x6d,
0x74, 0x74, 0x70, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x73, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x3b, 0x6d, 0x65, 0x73, 0x68,
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x48, 0x43, 0x4d, 0xaa, 0x02, 0x1d,
0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c,
0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x2e, 0x4d, 0x65, 0x73, 0x68, 0x2e, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0xca, 0x02, 0x1d,
0x63, 0x2f, 0x70, 0x62, 0x6d, 0x65, 0x73, 0x68, 0x2f, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c,
0x3b, 0x6d, 0x65, 0x73, 0x68, 0x76, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0xa2, 0x02, 0x03, 0x48, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0xe2, 0x02, 0x29,
0x43, 0x4d, 0xaa, 0x02, 0x1d, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2e, 0x43, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c,
0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2e, 0x4d, 0x65, 0x73, 0x68, 0x2e, 0x56, 0x32, 0x62, 0x65, 0x74, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x5c, 0x47, 0x50,
0x61, 0x31, 0xca, 0x02, 0x1d, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x20, 0x48, 0x61, 0x73, 0x68,
0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x32, 0x62, 0x65, 0x74, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x3a, 0x3a, 0x4d,
0x61, 0x31, 0xe2, 0x02, 0x29, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x5c, 0x43, 0x65, 0x73, 0x68, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x5c, 0x4d, 0x65, 0x73, 0x68, 0x5c, 0x56, 0x32, 0x62, 0x65, 0x74, 0x6f, 0x74, 0x6f, 0x33,
0x61, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02,
0x20, 0x48, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x73,
0x75, 0x6c, 0x3a, 0x3a, 0x4d, 0x65, 0x73, 0x68, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x65, 0x74, 0x61,
0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -173,13 +141,12 @@ var file_pbmesh_v2beta1_http_route_timeouts_proto_goTypes = []interface{}{
} }
var file_pbmesh_v2beta1_http_route_timeouts_proto_depIdxs = []int32{ var file_pbmesh_v2beta1_http_route_timeouts_proto_depIdxs = []int32{
1, // 0: hashicorp.consul.mesh.v2beta1.HTTPRouteTimeouts.request:type_name -> google.protobuf.Duration 1, // 0: hashicorp.consul.mesh.v2beta1.HTTPRouteTimeouts.request:type_name -> google.protobuf.Duration
1, // 1: hashicorp.consul.mesh.v2beta1.HTTPRouteTimeouts.backend_request:type_name -> google.protobuf.Duration 1, // 1: hashicorp.consul.mesh.v2beta1.HTTPRouteTimeouts.idle:type_name -> google.protobuf.Duration
1, // 2: hashicorp.consul.mesh.v2beta1.HTTPRouteTimeouts.idle:type_name -> google.protobuf.Duration 2, // [2:2] is the sub-list for method output_type
3, // [3:3] is the sub-list for method output_type 2, // [2:2] is the sub-list for method input_type
3, // [3:3] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee
3, // [3:3] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name
0, // [0:3] is the sub-list for field type_name
} }
func init() { file_pbmesh_v2beta1_http_route_timeouts_proto_init() } func init() { file_pbmesh_v2beta1_http_route_timeouts_proto_init() }

View File

@ -7,35 +7,13 @@ package hashicorp.consul.mesh.v2beta1;
import "google/protobuf/duration.proto"; import "google/protobuf/duration.proto";
// HTTPRouteTimeouts defines timeouts that can be configured for an HTTPRoute. // HTTPRouteTimeouts defines timeouts that can be configured for an HTTPRoute
// Timeout values are formatted like 1h/1m/1s/1ms as parsed by Golang time.ParseDuration // or GRPCRoute.
// and MUST BE >= 1ms.
//
// ALTERNATIVE: not using policy attachment semantics
message HTTPRouteTimeouts { message HTTPRouteTimeouts {
// Request specifies the duration for processing an HTTP client request after which the // RequestTimeout is the total amount of time permitted for the entire
// gateway will time out if unable to send a response. // downstream request (and retries) to be processed.
// Whether the gateway starts the timeout before or after the entire client request stream
// has been received, is implementation-dependent.
//
// For example, setting the `rules.timeouts.request` field to the value `10s` in an
// `HTTPRoute` will cause a timeout if a client request is taking longer than 10 seconds
// to complete.
//
// When this field is unspecified, request timeout behavior is implementation-dependent.
google.protobuf.Duration request = 1; google.protobuf.Duration request = 1;
// BackendRequest specifies a timeout for an individual request from the gateway // Idle specifies the total amount of time permitted for the request stream to be idle.
// to a backend service. Typically used in conjuction with retry configuration, google.protobuf.Duration idle = 2;
// if supported by an implementation.
//
// The value of BackendRequest defaults to and must be <= the value of Request timeout.
//
// Support: Extended
//
// TODO(rb): net-new feature
google.protobuf.Duration backend_request = 2;
// TODO(RB): this is a consul-only feature
google.protobuf.Duration idle = 3;
} }