diff --git a/internal/envconfig/envconfig.go b/internal/envconfig/envconfig.go index 91f760936c02..6414ee4bbe27 100644 --- a/internal/envconfig/envconfig.go +++ b/internal/envconfig/envconfig.go @@ -77,6 +77,11 @@ var ( // - Target resolution is disabled. // - The DNS resolver is being used. EnableDefaultPortForProxyTarget = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_DEFAULT_PORT_FOR_PROXY_TARGET", true) + + // XDSAuthorityRewrite indicates whether xDS authority rewriting is enabled. + // This feature is defined in gRFC A81 and is enabled by setting the + // environment variable GRPC_EXPERIMENTAL_XDS_AUTHORITY_REWRITE to "true". + XDSAuthorityRewrite = boolFromEnv("GRPC_EXPERIMENTAL_XDS_AUTHORITY_REWRITE", false) ) func boolFromEnv(envVar string, def bool) bool { diff --git a/internal/xds/xdsclient/xdsresource/filter_chain.go b/internal/xds/xdsclient/xdsresource/filter_chain.go index 76ace1090d68..50b72e0de6b2 100644 --- a/internal/xds/xdsclient/xdsresource/filter_chain.go +++ b/internal/xds/xdsclient/xdsresource/filter_chain.go @@ -680,7 +680,7 @@ func processNetworkFilters(filters []*v3listenerpb.Filter) (*FilterChain, error) // server-side." - A36 // Can specify v3 here, as will never get to this function // if v2. - routeU, err := generateRDSUpdateFromRouteConfiguration(hcm.GetRouteConfig()) + routeU, err := generateRDSUpdateFromRouteConfiguration(hcm.GetRouteConfig(), nil) if err != nil { return nil, fmt.Errorf("failed to parse inline RDS resp: %v", err) } diff --git a/internal/xds/xdsclient/xdsresource/listener_resource_type.go b/internal/xds/xdsclient/xdsresource/listener_resource_type.go index f4f69365abb5..2b9000fdf2b6 100644 --- a/internal/xds/xdsclient/xdsresource/listener_resource_type.go +++ b/internal/xds/xdsclient/xdsresource/listener_resource_type.go @@ -35,8 +35,8 @@ type listenerResourceDecoder struct { bootstrapConfig *bootstrap.Config } -func (d *listenerResourceDecoder) Decode(resource *xdsclient.AnyProto, _ xdsclient.DecodeOptions) (*xdsclient.DecodeResult, error) { - name, listener, err := unmarshalListenerResource(resource.ToAny()) +func (d *listenerResourceDecoder) Decode(resource *xdsclient.AnyProto, opts xdsclient.DecodeOptions) (*xdsclient.DecodeResult, error) { + name, listener, err := unmarshalListenerResource(resource.ToAny(), &opts) if name == "" { // Name is unset only when protobuf deserialization fails. return nil, err diff --git a/internal/xds/xdsclient/xdsresource/route_config_resource_type.go b/internal/xds/xdsclient/xdsresource/route_config_resource_type.go index 81033c91f5d9..1104fe9f2b4d 100644 --- a/internal/xds/xdsclient/xdsresource/route_config_resource_type.go +++ b/internal/xds/xdsclient/xdsresource/route_config_resource_type.go @@ -37,8 +37,8 @@ type routeConfigResourceDecoder struct { bootstrapConfig *bootstrap.Config } -func (d *routeConfigResourceDecoder) Decode(resource *xdsclient.AnyProto, _ xdsclient.DecodeOptions) (*xdsclient.DecodeResult, error) { - name, rc, err := unmarshalRouteConfigResource(resource.ToAny()) +func (d *routeConfigResourceDecoder) Decode(resource *xdsclient.AnyProto, opts xdsclient.DecodeOptions) (*xdsclient.DecodeResult, error) { + name, rc, err := unmarshalRouteConfigResource(resource.ToAny(), &opts) if name == "" { // Name is unset only when protobuf deserialization fails. return nil, err diff --git a/internal/xds/xdsclient/xdsresource/type_eds.go b/internal/xds/xdsclient/xdsresource/type_eds.go index a8d568e18263..39b2ba74ddcb 100644 --- a/internal/xds/xdsclient/xdsresource/type_eds.go +++ b/internal/xds/xdsclient/xdsresource/type_eds.go @@ -54,6 +54,7 @@ type Endpoint struct { Weight uint32 HashKey string Metadata map[string]any + Hostname string } // Locality contains information of a locality. diff --git a/internal/xds/xdsclient/xdsresource/type_rds.go b/internal/xds/xdsclient/xdsresource/type_rds.go index 0cde9738d5f1..48e8051b3222 100644 --- a/internal/xds/xdsclient/xdsresource/type_rds.go +++ b/internal/xds/xdsclient/xdsresource/type_rds.go @@ -150,6 +150,9 @@ type Route struct { // ClusterSpecifierPlugin is the name of the Cluster Specifier Plugin that // this Route is linked to, if specified by xDS. ClusterSpecifierPlugin string + // AutoHostRewrite indicates that the ":authority" header can be rewritten + // to the hostname of the upstream endpoint. + AutoHostRewrite bool } // WeightedCluster contains settings for an xds ActionType.WeightedCluster. diff --git a/internal/xds/xdsclient/xdsresource/unmarshal_eds.go b/internal/xds/xdsclient/xdsresource/unmarshal_eds.go index c5b9723a79a3..ebf47961c45d 100644 --- a/internal/xds/xdsclient/xdsresource/unmarshal_eds.go +++ b/internal/xds/xdsclient/xdsresource/unmarshal_eds.go @@ -132,6 +132,7 @@ func parseEndpoints(lbEndpoints []*v3endpointpb.LbEndpoint, uniqueEndpointAddrs Weight: weight, HashKey: hashKey, Metadata: endpointMetadata, + Hostname: lbEndpoint.GetEndpoint().GetHostname(), }) } return endpoints, nil diff --git a/internal/xds/xdsclient/xdsresource/unmarshal_eds_test.go b/internal/xds/xdsclient/xdsresource/unmarshal_eds_test.go index 3020b4953116..905cf8a20f26 100644 --- a/internal/xds/xdsclient/xdsresource/unmarshal_eds_test.go +++ b/internal/xds/xdsclient/xdsresource/unmarshal_eds_test.go @@ -93,12 +93,16 @@ func (s) TestEDSParseRespProto(t *testing.T) { name: "missing locality weight", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 0, 1, []endpointOpts{{addrWithPort: "addr1:314"}}, &addLocalityOptions{ + endpoints1 := []endpointOpts{{addrWithPort: "addr1:314"}} + locOption1 := &addLocalityOptions{ Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_HEALTHY}, - }) - clab0.addLocality("locality-2", 0, 0, []endpointOpts{{addrWithPort: "addr2:159"}}, &addLocalityOptions{ + } + clab0.addLocality("locality-1", 0, 1, endpoints1, locOption1) + endpoints2 := []endpointOpts{{addrWithPort: "addr2:159"}} + locOption2 := &addLocalityOptions{ Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_HEALTHY}, - }) + } + clab0.addLocality("locality-2", 0, 0, endpoints2, locOption2) return clab0.Build() }(), want: EndpointsUpdate{}, @@ -130,14 +134,18 @@ func (s) TestEDSParseRespProto(t *testing.T) { name: "good", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 1, []endpointOpts{{addrWithPort: "addr1:314"}}, &addLocalityOptions{ + endpoints1 := []endpointOpts{{addrWithPort: "addr1:314", hostname: "addr1"}} + locOption1 := &addLocalityOptions{ Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_UNHEALTHY}, Weight: []uint32{271}, - }) - clab0.addLocality("locality-2", 1, 0, []endpointOpts{{addrWithPort: "addr2:159"}}, &addLocalityOptions{ + } + clab0.addLocality("locality-1", 1, 1, endpoints1, locOption1) + endpoints2 := []endpointOpts{{addrWithPort: "addr2:159", hostname: "addr2"}} + locOption2 := &addLocalityOptions{ Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_DRAINING}, Weight: []uint32{828}, - }) + } + clab0.addLocality("locality-2", 1, 0, endpoints2, locOption2) return clab0.Build() }(), want: EndpointsUpdate{ @@ -148,6 +156,7 @@ func (s) TestEDSParseRespProto(t *testing.T) { Addresses: []string{"addr1:314"}, HealthStatus: EndpointHealthStatusUnhealthy, Weight: 271, + Hostname: "addr1", }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 1, @@ -158,6 +167,7 @@ func (s) TestEDSParseRespProto(t *testing.T) { Addresses: []string{"addr2:159"}, HealthStatus: EndpointHealthStatusDraining, Weight: 828, + Hostname: "addr2", }}, ID: clients.Locality{SubZone: "locality-2"}, Priority: 0, @@ -171,15 +181,19 @@ func (s) TestEDSParseRespProto(t *testing.T) { name: "good duplicate locality with different priority", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 1, []endpointOpts{{addrWithPort: "addr1:314"}}, &addLocalityOptions{ + endpoints1 := []endpointOpts{{addrWithPort: "addr1:314", hostname: "addr1"}} + locOption1 := &addLocalityOptions{ Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_UNHEALTHY}, Weight: []uint32{271}, - }) + } + clab0.addLocality("locality-1", 1, 1, endpoints1, locOption1) // Same locality name, but with different priority. - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{addrWithPort: "addr2:159"}}, &addLocalityOptions{ + endpoints2 := []endpointOpts{{addrWithPort: "addr2:159", hostname: "addr2"}} + locOption2 := &addLocalityOptions{ Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_DRAINING}, Weight: []uint32{828}, - }) + } + clab0.addLocality("locality-1", 1, 0, endpoints2, locOption2) return clab0.Build() }(), want: EndpointsUpdate{ @@ -190,6 +204,7 @@ func (s) TestEDSParseRespProto(t *testing.T) { Addresses: []string{"addr1:314"}, HealthStatus: EndpointHealthStatusUnhealthy, Weight: 271, + Hostname: "addr1", }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 1, @@ -200,6 +215,7 @@ func (s) TestEDSParseRespProto(t *testing.T) { Addresses: []string{"addr2:159"}, HealthStatus: EndpointHealthStatusDraining, Weight: 828, + Hostname: "addr2", }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, @@ -282,14 +298,18 @@ func (s) TestEDSParseRespProtoAdditionalAddrs(t *testing.T) { name: "multiple localities", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 1, []endpointOpts{{addrWithPort: "addr1:997", additionalAddrWithPorts: []string{"addr1:1000"}}}, &addLocalityOptions{ + endpoints1 := []endpointOpts{{addrWithPort: "addr1:997", additionalAddrWithPorts: []string{"addr1:1000"}}} + locOption1 := &addLocalityOptions{ Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_UNHEALTHY}, Weight: []uint32{271}, - }) - clab0.addLocality("locality-2", 1, 0, []endpointOpts{{addrWithPort: "addr2:998", additionalAddrWithPorts: []string{"addr2:1000"}}}, &addLocalityOptions{ + } + clab0.addLocality("locality-1", 1, 1, endpoints1, locOption1) + endpoints2 := []endpointOpts{{addrWithPort: "addr2:998", additionalAddrWithPorts: []string{"addr2:1000"}}} + locOption2 := &addLocalityOptions{ Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_HEALTHY}, Weight: []uint32{828}, - }) + } + clab0.addLocality("locality-2", 1, 0, endpoints2, locOption2) return clab0.Build() }(), want: EndpointsUpdate{ @@ -466,14 +486,18 @@ func (s) TestUnmarshalEndpoints(t *testing.T) { testutils.SetEnvConfig(t, &envconfig.XDSHTTPConnectEnabled, true) var v3EndpointsAny = testutils.MarshalAny(t, func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 1, []endpointOpts{{addrWithPort: "addr1:314"}}, &addLocalityOptions{ + endpoints1 := []endpointOpts{{addrWithPort: "addr1:314", hostname: "addr1"}} + locOption1 := &addLocalityOptions{ Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_UNHEALTHY}, Weight: []uint32{271}, - }) - clab0.addLocality("locality-2", 1, 0, []endpointOpts{{addrWithPort: "addr2:159"}}, &addLocalityOptions{ + } + clab0.addLocality("locality-1", 1, 1, endpoints1, locOption1) + endpoints2 := []endpointOpts{{addrWithPort: "addr2:159", hostname: "addr2"}} + locOption2 := &addLocalityOptions{ Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_DRAINING}, Weight: []uint32{828}, - }) + } + clab0.addLocality("locality-2", 1, 0, endpoints2, locOption2) return clab0.Build() }()) @@ -520,6 +544,7 @@ func (s) TestUnmarshalEndpoints(t *testing.T) { Addresses: []string{"addr1:314"}, HealthStatus: EndpointHealthStatusUnhealthy, Weight: 271, + Hostname: "addr1", }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 1, @@ -530,6 +555,7 @@ func (s) TestUnmarshalEndpoints(t *testing.T) { Addresses: []string{"addr2:159"}, HealthStatus: EndpointHealthStatusDraining, Weight: 828, + Hostname: "addr2", }}, ID: clients.Locality{SubZone: "locality-2"}, Priority: 0, @@ -551,6 +577,7 @@ func (s) TestUnmarshalEndpoints(t *testing.T) { Addresses: []string{"addr1:314"}, HealthStatus: EndpointHealthStatusUnhealthy, Weight: 271, + Hostname: "addr1", }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 1, @@ -561,6 +588,7 @@ func (s) TestUnmarshalEndpoints(t *testing.T) { Addresses: []string{"addr2:159"}, HealthStatus: EndpointHealthStatusDraining, Weight: 828, + Hostname: "addr2", }}, ID: clients.Locality{SubZone: "locality-2"}, Priority: 0, @@ -600,7 +628,7 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T name: "typed_filter_metadata_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{ + endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ @@ -615,7 +643,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T }), }, }, - }}, nil) + }} + clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ @@ -642,7 +671,7 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T name: "filter_metadata_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{ + endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ FilterMetadata: map[string]*structpb.Struct{ @@ -655,7 +684,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T }, }, }, - }}, nil) + }} + clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ @@ -682,7 +712,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T name: "typed_filter_metadata_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, &addLocalityOptions{ + endpoints := []endpointOpts{{addrWithPort: "addr1:314"}} + locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "test-key": testutils.MarshalAny(t, &v3corepb.Address{ @@ -696,7 +727,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T }), }, }, - }) + } + clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ @@ -723,7 +755,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T name: "filter_metadata_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, &addLocalityOptions{ + endpoints := []endpointOpts{{addrWithPort: "addr1:314"}} + locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ FilterMetadata: map[string]*structpb.Struct{ "test-key": { @@ -735,7 +768,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T }, }, }, - }) + } + clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ @@ -762,7 +796,7 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T name: "typed_filter_metadata_over_filter_metadata_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{ + endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ @@ -786,7 +820,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T }, }, }, - }}, nil) + }} + clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ @@ -813,7 +848,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T name: "typed_filter_metadata_over_filter_metadata_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, &addLocalityOptions{ + endpoints := []endpointOpts{{addrWithPort: "addr1:314"}} + locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "test-key": testutils.MarshalAny(t, &v3corepb.Address{ @@ -836,7 +872,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T }, }, }, - }) + } + clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ @@ -863,7 +900,7 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T name: "both_filter_and_typed_filter_metadata_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{ + endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ @@ -887,7 +924,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T }, }, }, - }}, nil) + }} + clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ @@ -917,7 +955,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T name: "both_filter_and_typed_filter_metadata_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, &addLocalityOptions{ + endpoints := []endpointOpts{{addrWithPort: "addr1:314"}} + locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "test-key": testutils.MarshalAny(t, &v3corepb.Address{ @@ -940,7 +979,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T }, }, }, - }) + } + clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ @@ -993,7 +1033,7 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing. name: "typed_filter_metadata_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{ + endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ @@ -1008,7 +1048,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing. }), }, }, - }}, nil) + }} + clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ @@ -1030,7 +1071,7 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing. name: "filter_metadata_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{ + endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ FilterMetadata: map[string]*structpb.Struct{ @@ -1043,7 +1084,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing. }, }, }, - }}, nil) + }} + clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ @@ -1065,7 +1107,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing. name: "typed_filter_metadata_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, &addLocalityOptions{ + endpoints := []endpointOpts{{addrWithPort: "addr1:314"}} + locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "test-key": testutils.MarshalAny(t, &v3corepb.Address{ @@ -1079,7 +1122,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing. }), }, }, - }) + } + clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ @@ -1101,7 +1145,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing. name: "filter_metadata_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, &addLocalityOptions{ + endpoints := []endpointOpts{{addrWithPort: "addr1:314"}} + locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ FilterMetadata: map[string]*structpb.Struct{ "test-key": { @@ -1113,7 +1158,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing. }, }, }, - }) + } + clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ @@ -1135,7 +1181,7 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing. name: "both_filter_and_typed_filter_metadata_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{ + endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ @@ -1159,7 +1205,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing. }, }, }, - }}, nil) + }} + clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ @@ -1181,7 +1228,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing. name: "both_filter_and_typed_filter_metadata_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, &addLocalityOptions{ + endpoints := []endpointOpts{{addrWithPort: "addr1:314"}} + locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "test-key": testutils.MarshalAny(t, &v3corepb.Address{ @@ -1204,7 +1252,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing. }, }, }, - }) + } + clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ @@ -1226,7 +1275,7 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing. name: "converter_failure_is_not_triggerred", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{ + endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ @@ -1239,7 +1288,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing. }), }, }, - }}, &addLocalityOptions{ + }} + locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "envoy.http11_proxy_transport_socket.proxy_address": testutils.MarshalAny(t, &v3corepb.Address{ @@ -1251,7 +1301,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing. }), }, }, - }) + } + clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ @@ -1295,7 +1346,7 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_ConverterFailure(t *t name: "converter_failure_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{ + endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ @@ -1308,7 +1359,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_ConverterFailure(t *t }), }, }, - }}, nil) + }} + clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), }, @@ -1316,7 +1368,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_ConverterFailure(t *t name: "converter_failure_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) - clab0.addLocality("locality-1", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, &addLocalityOptions{ + endpoints := []endpointOpts{{addrWithPort: "addr1:314"}} + locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "envoy.http11_proxy_transport_socket.proxy_address": testutils.MarshalAny(t, &v3corepb.Address{ @@ -1328,7 +1381,8 @@ func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_ConverterFailure(t *t }), }, }, - }) + } + clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), }, @@ -1382,6 +1436,7 @@ type endpointOpts struct { addrWithPort string additionalAddrWithPorts []string metadata *v3corepb.Metadata + hostname string } func addressFromStr(addrWithPort string) *v3corepb.Address { @@ -1420,6 +1475,7 @@ func (clab *claBuilder) addLocality(subzone string, weight uint32, priority uint Endpoint: &v3endpointpb.Endpoint{ Address: addressFromStr(e.addrWithPort), AdditionalAddresses: additionalAddrs, + Hostname: e.hostname, }, }, Metadata: e.metadata, diff --git a/internal/xds/xdsclient/xdsresource/unmarshal_lds.go b/internal/xds/xdsclient/xdsresource/unmarshal_lds.go index 25c607b48a4d..604c086ba428 100644 --- a/internal/xds/xdsclient/xdsresource/unmarshal_lds.go +++ b/internal/xds/xdsclient/xdsresource/unmarshal_lds.go @@ -27,12 +27,13 @@ import ( v3listenerpb "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" v3routepb "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" v3httppb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" + "google.golang.org/grpc/internal/xds/clients/xdsclient" "google.golang.org/grpc/internal/xds/httpfilter" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" ) -func unmarshalListenerResource(r *anypb.Any) (string, ListenerUpdate, error) { +func unmarshalListenerResource(r *anypb.Any, opts *xdsclient.DecodeOptions) (string, ListenerUpdate, error) { r, err := UnwrapResource(r) if err != nil { return "", ListenerUpdate{}, fmt.Errorf("failed to unwrap resource: %v", err) @@ -46,7 +47,7 @@ func unmarshalListenerResource(r *anypb.Any) (string, ListenerUpdate, error) { return "", ListenerUpdate{}, fmt.Errorf("failed to unmarshal resource: %v", err) } - lu, err := processListener(lis) + lu, err := processListener(lis, opts) if err != nil { return lis.GetName(), ListenerUpdate{}, err } @@ -54,16 +55,16 @@ func unmarshalListenerResource(r *anypb.Any) (string, ListenerUpdate, error) { return lis.GetName(), *lu, nil } -func processListener(lis *v3listenerpb.Listener) (*ListenerUpdate, error) { +func processListener(lis *v3listenerpb.Listener, opts *xdsclient.DecodeOptions) (*ListenerUpdate, error) { if lis.GetApiListener() != nil { - return processClientSideListener(lis) + return processClientSideListener(lis, opts) } return processServerSideListener(lis) } // processClientSideListener checks if the provided Listener proto meets // the expected criteria. If so, it returns a non-empty routeConfigName. -func processClientSideListener(lis *v3listenerpb.Listener) (*ListenerUpdate, error) { +func processClientSideListener(lis *v3listenerpb.Listener, opts *xdsclient.DecodeOptions) (*ListenerUpdate, error) { update := &ListenerUpdate{} apiLisAny := lis.GetApiListener().GetApiListener() @@ -95,7 +96,7 @@ func processClientSideListener(lis *v3listenerpb.Listener) (*ListenerUpdate, err } update.RouteConfigName = name case *v3httppb.HttpConnectionManager_RouteConfig: - routeU, err := generateRDSUpdateFromRouteConfiguration(apiLis.GetRouteConfig()) + routeU, err := generateRDSUpdateFromRouteConfiguration(apiLis.GetRouteConfig(), opts) if err != nil { return nil, fmt.Errorf("failed to parse inline RDS resp: %v", err) } diff --git a/internal/xds/xdsclient/xdsresource/unmarshal_lds_test.go b/internal/xds/xdsclient/xdsresource/unmarshal_lds_test.go index 2560365d09e2..24b0e7f0728b 100644 --- a/internal/xds/xdsclient/xdsresource/unmarshal_lds_test.go +++ b/internal/xds/xdsclient/xdsresource/unmarshal_lds_test.go @@ -580,7 +580,7 @@ func (s) TestUnmarshalListener_ClientSide(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - name, update, err := unmarshalListenerResource(test.resource) + name, update, err := unmarshalListenerResource(test.resource, nil) if (err != nil) != test.wantErr { t.Errorf("unmarshalListenerResource(%s), got err: %v, wantErr: %v", pretty.ToJSON(test.resource), err, test.wantErr) } @@ -1707,7 +1707,7 @@ func (s) TestUnmarshalListener_ServerSide(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - name, update, err := unmarshalListenerResource(test.resource) + name, update, err := unmarshalListenerResource(test.resource, nil) if err != nil && !strings.Contains(err.Error(), test.wantErr) { t.Errorf("unmarshalListenerResource(%s) = %v wantErr: %q", pretty.ToJSON(test.resource), err, test.wantErr) } diff --git a/internal/xds/xdsclient/xdsresource/unmarshal_rds.go b/internal/xds/xdsclient/xdsresource/unmarshal_rds.go index 479d023f40ac..d988b4e77f9a 100644 --- a/internal/xds/xdsclient/xdsresource/unmarshal_rds.go +++ b/internal/xds/xdsclient/xdsresource/unmarshal_rds.go @@ -25,6 +25,8 @@ import ( "time" "google.golang.org/grpc/codes" + "google.golang.org/grpc/internal/envconfig" + "google.golang.org/grpc/internal/xds/clients/xdsclient" "google.golang.org/grpc/internal/xds/clusterspecifier" "google.golang.org/grpc/internal/xds/matcher" "google.golang.org/protobuf/proto" @@ -34,7 +36,7 @@ import ( v3typepb "github.com/envoyproxy/go-control-plane/envoy/type/v3" ) -func unmarshalRouteConfigResource(r *anypb.Any) (string, RouteConfigUpdate, error) { +func unmarshalRouteConfigResource(r *anypb.Any, opts *xdsclient.DecodeOptions) (string, RouteConfigUpdate, error) { r, err := UnwrapResource(r) if err != nil { return "", RouteConfigUpdate{}, fmt.Errorf("failed to unwrap resource: %v", err) @@ -48,7 +50,7 @@ func unmarshalRouteConfigResource(r *anypb.Any) (string, RouteConfigUpdate, erro return "", RouteConfigUpdate{}, fmt.Errorf("failed to unmarshal resource: %v", err) } - u, err := generateRDSUpdateFromRouteConfiguration(rc) + u, err := generateRDSUpdateFromRouteConfiguration(rc, opts) if err != nil { return rc.GetName(), RouteConfigUpdate{}, err } @@ -67,12 +69,12 @@ func unmarshalRouteConfigResource(r *anypb.Any) (string, RouteConfigUpdate, erro // The RouteConfiguration includes a list of virtualHosts, which may have zero // or more elements. We are interested in the element whose domains field // matches the server name specified in the "xds:" URI. The only field in the -// VirtualHost proto that the we are interested in is the list of routes. We +// VirtualHost proto that we are interested in is the list of routes. We // only look at the last route in the list (the default route), whose match -// field must be empty and whose route field must be set. Inside that route +// field must be empty and whose route field must be set. Inside that route // message, the cluster field will contain the clusterName or weighted clusters // we are looking for. -func generateRDSUpdateFromRouteConfiguration(rc *v3routepb.RouteConfiguration) (RouteConfigUpdate, error) { +func generateRDSUpdateFromRouteConfiguration(rc *v3routepb.RouteConfiguration, opts *xdsclient.DecodeOptions) (RouteConfigUpdate, error) { vhs := make([]*VirtualHost, 0, len(rc.GetVirtualHosts())) csps, err := processClusterSpecifierPlugins(rc.ClusterSpecifierPlugins) if err != nil { @@ -83,7 +85,7 @@ func generateRDSUpdateFromRouteConfiguration(rc *v3routepb.RouteConfiguration) ( // ignored and not emitted by the xdsclient. var cspNames = make(map[string]bool) for _, vh := range rc.GetVirtualHosts() { - routes, cspNs, err := routesProtoToSlice(vh.Routes, csps) + routes, cspNs, err := routesProtoToSlice(vh.Routes, csps, opts) if err != nil { return RouteConfigUpdate{}, fmt.Errorf("received route is invalid: %v", err) } @@ -206,7 +208,7 @@ func generateRetryConfig(rp *v3routepb.RetryPolicy) (*RetryConfig, error) { return cfg, nil } -func routesProtoToSlice(routes []*v3routepb.Route, csps map[string]clusterspecifier.BalancerConfig) ([]*Route, map[string]bool, error) { +func routesProtoToSlice(routes []*v3routepb.Route, csps map[string]clusterspecifier.BalancerConfig, opts *xdsclient.DecodeOptions) ([]*Route, map[string]bool, error) { var routesRet []*Route var cspNames = make(map[string]bool) for _, r := range routes { @@ -302,6 +304,12 @@ func routesProtoToSlice(routes []*v3routepb.Route, csps map[string]clusterspecif case *v3routepb.Route_Route: action := r.GetRoute() + if envconfig.XDSAuthorityRewrite { + if opts != nil && opts.ServerConfig != nil && opts.ServerConfig.SupportsServerFeature(xdsclient.ServerFeatureTrustedXDSServer) { + route.AutoHostRewrite = action.GetAutoHostRewrite().GetValue() + } + } + // Hash Policies are only applicable for a Ring Hash LB. hp, err := hashPoliciesProtoToSlice(action.HashPolicy) if err != nil { diff --git a/internal/xds/xdsclient/xdsresource/unmarshal_rds_test.go b/internal/xds/xdsclient/xdsresource/unmarshal_rds_test.go index 90d80c8be6b7..8c04dbb2b8ce 100644 --- a/internal/xds/xdsclient/xdsresource/unmarshal_rds_test.go +++ b/internal/xds/xdsclient/xdsresource/unmarshal_rds_test.go @@ -29,8 +29,10 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "google.golang.org/grpc/codes" + "google.golang.org/grpc/internal/envconfig" "google.golang.org/grpc/internal/pretty" "google.golang.org/grpc/internal/testutils" + "google.golang.org/grpc/internal/xds/clients/xdsclient" "google.golang.org/grpc/internal/xds/clusterspecifier" "google.golang.org/grpc/internal/xds/httpfilter" "google.golang.org/grpc/internal/xds/matcher" @@ -710,7 +712,7 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - gotUpdate, gotError := generateRDSUpdateFromRouteConfiguration(test.rc) + gotUpdate, gotError := generateRDSUpdateFromRouteConfiguration(test.rc, nil) if (gotError != nil) != test.wantError || !cmp.Equal(gotUpdate, test.wantUpdate, cmpopts.EquateEmpty(), cmp.Transformer("FilterConfig", func(fc httpfilter.FilterConfig) string { @@ -722,6 +724,95 @@ func (s) TestRDSGenerateRDSUpdateFromRouteConfiguration(t *testing.T) { } } +func (s) TestGenerateRDSUpdateFromRouteConfigurationWithAutoHostRewrite(t *testing.T) { + const ( + clusterName = "clusterName" + ldsTarget = "lds.target.good:1111" + ) + + tests := []struct { + name string + isTrusted xdsclient.ServerFeature // Corresponds to ServerConfig + envConfigRewrite bool // Corresponds to envconfig.XDSAuthorityRewrite + autoHostRewrite bool + wantResult bool + }{ + { + name: "envConfigOn_Trusted", + isTrusted: xdsclient.ServerFeatureTrustedXDSServer, + envConfigRewrite: true, + autoHostRewrite: true, + wantResult: true, + }, + { + name: "envConfigOn_Trusted_AutoHostRewriteFalse", + isTrusted: xdsclient.ServerFeatureTrustedXDSServer, + envConfigRewrite: true, + autoHostRewrite: false, + wantResult: false, + }, + { + name: "envConfigOff_Trusted", + isTrusted: xdsclient.ServerFeatureTrustedXDSServer, + envConfigRewrite: false, + autoHostRewrite: true, + wantResult: false, + }, + { + name: "envConfigOn_Untrusted", + envConfigRewrite: true, + autoHostRewrite: false, + wantResult: false, + }, + { + name: "envConfigOff_Untrusted", + envConfigRewrite: false, + autoHostRewrite: true, + wantResult: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + testutils.SetEnvConfig(t, &envconfig.XDSAuthorityRewrite, test.envConfigRewrite) + + opts := &xdsclient.DecodeOptions{ + ServerConfig: &xdsclient.ServerConfig{ + ServerFeature: test.isTrusted, + }, + } + + routeConfig := &v3routepb.RouteConfiguration{ + Name: "routeName", + VirtualHosts: []*v3routepb.VirtualHost{{ + Domains: []string{ldsTarget}, + Routes: []*v3routepb.Route{{ + Match: &v3routepb.RouteMatch{PathSpecifier: &v3routepb.RouteMatch_Prefix{Prefix: "/"}}, + Action: &v3routepb.Route_Route{ + Route: &v3routepb.RouteAction{ + ClusterSpecifier: &v3routepb.RouteAction_Cluster{Cluster: clusterName}, + HostRewriteSpecifier: &v3routepb.RouteAction_AutoHostRewrite{AutoHostRewrite: &wrapperspb.BoolValue{Value: test.autoHostRewrite}}, + }, + }, + }}, + }}, + } + + update, err := generateRDSUpdateFromRouteConfiguration(routeConfig, opts) + if err != nil { + t.Errorf("generateRDSUpdateFromRouteConfiguration() failed, got : %v, want: ", err) + } + if len(update.VirtualHosts) == 0 || len(update.VirtualHosts[0].Routes) == 0 { + t.Errorf("Unexpected parsed routes from generateRDSUpdateFromRouteConfiguration(), got : 0, want: 1") + } + + if update.VirtualHosts[0].Routes[0].AutoHostRewrite != test.wantResult { + t.Errorf("AutoHostRewrite = %v, want %v", update.VirtualHosts[0].Routes[0].AutoHostRewrite, test.wantResult) + } + }) + } +} + var configOfClusterSpecifierDoesntExist = &anypb.Any{ TypeUrl: "does.not.exist", Value: []byte{1, 2, 3}, @@ -874,7 +965,7 @@ func (s) TestUnmarshalRouteConfig(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - name, update, err := unmarshalRouteConfigResource(test.resource) + name, update, err := unmarshalRouteConfigResource(test.resource, nil) if (err != nil) != test.wantErr { t.Errorf("unmarshalRouteConfigResource(%s), got err: %v, wantErr: %v", pretty.ToJSON(test.resource), err, test.wantErr) } @@ -1505,7 +1596,7 @@ func (s) TestRoutesProtoToSlice(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, _, err := routesProtoToSlice(tt.routes, nil) + got, _, err := routesProtoToSlice(tt.routes, nil, nil) if (err != nil) != tt.wantErr { t.Fatalf("routesProtoToSlice() error = %v, wantErr %v", err, tt.wantErr) }