@@ -316,6 +316,139 @@ name: my_route
316316 " vhost_vhds1" == actual_vhost_2.name ());
317317}
318318
319+ // verify that when RDS and VHDS define virtual hosts with the same name, VHDS takes precedence
320+ TEST_F (VhdsTest, VhdsOverridesRdsVirtualHostWithSameName) {
321+ const auto route_config =
322+ TestUtility::parseYaml<envoy::config::route::v3::RouteConfiguration>(R"EOF(
323+ name: my_route
324+ virtual_hosts:
325+ - name: overlapping_vhost
326+ domains: ["vhost.rds.domain"]
327+ routes:
328+ - match: { prefix: "/rds" }
329+ route: { cluster: rds_service }
330+ - name: vhost_rds_only
331+ domains: ["vhost.rds.only"]
332+ routes:
333+ - match: { prefix: "/rdsonly" }
334+ route: { cluster: rds_only_service }
335+ vhds:
336+ config_source:
337+ api_config_source:
338+ api_type: DELTA_GRPC
339+ grpc_services:
340+ envoy_grpc:
341+ cluster_name: xds_cluster
342+ )EOF" );
343+ RouteConfigUpdatePtr config_update_info = makeRouteConfigUpdate (route_config);
344+
345+ VhdsSubscriptionPtr subscription = VhdsSubscription::createVhdsSubscription (
346+ config_update_info, factory_context_, context_, provider_)
347+ .value ();
348+ EXPECT_EQ (2UL , config_update_info->protobufConfigurationCast ().virtual_hosts_size ());
349+
350+ // Add a VHDS virtual host with the same name as one of the RDS virtual hosts
351+ auto vhost = buildVirtualHost (" overlapping_vhost" , " vhost.vhds.domain" );
352+ const auto & added_resources = buildAddedResources ({vhost});
353+ const auto decoded_resources =
354+ TestUtility::decodeResources<envoy::config::route::v3::VirtualHost>(added_resources);
355+ const Protobuf::RepeatedPtrField<std::string> removed_resources;
356+ EXPECT_TRUE (factory_context_.cluster_manager_ .subscription_factory_ .callbacks_
357+ ->onConfigUpdate (decoded_resources.refvec_ , removed_resources, " 1" )
358+ .ok ());
359+
360+ // Should have 2 virtual hosts: vhost_rds_only from RDS + overlapping_vhost from VHDS
361+ EXPECT_EQ (2UL , config_update_info->protobufConfigurationCast ().virtual_hosts_size ());
362+
363+ // Verify the VHDS version of the overlapping vhost is used (with vhds domain, not rds domain)
364+ bool found_vhds_version = false ;
365+ bool found_rds_only = false ;
366+ for (int i = 0 ; i < config_update_info->protobufConfigurationCast ().virtual_hosts_size (); ++i) {
367+ const auto & vh = config_update_info->protobufConfigurationCast ().virtual_hosts (i);
368+ if (vh.name () == " overlapping_vhost" ) {
369+ // The VHDS version should have "vhost.vhds.domain", not "vhost.rds.domain"
370+ EXPECT_EQ (" vhost.vhds.domain" , vh.domains (0 ));
371+ found_vhds_version = true ;
372+ } else if (vh.name () == " vhost_rds_only" ) {
373+ found_rds_only = true ;
374+ }
375+ }
376+ EXPECT_TRUE (found_vhds_version);
377+ EXPECT_TRUE (found_rds_only);
378+ }
379+
380+ // verify VHDS precedence is maintained after an RDS update
381+ TEST_F (VhdsTest, VhdsOverridesRdsVirtualHostAfterRdsUpdate) {
382+ const auto route_config =
383+ TestUtility::parseYaml<envoy::config::route::v3::RouteConfiguration>(R"EOF(
384+ name: my_route
385+ virtual_hosts:
386+ - name: overlapping_vhost
387+ domains: ["vhost.rds.domain"]
388+ routes:
389+ - match: { prefix: "/rds" }
390+ route: { cluster: rds_service }
391+ vhds:
392+ config_source:
393+ api_config_source:
394+ api_type: DELTA_GRPC
395+ grpc_services:
396+ envoy_grpc:
397+ cluster_name: xds_cluster
398+ )EOF" );
399+ RouteConfigUpdatePtr config_update_info = makeRouteConfigUpdate (route_config);
400+
401+ VhdsSubscriptionPtr subscription = VhdsSubscription::createVhdsSubscription (
402+ config_update_info, factory_context_, context_, provider_)
403+ .value ();
404+
405+ // Add a VHDS virtual host with the same name
406+ auto vhost = buildVirtualHost (" overlapping_vhost" , " vhost.vhds.domain" );
407+ const auto & added_resources = buildAddedResources ({vhost});
408+ const auto decoded_resources =
409+ TestUtility::decodeResources<envoy::config::route::v3::VirtualHost>(added_resources);
410+ const Protobuf::RepeatedPtrField<std::string> removed_resources;
411+ EXPECT_TRUE (factory_context_.cluster_manager_ .subscription_factory_ .callbacks_
412+ ->onConfigUpdate (decoded_resources.refvec_ , removed_resources, " 1" )
413+ .ok ());
414+
415+ // Now trigger an RDS update that still has the same-named vhost
416+ const auto updated_route_config =
417+ TestUtility::parseYaml<envoy::config::route::v3::RouteConfiguration>(R"EOF(
418+ name: my_route
419+ virtual_hosts:
420+ - name: overlapping_vhost
421+ domains: ["vhost.rds.updated"]
422+ routes:
423+ - match: { prefix: "/rds-updated" }
424+ route: { cluster: rds_updated_service }
425+ - name: vhost_rds_new
426+ domains: ["vhost.rds.new"]
427+ routes:
428+ - match: { prefix: "/rdsnew" }
429+ route: { cluster: rds_new_service }
430+ vhds:
431+ config_source:
432+ api_config_source:
433+ api_type: DELTA_GRPC
434+ grpc_services:
435+ envoy_grpc:
436+ cluster_name: xds_cluster
437+ )EOF" );
438+ config_update_info->onRdsUpdate (updated_route_config, " 2" );
439+
440+ // Should have 2: vhost_rds_new from RDS + overlapping_vhost from VHDS
441+ EXPECT_EQ (2UL , config_update_info->protobufConfigurationCast ().virtual_hosts_size ());
442+
443+ // Verify VHDS version still takes precedence after RDS update
444+ for (int i = 0 ; i < config_update_info->protobufConfigurationCast ().virtual_hosts_size (); ++i) {
445+ const auto & vh = config_update_info->protobufConfigurationCast ().virtual_hosts (i);
446+ if (vh.name () == " overlapping_vhost" ) {
447+ EXPECT_EQ (" vhost.vhds.domain" , vh.domains (0 ));
448+ }
449+ }
450+ }
451+
319452} // namespace
320453} // namespace Router
321454} // namespace Envoy
0 commit comments