@@ -140,6 +140,91 @@ func (s) TestClientSideFederation(t *testing.T) {
140
140
}
141
141
}
142
142
143
+ // TestClientSideFederationWithOnlyXDSTPStyleLDS tests that federation is
144
+ // supported with new xdstp style names for LDS only while using the old style
145
+ // for other resources. This test in addition also checks that when service name
146
+ // contains escapable characters, we "fully" encode it for looking up
147
+ // VirtualHosts in xDS RouteConfigurtion.
148
+ func (s ) TestClientSideFederationWithOnlyXDSTPStyleLDS (t * testing.T ) {
149
+ // Start a management server as a sophisticated authority.
150
+ const authority = "traffic-manager.xds.notgoogleapis.com"
151
+ mgmtServer , err := e2e .StartManagementServer (e2e.ManagementServerOptions {})
152
+ if err != nil {
153
+ t .Fatalf ("Failed to spin up the xDS management server: %v" , err )
154
+ }
155
+ t .Cleanup (mgmtServer .Stop )
156
+
157
+ // Create a bootstrap file in a temporary directory.
158
+ nodeID := uuid .New ().String ()
159
+ bootstrapContents , err := bootstrap .Contents (bootstrap.Options {
160
+ NodeID : nodeID ,
161
+ ServerURI : mgmtServer .Address ,
162
+ ClientDefaultListenerResourceNameTemplate : fmt .Sprintf ("xdstp://%s/envoy.config.listener.v3.Listener/%%s" , authority ),
163
+ Authorities : map [string ]string {authority : mgmtServer .Address },
164
+ })
165
+ if err != nil {
166
+ t .Fatalf ("Failed to create bootstrap file: %v" , err )
167
+ }
168
+
169
+ resolverBuilder := internal .NewXDSResolverWithConfigForTesting .(func ([]byte ) (resolver.Builder , error ))
170
+ resolver , err := resolverBuilder (bootstrapContents )
171
+ if err != nil {
172
+ t .Fatalf ("Failed to create xDS resolver for testing: %v" , err )
173
+ }
174
+ server := stubserver .StartTestService (t , nil )
175
+ defer server .Stop ()
176
+
177
+ // serviceName with escapable characters - ' ', and '/'.
178
+ const serviceName = "my-service-client-side-xds/2nd component"
179
+
180
+ // All other resources are with old style name.
181
+ const rdsName = "route-" + serviceName
182
+ const cdsName = "cluster-" + serviceName
183
+ const edsName = "endpoints-" + serviceName
184
+
185
+ // Resource update sent to go-control-plane mgmt server.
186
+ resourceUpdate := e2e.UpdateOptions {
187
+ NodeID : nodeID ,
188
+ Listeners : func () []* v3listenerpb.Listener {
189
+ // LDS is new style xdstp name. Since the LDS resource name is prefixed
190
+ // with xdstp, the string will be %-encoded excluding '/'s. See
191
+ // bootstrap.PopulateResourceTemplate().
192
+ const specialEscapedServiceName = "my-service-client-side-xds/2nd%20component" // same as bootstrap.percentEncode(serviceName)
193
+ ldsName := fmt .Sprintf ("xdstp://%s/envoy.config.listener.v3.Listener/%s" , authority , specialEscapedServiceName )
194
+ return []* v3listenerpb.Listener {e2e .DefaultClientListener (ldsName , rdsName )}
195
+ }(),
196
+ Routes : func () []* v3routepb.RouteConfiguration {
197
+ // RouteConfiguration will has one entry in []VirutalHosts that contains the
198
+ // "fully" escaped service name in []Domains. This is to assert that gRPC
199
+ // uses the escaped service name to lookup VirtualHosts. RDS is also with
200
+ // old style name.
201
+ const fullyEscapedServiceName = "my-service-client-side-xds%2F2nd%20component" // same as url.PathEscape(serviceName)
202
+ return []* v3routepb.RouteConfiguration {e2e .DefaultRouteConfig (rdsName , fullyEscapedServiceName , cdsName )}
203
+ }(),
204
+ Clusters : []* v3clusterpb.Cluster {e2e .DefaultCluster (cdsName , edsName , e2e .SecurityLevelNone )},
205
+ Endpoints : []* v3endpointpb.ClusterLoadAssignment {e2e .DefaultEndpoint (edsName , "localhost" , []uint32 {testutils .ParsePort (t , server .Address )})},
206
+ SkipValidation : true ,
207
+ }
208
+
209
+ ctx , cancel := context .WithTimeout (context .Background (), defaultTestTimeout )
210
+ defer cancel ()
211
+ if err := mgmtServer .Update (ctx , resourceUpdate ); err != nil {
212
+ t .Fatal (err )
213
+ }
214
+
215
+ // Create a ClientConn and make a successful RPC.
216
+ cc , err := grpc .Dial (fmt .Sprintf ("xds:///%s" , serviceName ), grpc .WithTransportCredentials (insecure .NewCredentials ()), grpc .WithResolvers (resolver ))
217
+ if err != nil {
218
+ t .Fatalf ("failed to dial local test server: %v" , err )
219
+ }
220
+ defer cc .Close ()
221
+
222
+ client := testgrpc .NewTestServiceClient (cc )
223
+ if _ , err := client .EmptyCall (ctx , & testpb.Empty {}, grpc .WaitForReady (true )); err != nil {
224
+ t .Fatalf ("rpc EmptyCall() failed: %v" , err )
225
+ }
226
+ }
227
+
143
228
// TestFederation_UnknownAuthorityInDialTarget tests the case where a ClientConn
144
229
// is created with a dial target containing an authority which is not specified
145
230
// in the bootstrap configuration. The test verifies that RPCs on the ClientConn
0 commit comments