/* * * Copyright 2021 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package xdsresource import ( "fmt" "math" "net" "strconv" "testing" v3corepb "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" v3endpointpb "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" v3discoverypb "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" v3typepb "github.com/envoyproxy/go-control-plane/envoy/type/v3" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "google.golang.org/grpc/experimental/balancer/hostname" "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" "google.golang.org/grpc/internal/xds/xdsclient/xdsresource/version" "google.golang.org/grpc/resolver" "google.golang.org/grpc/resolver/ringhash" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/wrapperspb" ) // enableA86 enables A86 support for the duration of the test by: // 1. Setting the GRPC_EXPERIMENTAL_XDS_HTTP_CONNECT env var to true. // 2. Registering the proxy address converter (and restore original // on cleanup). func enableA86(t *testing.T) { testutils.SetEnvConfig(t, &envconfig.XDSHTTPConnectEnabled, true) cleanup, err := RegisterMetadataConverterForTesting(version.V3AddressURL) if err != nil { t.Fatalf("RegisterMetadataConverterForTesting(%q) failed: %v", version.V3AddressURL, err) } t.Cleanup(cleanup) } // disableA86 disables A86 support for the duration of the test by: // 1. Setting the GRPC_EXPERIMENTAL_XDS_HTTP_CONNECT env var to false. // 2. Unregistering the proxy address converter for the duration of the test // (and restoring the original on cleanup). func disableA86(t *testing.T) { testutils.SetEnvConfig(t, &envconfig.XDSHTTPConnectEnabled, false) cleanup := UnregisterMetadataConverterForTesting(version.V3AddressURL) t.Cleanup(cleanup) } func buildResolverEndpoint(addr []string, host string) resolver.Endpoint { address := []resolver.Address{} for _, a := range addr { address = append(address, resolver.Address{Addr: a}) } resolverEndpoint := resolver.Endpoint{Addresses: address} resolverEndpoint = hostname.Set(resolverEndpoint, host) return resolverEndpoint } func (s) TestEDSParseRespProto(t *testing.T) { tests := []struct { name string m *v3endpointpb.ClusterLoadAssignment want EndpointsUpdate wantErr bool }{ { name: "missing-priority", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) clab0.addLocality("locality-1", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, nil) clab0.addLocality("locality-2", 1, 2, []endpointOpts{{addrWithPort: "addr2:159"}}, nil) return clab0.Build() }(), want: EndpointsUpdate{}, wantErr: true, }, { name: "missing-locality-ID", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) clab0.addLocality("", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, nil) return clab0.Build() }(), want: EndpointsUpdate{}, wantErr: true, }, { name: "zero-endpoint-weight", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) clab0.addLocality("locality-0", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, &addLocalityOptions{Weight: []uint32{0}}) return clab0.Build() }(), want: EndpointsUpdate{}, wantErr: true, }, { name: "duplicate-locality-in-the-same-priority", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) clab0.addLocality("locality-0", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, nil) clab0.addLocality("locality-0", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, nil) // Duplicate locality with the same priority. return clab0.Build() }(), want: EndpointsUpdate{}, wantErr: true, }, { name: "missing locality weight", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints1 := []endpointOpts{{addrWithPort: "addr1:314"}} locOption1 := &addLocalityOptions{Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_HEALTHY}} 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{}, }, { name: "max sum of endpoint weights within a locality exceeded", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{ {addrWithPort: "addr1:314"}, {addrWithPort: "addr2:159"}, } locOption := &addLocalityOptions{ Weight: []uint32{math.MaxUint32, 1}, } clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), want: EndpointsUpdate{}, wantErr: true, }, { name: "max sum of weights at the same priority exceeded", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) clab0.addLocality("locality-1", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, nil) clab0.addLocality("locality-2", 4294967295, 1, []endpointOpts{{addrWithPort: "addr2:159"}}, nil) clab0.addLocality("locality-3", 1, 1, []endpointOpts{{addrWithPort: "addr2:88"}}, nil) return clab0.Build() }(), want: EndpointsUpdate{}, wantErr: true, }, { name: "duplicate endpoint address", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) clab0.addLocality("locality-1", 1, 1, []endpointOpts{{addrWithPort: "addr:997"}}, nil) clab0.addLocality("locality-2", 1, 0, []endpointOpts{{addrWithPort: "addr:997"}}, nil) return clab0.Build() }(), want: EndpointsUpdate{}, wantErr: true, }, { name: "unsupported-drop-denominator", m: &v3endpointpb.ClusterLoadAssignment{ ClusterName: "test", Policy: &v3endpointpb.ClusterLoadAssignment_Policy{ DropOverloads: []*v3endpointpb.ClusterLoadAssignment_Policy_DropOverload{{ Category: "test-drop", DropPercentage: &v3typepb.FractionalPercent{ Numerator: 1, Denominator: v3typepb.FractionalPercent_DenominatorType(7), }, }}, }, }, want: EndpointsUpdate{}, wantErr: true, }, { name: "good", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) 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) 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{ Drops: nil, Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnhealthy, Weight: 271, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 1, Weight: 1, }, { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr2:159"}, "addr2"), HealthStatus: EndpointHealthStatusDraining, Weight: 828, }}, ID: clients.Locality{SubZone: "locality-2"}, Priority: 0, Weight: 1, }, }, }, wantErr: false, }, { name: "good duplicate locality with different priority", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) 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. 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{ Drops: nil, Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnhealthy, Weight: 271, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 1, Weight: 1, }, { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr2:159"}, "addr2"), HealthStatus: EndpointHealthStatusDraining, Weight: 828, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, }, }, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := parseEDSRespProto(tt.m) if (err != nil) != tt.wantErr { t.Fatalf("parseEDSRespProto() error = %v, wantErr %v", err, tt.wantErr) } if d := cmp.Diff(got, tt.want, cmpopts.EquateEmpty()); d != "" { t.Errorf("parseEDSRespProto() got = %v, want %v, diff: %v", got, tt.want, d) } }) } } func (s) TestEDSParseRespProtoAdditionalAddrs(t *testing.T) { origDualstackEndpointsEnabled := envconfig.XDSDualstackEndpointsEnabled defer func() { envconfig.XDSDualstackEndpointsEnabled = origDualstackEndpointsEnabled }() envconfig.XDSDualstackEndpointsEnabled = true tests := []struct { name string m *v3endpointpb.ClusterLoadAssignment want EndpointsUpdate wantErr bool }{ { name: "duplicate primary address in self additional addresses", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) clab0.addLocality("locality-1", 1, 0, []endpointOpts{{addrWithPort: "addr:998", additionalAddrWithPorts: []string{"addr:998"}}}, nil) return clab0.Build() }(), want: EndpointsUpdate{}, wantErr: true, }, { name: "duplicate primary address in other locality additional addresses", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) clab0.addLocality("locality-1", 1, 1, []endpointOpts{{addrWithPort: "addr:997"}}, nil) clab0.addLocality("locality-2", 1, 0, []endpointOpts{{addrWithPort: "addr:998", additionalAddrWithPorts: []string{"addr:997"}}}, nil) return clab0.Build() }(), want: EndpointsUpdate{}, wantErr: true, }, { name: "duplicate additional address in self additional addresses", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) clab0.addLocality("locality-1", 1, 0, []endpointOpts{{addrWithPort: "addr:998", additionalAddrWithPorts: []string{"addr:999", "addr:999"}}}, nil) return clab0.Build() }(), want: EndpointsUpdate{}, wantErr: true, }, { name: "duplicate additional address in other locality additional addresses", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) clab0.addLocality("locality-1", 1, 1, []endpointOpts{{addrWithPort: "addr:997", additionalAddrWithPorts: []string{"addr:1000"}}}, nil) clab0.addLocality("locality-2", 1, 0, []endpointOpts{{addrWithPort: "addr:998", additionalAddrWithPorts: []string{"addr:1000"}}}, nil) return clab0.Build() }(), want: EndpointsUpdate{}, wantErr: true, }, { name: "multiple localities", m: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints1 := []endpointOpts{{addrWithPort: "addr1:997", additionalAddrWithPorts: []string{"addr1:1000"}, hostname: "addr1"}} locOption1 := &addLocalityOptions{ Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_UNHEALTHY}, Weight: []uint32{271}, } clab0.addLocality("locality-1", 1, 1, endpoints1, locOption1) endpoints2 := []endpointOpts{{addrWithPort: "addr2:998", additionalAddrWithPorts: []string{"addr2:1000"}, hostname: "addr2"}} locOption2 := &addLocalityOptions{ Health: []v3corepb.HealthStatus{v3corepb.HealthStatus_HEALTHY}, Weight: []uint32{828}, } clab0.addLocality("locality-2", 1, 0, endpoints2, locOption2) return clab0.Build() }(), want: EndpointsUpdate{ Drops: nil, Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:997", "addr1:1000"}, "addr1"), HealthStatus: EndpointHealthStatusUnhealthy, Weight: 271, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 1, Weight: 1, }, { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr2:998", "addr2:1000"}, "addr2"), HealthStatus: EndpointHealthStatusHealthy, Weight: 828, }}, ID: clients.Locality{SubZone: "locality-2"}, Priority: 0, Weight: 1, }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := parseEDSRespProto(tt.m) if (err != nil) != tt.wantErr { t.Fatalf("parseEDSRespProto() error = %v, wantErr %v", err, tt.wantErr) } if d := cmp.Diff(got, tt.want, cmpopts.EquateEmpty()); d != "" { t.Errorf("parseEDSRespProto() got = %v, want %v, diff: %v", got, tt.want, d) } }) } } func (s) TestUnmarshalEndpointHashKey(t *testing.T) { baseCLA := &v3endpointpb.ClusterLoadAssignment{ ClusterName: "test-cluster", Endpoints: []*v3endpointpb.LocalityLbEndpoints{ { Locality: &v3corepb.Locality{Region: "r"}, LbEndpoints: []*v3endpointpb.LbEndpoint{ { HostIdentifier: &v3endpointpb.LbEndpoint_Endpoint{ Endpoint: &v3endpointpb.Endpoint{ Address: &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "test-address", PortSpecifier: &v3corepb.SocketAddress_PortValue{ PortValue: 8080, }, }, }, }, }, }, }, }, LoadBalancingWeight: &wrapperspb.UInt32Value{Value: 1}, }, }, } tests := []struct { name string metadata *v3corepb.Metadata wantHashKey string compatEnvVar bool }{ { name: "no metadata", metadata: nil, wantHashKey: "", }, { name: "empty metadata", metadata: &v3corepb.Metadata{}, wantHashKey: "", }, { name: "filter metadata without envoy.lb", metadata: &v3corepb.Metadata{ FilterMetadata: map[string]*structpb.Struct{ "test-filter": {}, }, }, wantHashKey: "", }, { name: "nil envoy.lb", metadata: &v3corepb.Metadata{ FilterMetadata: map[string]*structpb.Struct{ "envoy.lb": nil, }, }, wantHashKey: "", }, { name: "envoy.lb without hash key", metadata: &v3corepb.Metadata{ FilterMetadata: map[string]*structpb.Struct{ "envoy.lb": { Fields: map[string]*structpb.Value{ "hash_key": { Kind: &structpb.Value_NumberValue{NumberValue: 123.0}, }, }, }, }, }, wantHashKey: "", }, { name: "envoy.lb with hash key, compat mode off", metadata: &v3corepb.Metadata{ FilterMetadata: map[string]*structpb.Struct{ "envoy.lb": { Fields: map[string]*structpb.Value{ "hash_key": { Kind: &structpb.Value_StringValue{StringValue: "test-hash-key"}, }, }, }, }, }, wantHashKey: "test-hash-key", }, { name: "envoy.lb with hash key, compat mode on", metadata: &v3corepb.Metadata{ FilterMetadata: map[string]*structpb.Struct{ "envoy.lb": { Fields: map[string]*structpb.Value{ "hash_key": { Kind: &structpb.Value_StringValue{StringValue: "test-hash-key"}, }, }, }, }, }, wantHashKey: "", compatEnvVar: true, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { testutils.SetEnvConfig(t, &envconfig.XDSEndpointHashKeyBackwardCompat, test.compatEnvVar) cla := proto.Clone(baseCLA).(*v3endpointpb.ClusterLoadAssignment) cla.Endpoints[0].LbEndpoints[0].Metadata = test.metadata marshalledCLA := testutils.MarshalAny(t, cla) _, update, err := unmarshalEndpointsResource(marshalledCLA) if err != nil { t.Fatalf("unmarshalEndpointsResource() got error = %v, want success", err) } got := ringhash.HashKey(update.Localities[0].Endpoints[0].ResolverEndpoint) if got != test.wantHashKey { t.Errorf("unmarshalEndpointResource() endpoint hash key: got %s, want %s", got, test.wantHashKey) } }) } } func (s) TestUnmarshalEndpoints(t *testing.T) { enableA86(t) var v3EndpointsAny = testutils.MarshalAny(t, func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) 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) 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() }()) tests := []struct { name string resource *anypb.Any wantName string wantUpdate EndpointsUpdate wantErr bool }{ { name: "non-clusterLoadAssignment_resourcetype", resource: &anypb.Any{TypeUrl: version.V3HTTPConnManagerURL}, wantErr: true, }, { name: "badly_marshaled_clusterLoadAssignment_resource", resource: &anypb.Any{ TypeUrl: version.V3EndpointsURL, Value: []byte{1, 2, 3, 4}, }, wantErr: true, }, { name: "bad_endpoints_resource", resource: testutils.MarshalAny(t, func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) clab0.addLocality("locality-1", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, nil) clab0.addLocality("locality-2", 1, 2, []endpointOpts{{addrWithPort: "addr2:159"}}, nil) return clab0.Build() }()), wantName: "test", wantErr: true, }, { name: "endpoint_resource_with_empty_cluster_name", resource: testutils.MarshalAny(t, func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("", nil) clab0.addLocality("locality-1", 1, 0, []endpointOpts{{addrWithPort: "addr1:314"}}, nil) clab0.addLocality("locality-2", 1, 2, []endpointOpts{{addrWithPort: "addr2:159"}}, nil) return clab0.Build() }()), wantName: "", wantErr: true, }, { name: "v3_endpoints", resource: v3EndpointsAny, wantName: "test", wantUpdate: EndpointsUpdate{ Drops: nil, Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnhealthy, Weight: 271, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 1, Weight: 1, }, { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr2:159"}, "addr2"), HealthStatus: EndpointHealthStatusDraining, Weight: 828, }}, ID: clients.Locality{SubZone: "locality-2"}, Priority: 0, Weight: 1, }, }, Raw: v3EndpointsAny, }, }, { name: "v3_endpoints_wrapped", resource: testutils.MarshalAny(t, &v3discoverypb.Resource{Resource: v3EndpointsAny}), wantName: "test", wantUpdate: EndpointsUpdate{ Drops: nil, Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnhealthy, Weight: 271, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 1, Weight: 1, }, { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr2:159"}, "addr2"), HealthStatus: EndpointHealthStatusDraining, Weight: 828, }}, ID: clients.Locality{SubZone: "locality-2"}, Priority: 0, Weight: 1, }, }, Raw: v3EndpointsAny, }, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { name, update, err := unmarshalEndpointsResource(test.resource) if (err != nil) != test.wantErr { t.Fatalf("unmarshalEndpointsResource(%s), got err: %v, wantErr: %v", pretty.ToJSON(test.resource), err, test.wantErr) } if name != test.wantName { t.Errorf("unmarshalEndpointsResource(%s), got name: %s, want: %s", pretty.ToJSON(test.resource), name, test.wantName) } if diff := cmp.Diff(update, test.wantUpdate, cmpOpts); diff != "" { t.Errorf("unmarshalEndpointsResource(%s), got unexpected update, diff (-got +want): %v", pretty.ToJSON(test.resource), diff) } }) } } // Tests custom metadata parsing for success cases when the // GRPC_EXPERIMENTAL_XDS_HTTP_CONNECT environment variable is set. func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOn(t *testing.T) { enableA86(t) tests := []struct { name string endpointProto *v3endpointpb.ClusterLoadAssignment wantEndpoint EndpointsUpdate }{ { name: "typed_filter_metadata_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "test-key": testutils.MarshalAny(t, &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "1.2.3.4", PortSpecifier: &v3corepb.SocketAddress_PortValue{ PortValue: 1111, }}, }, }), }, }, hostname: "addr1", }} clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, Metadata: map[string]any{ "test-key": ProxyAddressMetadataValue{ Address: "1.2.3.4:1111", }, }, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, }, }, }, }, { name: "filter_metadata_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ FilterMetadata: map[string]*structpb.Struct{ "test-key": { Fields: map[string]*structpb.Value{ "key": { Kind: &structpb.Value_NumberValue{NumberValue: 123.0}, }, }, }, }, }, hostname: "addr1", }} clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, Metadata: map[string]any{ "test-key": StructMetadataValue{Data: map[string]any{ "key": float64(123), }}, }, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, }, }, }, }, { name: "typed_filter_metadata_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{addrWithPort: "addr1:314", hostname: "addr1"}} locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "test-key": testutils.MarshalAny(t, &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "1.2.3.4", PortSpecifier: &v3corepb.SocketAddress_PortValue{ PortValue: 1111, }}, }, }), }, }, } clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, Metadata: map[string]any{ "test-key": ProxyAddressMetadataValue{ Address: "1.2.3.4:1111", }, }, }, }, }, }, { name: "filter_metadata_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{addrWithPort: "addr1:314", hostname: "addr1"}} locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ FilterMetadata: map[string]*structpb.Struct{ "test-key": { Fields: map[string]*structpb.Value{ "key": { Kind: &structpb.Value_NumberValue{NumberValue: 123.0}, }, }, }, }, }, } clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, Metadata: map[string]any{ "test-key": StructMetadataValue{Data: map[string]any{ "key": float64(123), }}, }, }, }, }, }, { name: "typed_filter_metadata_over_filter_metadata_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "test-key": testutils.MarshalAny(t, &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "1.2.3.4", PortSpecifier: &v3corepb.SocketAddress_PortValue{ PortValue: 1111, }}, }, }), }, FilterMetadata: map[string]*structpb.Struct{ "test-key": { Fields: map[string]*structpb.Value{ "key": { Kind: &structpb.Value_NumberValue{NumberValue: 123.0}, }, }, }, }, }, hostname: "addr1", }} clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, Metadata: map[string]any{ "test-key": ProxyAddressMetadataValue{ Address: "1.2.3.4:1111", }, }, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, }, }, }, }, { name: "typed_filter_metadata_over_filter_metadata_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{addrWithPort: "addr1:314", hostname: "addr1"}} locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "test-key": testutils.MarshalAny(t, &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "1.2.3.4", PortSpecifier: &v3corepb.SocketAddress_PortValue{ PortValue: 1111, }}, }, }), }, FilterMetadata: map[string]*structpb.Struct{ "test-key": { Fields: map[string]*structpb.Value{ "key": { Kind: &structpb.Value_NumberValue{NumberValue: 123.0}, }, }, }, }, }, } clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, Metadata: map[string]any{ "test-key": ProxyAddressMetadataValue{ Address: "1.2.3.4:1111", }, }, }, }, }, }, { name: "both_filter_and_typed_filter_metadata_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "test-key": testutils.MarshalAny(t, &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "1.2.3.4", PortSpecifier: &v3corepb.SocketAddress_PortValue{ PortValue: 1111, }}, }, }), }, FilterMetadata: map[string]*structpb.Struct{ "another-test-key": { Fields: map[string]*structpb.Value{ "key": { Kind: &structpb.Value_NumberValue{NumberValue: 123.0}, }, }, }, }, }, hostname: "addr1", }} clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, Metadata: map[string]any{ "test-key": ProxyAddressMetadataValue{ Address: "1.2.3.4:1111", }, "another-test-key": StructMetadataValue{Data: map[string]any{ "key": float64(123), }}, }, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, }, }, }, }, { name: "both_filter_and_typed_filter_metadata_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{addrWithPort: "addr1:314", hostname: "addr1"}} locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "test-key": testutils.MarshalAny(t, &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "1.2.3.4", PortSpecifier: &v3corepb.SocketAddress_PortValue{ PortValue: 1111, }}, }, }), }, FilterMetadata: map[string]*structpb.Struct{ "another-test-key": { Fields: map[string]*structpb.Value{ "key": { Kind: &structpb.Value_NumberValue{NumberValue: 123.0}, }, }, }, }, }, } clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, Metadata: map[string]any{ "test-key": ProxyAddressMetadataValue{ Address: "1.2.3.4:1111", }, "another-test-key": StructMetadataValue{Data: map[string]any{ "key": float64(123), }}, }, }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := parseEDSRespProto(tt.endpointProto) if err != nil { t.Fatalf("parseEDSRespProto() failed: %v", err) } if diff := cmp.Diff(tt.wantEndpoint, got, cmpopts.EquateEmpty()); diff != "" { t.Errorf("parseEDSRespProto() returned unexpected diff (-want +got):\n%s", diff) } }) } } // Tests custom metadata parsing for success cases when the // GRPC_EXPERIMENTAL_XDS_HTTP_CONNECT environment variable is not set and // GRPC_XDS_ENDPOINT_HASH_KEY_BACKWARD_COMPAT is set to true (disabling A76). func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_EnvVarOff(t *testing.T) { disableA86(t) testutils.SetEnvConfig(t, &envconfig.XDSEndpointHashKeyBackwardCompat, true) tests := []struct { name string endpointProto *v3endpointpb.ClusterLoadAssignment wantEndpoint EndpointsUpdate }{ { name: "typed_filter_metadata_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "test-key": testutils.MarshalAny(t, &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "1.2.3.4", PortSpecifier: &v3corepb.SocketAddress_PortValue{ PortValue: 1111, }}, }, }), }, }, hostname: "addr1", }} clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, }, }, }, }, { name: "filter_metadata_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ FilterMetadata: map[string]*structpb.Struct{ "test-key": { Fields: map[string]*structpb.Value{ "key": { Kind: &structpb.Value_NumberValue{NumberValue: 123.0}, }, }, }, }, }, hostname: "addr1", }} clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, }, }, }, }, { name: "typed_filter_metadata_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{addrWithPort: "addr1:314", hostname: "addr1"}} locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "test-key": testutils.MarshalAny(t, &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "1.2.3.4", PortSpecifier: &v3corepb.SocketAddress_PortValue{ PortValue: 1111, }}, }, }), }, }, } clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, }, }, }, }, { name: "filter_metadata_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{addrWithPort: "addr1:314", hostname: "addr1"}} locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ FilterMetadata: map[string]*structpb.Struct{ "test-key": { Fields: map[string]*structpb.Value{ "key": { Kind: &structpb.Value_NumberValue{NumberValue: 123.0}, }, }, }, }, }, } clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, }, }, }, }, { name: "both_filter_and_typed_filter_metadata_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "test-key": testutils.MarshalAny(t, &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "1.2.3.4", PortSpecifier: &v3corepb.SocketAddress_PortValue{ PortValue: 1111, }}, }, }), }, FilterMetadata: map[string]*structpb.Struct{ "another-test-key": { Fields: map[string]*structpb.Value{ "key": { Kind: &structpb.Value_NumberValue{NumberValue: 123.0}, }, }, }, }, }, hostname: "addr1", }} clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, }, }, }, }, { name: "both_filter_and_typed_filter_metadata_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{addrWithPort: "addr1:314", hostname: "addr1"}} locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "test-key": testutils.MarshalAny(t, &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "1.2.3.4", PortSpecifier: &v3corepb.SocketAddress_PortValue{ PortValue: 1111, }}, }, }), }, FilterMetadata: map[string]*structpb.Struct{ "another-test-key": { Fields: map[string]*structpb.Value{ "key": { Kind: &structpb.Value_NumberValue{NumberValue: 123.0}, }, }, }, }, }, } clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, }, }, }, }, { name: "converter_failure_is_not_triggerred", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "envoy.http11_proxy_transport_socket.proxy_address": testutils.MarshalAny(t, &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "invalid", }, }, }), }, }, hostname: "addr1", }} locOption := &addLocalityOptions{ Metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "envoy.http11_proxy_transport_socket.proxy_address": testutils.MarshalAny(t, &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "invalid", }, }, }), }, }, } clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), wantEndpoint: EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := parseEDSRespProto(tt.endpointProto) if err != nil { t.Fatalf("parseEDSRespProto() failed: %v", err) } if diff := cmp.Diff(tt.wantEndpoint, got, cmpopts.EquateEmpty()); diff != "" { t.Errorf("parseEDSRespProto() returned unexpected diff (-want +got):\n%s", diff) } }) } } // Tests custom metadata parsing for converter failure cases when the // GRPC_EXPERIMENTAL_XDS_HTTP_CONNECT environment variable is set. func (s) TestEDSParseRespProto_HTTP_Connect_CustomMetadata_ConverterFailure(t *testing.T) { enableA86(t) tests := []struct { name string endpointProto *v3endpointpb.ClusterLoadAssignment }{ { name: "converter_failure_in_endpoint", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "envoy.http11_proxy_transport_socket.proxy_address": testutils.MarshalAny(t, &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "invalid", }, }, }), }, }, }} clab0.addLocality("locality-1", 1, 0, endpoints, nil) return clab0.Build() }(), }, { name: "converter_failure_in_locality", endpointProto: func() *v3endpointpb.ClusterLoadAssignment { clab0 := newClaBuilder("test", nil) 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{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "invalid", }, }, }), }, }, } clab0.addLocality("locality-1", 1, 0, endpoints, locOption) return clab0.Build() }(), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if _, err := parseEDSRespProto(tt.endpointProto); err == nil { t.Fatalf("parseEDSRespProto() did not return error when expected") } }) } } // Tests metadata parsing when HTTP Connect is enabled but A76 hash key is // disabled (backward compat mode). This verifies that: // - Metadata parsing happens (TypedFilterMetadata + FilterMetadata) // - Hash key is NOT extracted from envoy.lb func (s) TestEDSParseRespProto_HTTP_Connect_On_HashKeyBackwardCompat_On(t *testing.T) { enableA86(t) testutils.SetEnvConfig(t, &envconfig.XDSEndpointHashKeyBackwardCompat, true) clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "envoy.http11_proxy_transport_socket.proxy_address": testutils.MarshalAny(t, &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "1.2.3.4", PortSpecifier: &v3corepb.SocketAddress_PortValue{ PortValue: 1111, }, }, }, }), }, FilterMetadata: map[string]*structpb.Struct{ "envoy.lb": { Fields: map[string]*structpb.Value{ "hash_key": { Kind: &structpb.Value_StringValue{StringValue: "test-hash-key"}, }, }, }, }, }, hostname: "addr1", }} clab0.addLocality("locality-1", 1, 0, endpoints, nil) got, err := parseEDSRespProto(clab0.Build()) if err != nil { t.Fatalf("parseEDSRespProto() failed: %v", err) } wantEndpoint := EndpointsUpdate{ Localities: []Locality{ { Endpoints: []Endpoint{{ ResolverEndpoint: buildResolverEndpoint([]string{"addr1:314"}, "addr1"), HealthStatus: EndpointHealthStatusUnknown, Weight: 1, Metadata: map[string]any{ "envoy.http11_proxy_transport_socket.proxy_address": ProxyAddressMetadataValue{ Address: "1.2.3.4:1111", }, "envoy.lb": StructMetadataValue{Data: map[string]any{ "hash_key": "test-hash-key", }}, }, }}, ID: clients.Locality{SubZone: "locality-1"}, Priority: 0, Weight: 1, }, }, } if diff := cmp.Diff(wantEndpoint, got, cmpopts.EquateEmpty()); diff != "" { t.Errorf("parseEDSRespProto() returned unexpected diff (-want +got):\n%s", diff) } // Verify hash key is NOT extracted when backward compat is on. hashKey := ringhash.HashKey(got.Localities[0].Endpoints[0].ResolverEndpoint) if hashKey != "" { t.Errorf("Expected empty hash key with backward compat on, got %q", hashKey) } } // Tests that when A76 is enabled but A86 is disabled, invalid typed metadata // does not cause a parsing failure, and hash key is still extracted. func (s) TestEDSParseRespProto_HTTP_Connect_Off_HashKeyBackwardCompat_Off_InvalidTypedMetadata(t *testing.T) { disableA86(t) testutils.SetEnvConfig(t, &envconfig.XDSEndpointHashKeyBackwardCompat, false) // A76 on clab0 := newClaBuilder("test", nil) endpoints := []endpointOpts{{ addrWithPort: "addr1:314", metadata: &v3corepb.Metadata{ TypedFilterMetadata: map[string]*anypb.Any{ "envoy.http11_proxy_transport_socket.proxy_address": testutils.MarshalAny(t, &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Address: "invalid", // This would fail conversion if processed. }, }, }), }, FilterMetadata: map[string]*structpb.Struct{ "envoy.lb": { Fields: map[string]*structpb.Value{ "hash_key": { Kind: &structpb.Value_StringValue{StringValue: "test-hash-key"}, }, }, }, }, }, hostname: "addr1", }} clab0.addLocality("locality-1", 1, 0, endpoints, nil) got, err := parseEDSRespProto(clab0.Build()) if err != nil { t.Fatalf("parseEDSRespProto() failed unexpectedly with A76 on and A86 off: %v", err) } // Verify hash key is extracted when A76 is on. hashKey := ringhash.HashKey(got.Localities[0].Endpoints[0].ResolverEndpoint) if want := "test-hash-key"; hashKey != want { t.Errorf("Expected hash key %q with A76 on, got %q", want, hashKey) } } // claBuilder builds a ClusterLoadAssignment, aka EDS // response. type claBuilder struct { v *v3endpointpb.ClusterLoadAssignment } // newClaBuilder creates a claBuilder. func newClaBuilder(clusterName string, dropPercents []uint32) *claBuilder { var drops []*v3endpointpb.ClusterLoadAssignment_Policy_DropOverload for i, d := range dropPercents { drops = append(drops, &v3endpointpb.ClusterLoadAssignment_Policy_DropOverload{ Category: fmt.Sprintf("test-drop-%d", i), DropPercentage: &v3typepb.FractionalPercent{ Numerator: d, Denominator: v3typepb.FractionalPercent_HUNDRED, }, }) } return &claBuilder{ v: &v3endpointpb.ClusterLoadAssignment{ ClusterName: clusterName, Policy: &v3endpointpb.ClusterLoadAssignment_Policy{ DropOverloads: drops, }, }, } } // addLocalityOptions contains options when adding locality to the builder. type addLocalityOptions struct { Health []v3corepb.HealthStatus Weight []uint32 Metadata *v3corepb.Metadata } type endpointOpts struct { addrWithPort string additionalAddrWithPorts []string metadata *v3corepb.Metadata hostname string } func addressFromStr(addrWithPort string) *v3corepb.Address { host, portStr, err := net.SplitHostPort(addrWithPort) if err != nil { panic("failed to split " + addrWithPort) } port, err := strconv.Atoi(portStr) if err != nil { panic("failed to atoi " + portStr) } return &v3corepb.Address{ Address: &v3corepb.Address_SocketAddress{ SocketAddress: &v3corepb.SocketAddress{ Protocol: v3corepb.SocketAddress_TCP, Address: host, PortSpecifier: &v3corepb.SocketAddress_PortValue{PortValue: uint32(port)}, }, }, } } // addLocality adds a locality to the builder. func (clab *claBuilder) addLocality(subzone string, weight uint32, priority uint32, endpoints []endpointOpts, opts *addLocalityOptions) { var lbEndPoints []*v3endpointpb.LbEndpoint for i, e := range endpoints { var additionalAddrs []*v3endpointpb.Endpoint_AdditionalAddress for _, a := range e.additionalAddrWithPorts { additionalAddrs = append(additionalAddrs, &v3endpointpb.Endpoint_AdditionalAddress{ Address: addressFromStr(a), }) } lbe := &v3endpointpb.LbEndpoint{ HostIdentifier: &v3endpointpb.LbEndpoint_Endpoint{ Endpoint: &v3endpointpb.Endpoint{ Address: addressFromStr(e.addrWithPort), AdditionalAddresses: additionalAddrs, Hostname: e.hostname, }, }, Metadata: e.metadata, } if opts != nil { if i < len(opts.Health) { lbe.HealthStatus = opts.Health[i] } if i < len(opts.Weight) { lbe.LoadBalancingWeight = &wrapperspb.UInt32Value{Value: opts.Weight[i]} } } lbEndPoints = append(lbEndPoints, lbe) } var localityID *v3corepb.Locality if subzone != "" { localityID = &v3corepb.Locality{ Region: "", Zone: "", SubZone: subzone, } } locality := &v3endpointpb.LocalityLbEndpoints{ Locality: localityID, LbEndpoints: lbEndPoints, LoadBalancingWeight: &wrapperspb.UInt32Value{Value: weight}, Priority: priority, } if opts != nil { locality.Metadata = opts.Metadata } clab.v.Endpoints = append(clab.v.Endpoints, locality) } // Build builds ClusterLoadAssignment. func (clab *claBuilder) Build() *v3endpointpb.ClusterLoadAssignment { return clab.v }