Skip to content

Commit 0381eb6

Browse files
authored
xds: Support :authority header rewriting for LOGICAL_DNS clusters (#8822)
## What This PR enables Auto Host Rewriting for Logical_DNS clusters by setting DNS Hostname as an Endpoint Attribute. ## Why With current grpc-go's gRFC A81 implementation, Auto Host Rewriting is not enabled even when a Route config enables AutoHostRewrite and GRPC_EXPERIMENTAL_XDS_AUTHORITY_REWRITE is set to be true when a Cluster config uses Logical DNS (current implementation seems supports only EDS). RELEASE NOTES: - xDS: Added support for :authority rewriting (gRFC A81) for LOGICAL_DNS clusters
1 parent 90f571d commit 0381eb6

File tree

4 files changed

+97
-50
lines changed

4 files changed

+97
-50
lines changed

internal/xds/balancer/clusterimpl/tests/balancer_test.go

Lines changed: 89 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,7 +1304,7 @@ func setupManagementServerAndResolver(t *testing.T) (*e2e.ManagementServer, reso
13041304

13051305
// configureXDSResources configures the management server with a route that
13061306
// enables auto_host_rewrite and an endpoint with the specified hostname.
1307-
func configureXDSResources(ctx context.Context, t *testing.T, mgmtServer *e2e.ManagementServer, nodeID string, serverAddr string, endpointHostname string, secLevel e2e.SecurityLevel) {
1307+
func configureXDSResources(ctx context.Context, t *testing.T, mgmtServer *e2e.ManagementServer, nodeID, serverAddr, endpointHostname string, secLevel e2e.SecurityLevel, clusterType e2e.ClusterType) {
13081308
t.Helper()
13091309

13101310
const (
@@ -1314,16 +1314,30 @@ func configureXDSResources(ctx context.Context, t *testing.T, mgmtServer *e2e.Ma
13141314
endpointName = "endpoints-my-test-xds-service"
13151315
)
13161316

1317+
port := testutils.ParsePort(t, serverAddr)
1318+
13171319
resources := e2e.DefaultClientResources(e2e.ResourceParams{
13181320
DialTarget: serviceName,
13191321
NodeID: nodeID,
13201322
Host: "localhost",
1321-
Port: testutils.ParsePort(t, serverAddr),
1323+
Port: port,
13221324
SecLevel: secLevel,
13231325
})
13241326

1325-
// Set the endpoint hostname for authority rewriting.
1326-
resources.Endpoints[0].Endpoints[0].LbEndpoints[0].GetEndpoint().Hostname = endpointHostname
1327+
if clusterType == e2e.ClusterTypeLogicalDNS {
1328+
resources.Clusters = []*v3clusterpb.Cluster{
1329+
e2e.ClusterResourceWithOptions(e2e.ClusterOptions{
1330+
ClusterName: clusterName,
1331+
Type: e2e.ClusterTypeLogicalDNS,
1332+
DNSHostName: endpointHostname,
1333+
DNSPort: port,
1334+
}),
1335+
}
1336+
resources.Endpoints = nil
1337+
} else {
1338+
// Set the endpoint hostname for authority rewriting.
1339+
resources.Endpoints[0].Endpoints[0].LbEndpoints[0].GetEndpoint().Hostname = endpointHostname
1340+
}
13271341

13281342
// Modify the route to enable AutoHostRewrite.
13291343
resources.Routes[0].VirtualHosts[0].Routes[0].GetRoute().HostRewriteSpecifier = &v3routepb.RouteAction_AutoHostRewrite{
@@ -1339,54 +1353,84 @@ func configureXDSResources(ctx context.Context, t *testing.T, mgmtServer *e2e.Ma
13391353
// rewritten to the endpoint's hostname. Also verifies that CallAuthority
13401354
// call option takes precedence.
13411355
func (s) TestAuthorityOverriding(t *testing.T) {
1342-
testutils.SetEnvConfig(t, &envconfig.XDSAuthorityRewrite, true)
1343-
mgmtServer, resolverBuilder, nodeID := setupManagementServerAndResolver(t)
1344-
1345-
// Start a server backend exposing the test service.
1346-
var gotAuthority string
1347-
f := &stubserver.StubServer{
1348-
EmptyCallF: func(ctx context.Context, _ *testpb.Empty) (*testpb.Empty, error) {
1349-
if md, ok := metadata.FromIncomingContext(ctx); ok {
1350-
if authVals := md.Get(":authority"); len(authVals) > 0 {
1351-
gotAuthority = authVals[0]
1352-
}
1353-
}
1354-
return &testpb.Empty{}, nil
1356+
tests := []struct {
1357+
name string
1358+
clusterType e2e.ClusterType
1359+
}{
1360+
{
1361+
name: "EDS",
1362+
clusterType: e2e.ClusterTypeEDS,
1363+
},
1364+
{
1365+
name: "LogicalDNS",
1366+
clusterType: e2e.ClusterTypeLogicalDNS,
13551367
},
13561368
}
1357-
server := stubserver.StartTestService(t, f)
1358-
defer server.Stop()
13591369

1360-
const xdsAuthorityOverride = "rewritten.example.com"
1361-
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
1362-
defer cancel()
1363-
configureXDSResources(ctx, t, mgmtServer, nodeID, server.Address, xdsAuthorityOverride, e2e.SecurityLevelNone)
1370+
for _, test := range tests {
1371+
t.Run(test.name, func(t *testing.T) {
1372+
testutils.SetEnvConfig(t, &envconfig.XDSAuthorityRewrite, true)
1373+
mgmtServer, resolverBuilder, nodeID := setupManagementServerAndResolver(t)
13641374

1365-
// Create a ClientConn and make a successful RPC.
1366-
cc, err := grpc.NewClient("xds:///my-test-xds-service", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(resolverBuilder))
1367-
if err != nil {
1368-
t.Fatalf("Failed to create client: %v", err)
1369-
}
1370-
defer cc.Close()
1375+
// Start a server backend exposing the test service.
1376+
var gotAuthority string
1377+
f := &stubserver.StubServer{
1378+
EmptyCallF: func(ctx context.Context, _ *testpb.Empty) (*testpb.Empty, error) {
1379+
if md, ok := metadata.FromIncomingContext(ctx); ok {
1380+
if authVals := md.Get(":authority"); len(authVals) > 0 {
1381+
gotAuthority = authVals[0]
1382+
}
1383+
}
1384+
return &testpb.Empty{}, nil
1385+
},
1386+
}
1387+
server := stubserver.StartTestService(t, f)
1388+
defer server.Stop()
13711389

1372-
client := testgrpc.NewTestServiceClient(cc)
1373-
if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil {
1374-
t.Fatalf("client.EmptyCall() failed: %v", err)
1375-
}
1390+
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
1391+
defer cancel()
13761392

1377-
if gotAuthority != xdsAuthorityOverride {
1378-
t.Errorf("invalid authority got: %q, want: %q", gotAuthority, xdsAuthorityOverride)
1379-
}
1393+
var hostname string
1394+
if test.clusterType == e2e.ClusterTypeEDS {
1395+
hostname = "rewritten.example.com"
1396+
} else {
1397+
hostname, _ = hostAndPortFromAddress(t, server.Address)
1398+
}
1399+
configureXDSResources(ctx, t, mgmtServer, nodeID, server.Address, hostname, e2e.SecurityLevelNone, test.clusterType)
13801400

1381-
// The authority specified via the `CallAuthority` CallOption takes the
1382-
// highest precedence when determining the `:authority` header.
1383-
const userAuthorityOverride = "user-override.com"
1384-
if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.CallAuthority(userAuthorityOverride)); err != nil {
1385-
t.Fatalf("client.EmptyCall() failed: %v", err)
1386-
}
1401+
// Create a ClientConn and make a successful RPC.
1402+
cc, err := grpc.NewClient("xds:///my-test-xds-service", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithResolvers(resolverBuilder))
1403+
if err != nil {
1404+
t.Fatalf("Failed to create client: %v", err)
1405+
}
1406+
defer cc.Close()
1407+
1408+
client := testgrpc.NewTestServiceClient(cc)
1409+
if _, err := client.EmptyCall(ctx, &testpb.Empty{}); err != nil {
1410+
t.Fatalf("client.EmptyCall() failed: %v", err)
1411+
}
13871412

1388-
if gotAuthority != userAuthorityOverride {
1389-
t.Errorf("Server received authority %q, want %q (user override)", gotAuthority, userAuthorityOverride)
1413+
var wantAuthority string
1414+
if test.clusterType == e2e.ClusterTypeEDS {
1415+
wantAuthority = "rewritten.example.com"
1416+
} else {
1417+
wantAuthority = server.Address
1418+
}
1419+
if gotAuthority != wantAuthority {
1420+
t.Errorf("invalid authority got: %q, want: %q", gotAuthority, wantAuthority)
1421+
}
1422+
1423+
// The authority specified via the `CallAuthority` CallOption takes the
1424+
// highest precedence when determining the `:authority` header.
1425+
const userAuthorityOverride = "user-override.com"
1426+
if _, err := client.EmptyCall(ctx, &testpb.Empty{}, grpc.CallAuthority(userAuthorityOverride)); err != nil {
1427+
t.Fatalf("client.EmptyCall() failed: %v", err)
1428+
}
1429+
1430+
if gotAuthority != userAuthorityOverride {
1431+
t.Errorf("Server received authority %q, want %q (user override)", gotAuthority, userAuthorityOverride)
1432+
}
1433+
})
13901434
}
13911435
}
13921436

@@ -1442,7 +1486,7 @@ func (s) TestAuthorityOverridingWithTLS(t *testing.T) {
14421486

14431487
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
14441488
defer cancel()
1445-
configureXDSResources(ctx, t, mgmtServer, nodeID, f.Address, test.xdsAuthorityOverride, e2e.SecurityLevelMTLS)
1489+
configureXDSResources(ctx, t, mgmtServer, nodeID, f.Address, test.xdsAuthorityOverride, e2e.SecurityLevelMTLS, e2e.ClusterTypeEDS)
14461490

14471491
// Create ClientConn with TLS
14481492
cc, err := grpc.NewClient("xds:///my-test-xds-service", grpc.WithTransportCredentials(clientCreds), grpc.WithResolvers(resolverBuilder))

internal/xds/balancer/clusterresolver/configbuilder.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,10 @@ func buildClusterImplConfigForDNS(g *nameGenerator, endpoints []resolver.Endpoin
144144
retEndpoints := make([]resolver.Endpoint, len(endpoints))
145145
pName := fmt.Sprintf("priority-%v", g.prefix)
146146
for i, e := range endpoints {
147-
retEndpoints[i] = hierarchy.SetInEndpoint(e, []string{pName})
147+
// For Logical DNS clusters, the same hostname attribute is added
148+
// to all endpoints. It is set to the name that is resolved for the
149+
// Logical DNS cluster, including the port number.
150+
retEndpoints[i] = xdsresource.SetHostname(hierarchy.SetInEndpoint(e, []string{pName}), mechanism.DNSHostname)
148151
// Copy the nested address field as slice fields are shared by the
149152
// iteration variable and the original slice.
150153
retEndpoints[i].Addresses = append([]resolver.Address{}, e.Addresses...)

internal/xds/xdsclient/xdsresource/unmarshal_eds.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ import (
4040
// a resolver.Endpoint.
4141
type hostnameKeyType struct{}
4242

43-
// setHostname returns a copy of the given endpoint with hostname added
43+
// SetHostname returns a copy of the given endpoint with hostname added
4444
// as an attribute.
45-
func setHostname(endpoint resolver.Endpoint, hostname string) resolver.Endpoint {
45+
func SetHostname(endpoint resolver.Endpoint, hostname string) resolver.Endpoint {
4646
// Only set if non-empty; xds_cluster_impl uses this to trigger :authority
4747
// rewriting.
4848
if hostname == "" {
@@ -154,7 +154,7 @@ func parseEndpoints(lbEndpoints []*v3endpointpb.LbEndpoint, uniqueEndpointAddrs
154154
}
155155
}
156156
endpoint := resolver.Endpoint{Addresses: address}
157-
endpoint = setHostname(endpoint, lbEndpoint.GetEndpoint().GetHostname())
157+
endpoint = SetHostname(endpoint, lbEndpoint.GetEndpoint().GetHostname())
158158
endpoint = ringhash.SetHashKey(endpoint, hashKey)
159159
endpoints = append(endpoints, Endpoint{
160160
ResolverEndpoint: endpoint,

internal/xds/xdsclient/xdsresource/unmarshal_eds_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func buildResolverEndpoint(addr []string, hostname string) resolver.Endpoint {
4848
address = append(address, resolver.Address{Addr: a})
4949
}
5050
resolverEndpoint := resolver.Endpoint{Addresses: address}
51-
resolverEndpoint = setHostname(resolverEndpoint, hostname)
51+
resolverEndpoint = SetHostname(resolverEndpoint, hostname)
5252
return resolverEndpoint
5353
}
5454

0 commit comments

Comments
 (0)