2828import static io .grpc .xds .XdsTestUtils .getWrrLbConfigAsMap ;
2929import static org .junit .Assert .assertEquals ;
3030import static org .junit .Assert .assertNotNull ;
31+ import static org .junit .Assert .assertNotSame ;
3132import static org .junit .Assert .assertNull ;
3233import static org .junit .Assert .assertSame ;
3334import static org .junit .Assert .assertTrue ;
3435import static org .mockito .ArgumentMatchers .eq ;
3536import static org .mockito .Mockito .mock ;
37+ import static org .mockito .Mockito .times ;
3638import static org .mockito .Mockito .verify ;
3739
3840import com .google .common .collect .ImmutableList ;
@@ -89,8 +91,8 @@ public class GcpAuthenticationFilterTest {
8991
9092 @ Test
9193 public void testNewFilterInstancesPerFilterName () {
92- assertThat (new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME1" ))
93- .isNotEqualTo (new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME1" ));
94+ assertThat (new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME1" , 10 ))
95+ .isNotEqualTo (new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME1" , 10 ));
9496 }
9597
9698 @ Test
@@ -152,7 +154,7 @@ public void testClientInterceptor_success() throws IOException, ResourceInvalidE
152154 .withOption (CLUSTER_SELECTION_KEY , "cluster:cluster0" )
153155 .withOption (XDS_CONFIG_CALL_OPTION_KEY , defaultXdsConfig );
154156 GcpAuthenticationConfig config = new GcpAuthenticationConfig (10 );
155- GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" );
157+ GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" , 10 );
156158 ClientInterceptor interceptor = filter .buildClientInterceptor (config , null , null );
157159 MethodDescriptor <Void , Void > methodDescriptor = TestMethodDescriptors .voidMethod ();
158160 Channel mockChannel = Mockito .mock (Channel .class );
@@ -181,7 +183,7 @@ public void testClientInterceptor_createsAndReusesCachedCredentials()
181183 .withOption (CLUSTER_SELECTION_KEY , "cluster:cluster0" )
182184 .withOption (XDS_CONFIG_CALL_OPTION_KEY , defaultXdsConfig );
183185 GcpAuthenticationConfig config = new GcpAuthenticationConfig (10 );
184- GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" );
186+ GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" , 10 );
185187 ClientInterceptor interceptor = filter .buildClientInterceptor (config , null , null );
186188 MethodDescriptor <Void , Void > methodDescriptor = TestMethodDescriptors .voidMethod ();
187189 Channel mockChannel = Mockito .mock (Channel .class );
@@ -190,7 +192,7 @@ public void testClientInterceptor_createsAndReusesCachedCredentials()
190192 interceptor .interceptCall (methodDescriptor , callOptionsWithXds , mockChannel );
191193 interceptor .interceptCall (methodDescriptor , callOptionsWithXds , mockChannel );
192194
193- verify (mockChannel , Mockito . times (2 ))
195+ verify (mockChannel , times (2 ))
194196 .newCall (eq (methodDescriptor ), callOptionsCaptor .capture ());
195197 CallOptions firstCapturedOptions = callOptionsCaptor .getAllValues ().get (0 );
196198 CallOptions secondCapturedOptions = callOptionsCaptor .getAllValues ().get (1 );
@@ -202,7 +204,7 @@ public void testClientInterceptor_createsAndReusesCachedCredentials()
202204 @ Test
203205 public void testClientInterceptor_withoutClusterSelectionKey () throws Exception {
204206 GcpAuthenticationConfig config = new GcpAuthenticationConfig (10 );
205- GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" );
207+ GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" , 10 );
206208 ClientInterceptor interceptor = filter .buildClientInterceptor (config , null , null );
207209 MethodDescriptor <Void , Void > methodDescriptor = TestMethodDescriptors .voidMethod ();
208210 Channel mockChannel = mock (Channel .class );
@@ -233,7 +235,7 @@ public void testClientInterceptor_clusterSelectionKeyWithoutPrefix() throws Exce
233235 Channel mockChannel = mock (Channel .class );
234236
235237 GcpAuthenticationConfig config = new GcpAuthenticationConfig (10 );
236- GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" );
238+ GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" , 10 );
237239 ClientInterceptor interceptor = filter .buildClientInterceptor (config , null , null );
238240 MethodDescriptor <Void , Void > methodDescriptor = TestMethodDescriptors .voidMethod ();
239241 interceptor .interceptCall (methodDescriptor , callOptionsWithXds , mockChannel );
@@ -244,7 +246,7 @@ public void testClientInterceptor_clusterSelectionKeyWithoutPrefix() throws Exce
244246 @ Test
245247 public void testClientInterceptor_xdsConfigDoesNotExist () throws Exception {
246248 GcpAuthenticationConfig config = new GcpAuthenticationConfig (10 );
247- GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" );
249+ GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" , 10 );
248250 ClientInterceptor interceptor = filter .buildClientInterceptor (config , null , null );
249251 MethodDescriptor <Void , Void > methodDescriptor = TestMethodDescriptors .voidMethod ();
250252 Channel mockChannel = mock (Channel .class );
@@ -274,7 +276,7 @@ public void testClientInterceptor_incorrectClusterName() throws Exception {
274276 .withOption (CLUSTER_SELECTION_KEY , "cluster:cluster" )
275277 .withOption (XDS_CONFIG_CALL_OPTION_KEY , defaultXdsConfig );
276278 GcpAuthenticationConfig config = new GcpAuthenticationConfig (10 );
277- GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" );
279+ GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" , 10 );
278280 ClientInterceptor interceptor = filter .buildClientInterceptor (config , null , null );
279281 MethodDescriptor <Void , Void > methodDescriptor = TestMethodDescriptors .voidMethod ();
280282 Channel mockChannel = mock (Channel .class );
@@ -300,7 +302,7 @@ public void testClientInterceptor_statusOrError() throws Exception {
300302 .withOption (CLUSTER_SELECTION_KEY , "cluster:cluster0" )
301303 .withOption (XDS_CONFIG_CALL_OPTION_KEY , defaultXdsConfig );
302304 GcpAuthenticationConfig config = new GcpAuthenticationConfig (10 );
303- GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" );
305+ GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" , 10 );
304306 ClientInterceptor interceptor = filter .buildClientInterceptor (config , null , null );
305307 MethodDescriptor <Void , Void > methodDescriptor = TestMethodDescriptors .voidMethod ();
306308 Channel mockChannel = mock (Channel .class );
@@ -329,7 +331,7 @@ public void testClientInterceptor_notAudienceWrapper()
329331 .withOption (CLUSTER_SELECTION_KEY , "cluster:cluster0" )
330332 .withOption (XDS_CONFIG_CALL_OPTION_KEY , defaultXdsConfig );
331333 GcpAuthenticationConfig config = new GcpAuthenticationConfig (10 );
332- GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" );
334+ GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" , 10 );
333335 ClientInterceptor interceptor = filter .buildClientInterceptor (config , null , null );
334336 MethodDescriptor <Void , Void > methodDescriptor = TestMethodDescriptors .voidMethod ();
335337 Channel mockChannel = Mockito .mock (Channel .class );
@@ -342,6 +344,115 @@ public void testClientInterceptor_notAudienceWrapper()
342344 assertThat (clientCall .error .getDescription ()).contains ("GCP Authn found wrong type" );
343345 }
344346
347+ @ Test
348+ public void testLruCacheAcrossInterceptors () throws IOException , ResourceInvalidException {
349+ XdsConfig .XdsClusterConfig clusterConfig = new XdsConfig .XdsClusterConfig (
350+ CLUSTER_NAME , cdsUpdate , new EndpointConfig (StatusOr .fromValue (edsUpdate )));
351+ XdsConfig defaultXdsConfig = new XdsConfig .XdsConfigBuilder ()
352+ .setListener (ldsUpdate )
353+ .setRoute (rdsUpdate )
354+ .setVirtualHost (rdsUpdate .virtualHosts .get (0 ))
355+ .addCluster (CLUSTER_NAME , StatusOr .fromValue (clusterConfig )).build ();
356+ CallOptions callOptionsWithXds = CallOptions .DEFAULT
357+ .withOption (CLUSTER_SELECTION_KEY , "cluster:cluster0" )
358+ .withOption (XDS_CONFIG_CALL_OPTION_KEY , defaultXdsConfig );
359+ GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" , 2 );
360+ ClientInterceptor interceptor1
361+ = filter .buildClientInterceptor (new GcpAuthenticationConfig (2 ), null , null );
362+ MethodDescriptor <Void , Void > methodDescriptor = TestMethodDescriptors .voidMethod ();
363+ Channel mockChannel = Mockito .mock (Channel .class );
364+ ArgumentCaptor <CallOptions > callOptionsCaptor = ArgumentCaptor .forClass (CallOptions .class );
365+
366+ interceptor1 .interceptCall (methodDescriptor , callOptionsWithXds , mockChannel );
367+ verify (mockChannel ).newCall (eq (methodDescriptor ), callOptionsCaptor .capture ());
368+ CallOptions capturedOptions1 = callOptionsCaptor .getAllValues ().get (0 );
369+ assertNotNull (capturedOptions1 .getCredentials ());
370+ ClientInterceptor interceptor2
371+ = filter .buildClientInterceptor (new GcpAuthenticationConfig (1 ), null , null );
372+ interceptor2 .interceptCall (methodDescriptor , callOptionsWithXds , mockChannel );
373+ verify (mockChannel , times (2 ))
374+ .newCall (eq (methodDescriptor ), callOptionsCaptor .capture ());
375+ CallOptions capturedOptions2 = callOptionsCaptor .getAllValues ().get (1 );
376+ assertNotNull (capturedOptions2 .getCredentials ());
377+
378+ assertSame (capturedOptions1 .getCredentials (), capturedOptions2 .getCredentials ());
379+ }
380+
381+ @ Test
382+ public void testLruCacheEvictionOnResize () throws IOException , ResourceInvalidException {
383+ XdsConfig .XdsClusterConfig clusterConfig = new XdsConfig .XdsClusterConfig (
384+ CLUSTER_NAME , cdsUpdate , new EndpointConfig (StatusOr .fromValue (edsUpdate )));
385+ XdsConfig defaultXdsConfig = new XdsConfig .XdsConfigBuilder ()
386+ .setListener (ldsUpdate )
387+ .setRoute (rdsUpdate )
388+ .setVirtualHost (rdsUpdate .virtualHosts .get (0 ))
389+ .addCluster (CLUSTER_NAME , StatusOr .fromValue (clusterConfig )).build ();
390+ CallOptions callOptionsWithXds = CallOptions .DEFAULT
391+ .withOption (CLUSTER_SELECTION_KEY , "cluster:cluster0" )
392+ .withOption (XDS_CONFIG_CALL_OPTION_KEY , defaultXdsConfig );
393+ GcpAuthenticationFilter filter = new GcpAuthenticationFilter ("FILTER_INSTANCE_NAME" , 2 );
394+ MethodDescriptor <Void , Void > methodDescriptor = TestMethodDescriptors .voidMethod ();
395+
396+ ClientInterceptor interceptor1 =
397+ filter .buildClientInterceptor (new GcpAuthenticationConfig (2 ), null , null );
398+ Channel mockChannel1 = Mockito .mock (Channel .class );
399+ ArgumentCaptor <CallOptions > captor = ArgumentCaptor .forClass (CallOptions .class );
400+ interceptor1 .interceptCall (methodDescriptor , callOptionsWithXds , mockChannel1 );
401+ verify (mockChannel1 ).newCall (eq (methodDescriptor ), captor .capture ());
402+ CallOptions options1 = captor .getValue ();
403+ // This will recreate the cache with max size of 1 and copy the credential for audience1.
404+ ClientInterceptor interceptor2 =
405+ filter .buildClientInterceptor (new GcpAuthenticationConfig (1 ), null , null );
406+ Channel mockChannel2 = Mockito .mock (Channel .class );
407+ interceptor2 .interceptCall (methodDescriptor , callOptionsWithXds , mockChannel2 );
408+ verify (mockChannel2 ).newCall (eq (methodDescriptor ), captor .capture ());
409+ CallOptions options2 = captor .getValue ();
410+
411+ assertSame (options1 .getCredentials (), options2 .getCredentials ());
412+
413+ clusterConfig = new XdsConfig .XdsClusterConfig (
414+ CLUSTER_NAME , getCdsUpdate2 (), new EndpointConfig (StatusOr .fromValue (edsUpdate )));
415+ defaultXdsConfig = new XdsConfig .XdsConfigBuilder ()
416+ .setListener (ldsUpdate )
417+ .setRoute (rdsUpdate )
418+ .setVirtualHost (rdsUpdate .virtualHosts .get (0 ))
419+ .addCluster (CLUSTER_NAME , StatusOr .fromValue (clusterConfig )).build ();
420+ callOptionsWithXds = CallOptions .DEFAULT
421+ .withOption (CLUSTER_SELECTION_KEY , "cluster:cluster0" )
422+ .withOption (XDS_CONFIG_CALL_OPTION_KEY , defaultXdsConfig );
423+
424+ // This will evict the credential for audience1 and add new credential for audience2
425+ ClientInterceptor interceptor3 =
426+ filter .buildClientInterceptor (new GcpAuthenticationConfig (1 ), null , null );
427+ Channel mockChannel3 = Mockito .mock (Channel .class );
428+ interceptor3 .interceptCall (methodDescriptor , callOptionsWithXds , mockChannel3 );
429+ verify (mockChannel3 ).newCall (eq (methodDescriptor ), captor .capture ());
430+ CallOptions options3 = captor .getValue ();
431+
432+ assertNotSame (options1 .getCredentials (), options3 .getCredentials ());
433+
434+ clusterConfig = new XdsConfig .XdsClusterConfig (
435+ CLUSTER_NAME , cdsUpdate , new EndpointConfig (StatusOr .fromValue (edsUpdate )));
436+ defaultXdsConfig = new XdsConfig .XdsConfigBuilder ()
437+ .setListener (ldsUpdate )
438+ .setRoute (rdsUpdate )
439+ .setVirtualHost (rdsUpdate .virtualHosts .get (0 ))
440+ .addCluster (CLUSTER_NAME , StatusOr .fromValue (clusterConfig )).build ();
441+ callOptionsWithXds = CallOptions .DEFAULT
442+ .withOption (CLUSTER_SELECTION_KEY , "cluster:cluster0" )
443+ .withOption (XDS_CONFIG_CALL_OPTION_KEY , defaultXdsConfig );
444+
445+ // This will create new credential for audience1 because it has been evicted
446+ ClientInterceptor interceptor4 =
447+ filter .buildClientInterceptor (new GcpAuthenticationConfig (1 ), null , null );
448+ Channel mockChannel4 = Mockito .mock (Channel .class );
449+ interceptor4 .interceptCall (methodDescriptor , callOptionsWithXds , mockChannel4 );
450+ verify (mockChannel4 ).newCall (eq (methodDescriptor ), captor .capture ());
451+ CallOptions options4 = captor .getValue ();
452+
453+ assertNotSame (options1 .getCredentials (), options4 .getCredentials ());
454+ }
455+
345456 private static LdsUpdate getLdsUpdate () {
346457 Filter .NamedFilterConfig routerFilterConfig = new Filter .NamedFilterConfig (
347458 serverName , RouterFilter .ROUTER_CONFIG );
@@ -384,6 +495,19 @@ private static CdsUpdate getCdsUpdate() {
384495 }
385496 }
386497
498+ private static CdsUpdate getCdsUpdate2 () {
499+ ImmutableMap .Builder <String , Object > parsedMetadata = ImmutableMap .builder ();
500+ parsedMetadata .put ("FILTER_INSTANCE_NAME" , new AudienceWrapper ("NEW_TEST_AUDIENCE" ));
501+ try {
502+ CdsUpdate .Builder cdsUpdate = CdsUpdate .forEds (
503+ CLUSTER_NAME , EDS_NAME , null , null , null , null , false )
504+ .lbPolicyConfig (getWrrLbConfigAsMap ());
505+ return cdsUpdate .parsedMetadata (parsedMetadata .build ()).build ();
506+ } catch (IOException ex ) {
507+ return null ;
508+ }
509+ }
510+
387511 private static CdsUpdate getCdsUpdateWithIncorrectAudienceWrapper () throws IOException {
388512 ImmutableMap .Builder <String , Object > parsedMetadata = ImmutableMap .builder ();
389513 parsedMetadata .put ("FILTER_INSTANCE_NAME" , "TEST_AUDIENCE" );
0 commit comments