From dc99268ae43aaefab2c18f66446c6b2c3a66eaee Mon Sep 17 00:00:00 2001 From: Kateryna Nezdolii Date: Thu, 9 Apr 2026 13:58:40 +0000 Subject: [PATCH] Support ADS mode in cilium proxy When npds_config is enabled in bpf_metadata extension and set to and ads type, stream all resources via ADS. Since Envoy already supports streaming of core resource types via ads, only additional logic is needed for network policies and network policy hosts. There is no ordering dependency between network policies and network policy hosts (they can be streamed independently). Signed-off-by: Kateryna Nezdolii --- cilium/BUILD | 4 + cilium/grpc_subscription.cc | 3 +- cilium/grpc_subscription.h | 6 +- cilium/host_map.cc | 49 ++++- cilium/host_map.h | 19 +- cilium/network_policy.cc | 36 +++- cilium/network_policy.h | 13 +- tests/BUILD | 25 +++ tests/bpf_metadata_integration_test.cc | 271 +++++++++++++++++++++++++ tests/cilium_http_integration_test.cc | 4 +- 10 files changed, 401 insertions(+), 29 deletions(-) create mode 100644 tests/bpf_metadata_integration_test.cc diff --git a/cilium/BUILD b/cilium/BUILD index 4d6dd1e10..ca9e1269e 100644 --- a/cilium/BUILD +++ b/cilium/BUILD @@ -111,6 +111,7 @@ envoy_cc_library( ], repository = "@envoy", deps = [ + "@com_google_absl//absl/strings", "@envoy//envoy/network:connection_interface", "@envoy//source/common/config:type_to_endpoint_lib", "@envoy//source/extensions/config_subscription/grpc:grpc_subscription_lib", @@ -336,6 +337,7 @@ envoy_cc_library( "//cilium:socket_option_lib", "//cilium/api:bpf_metadata_cc_proto", "//cilium/api:nphds_cc_proto", + "@com_google_absl//absl/strings", "@envoy//envoy/buffer:buffer_interface", "@envoy//envoy/config:subscription_interface", "@envoy//envoy/network:connection_interface", @@ -343,6 +345,8 @@ envoy_cc_library( "@envoy//envoy/registry", "@envoy//envoy/server:filter_config_interface", "@envoy//envoy/singleton:manager_interface", + "@envoy//envoy/stats:stats_interface", + "@envoy//envoy/stats:stats_macros", "@envoy//source/common/common:assert_lib", "@envoy//source/common/common:logger_lib", "@envoy//source/common/network:address_lib", diff --git a/cilium/grpc_subscription.cc b/cilium/grpc_subscription.cc index 891b66662..e07b67231 100644 --- a/cilium/grpc_subscription.cc +++ b/cilium/grpc_subscription.cc @@ -121,7 +121,8 @@ const Protobuf::MethodDescriptor& sotwGrpcMethod(absl::string_view type_url) { } std::unique_ptr -subscribe(const std::string& type_url, const envoy::config::core::v3::ConfigSource& npds_config, +subscribe(const absl::string_view type_url, + const envoy::config::core::v3::ConfigSource& npds_config, const LocalInfo::LocalInfo& local_info, Upstream::ClusterManager& cm, Event::Dispatcher& dispatcher, Random::RandomGenerator& random, Stats::Scope& scope, Config::SubscriptionCallbacks& callbacks, diff --git a/cilium/grpc_subscription.h b/cilium/grpc_subscription.h index 1a158b0e6..0d511de1c 100644 --- a/cilium/grpc_subscription.h +++ b/cilium/grpc_subscription.h @@ -2,7 +2,6 @@ #include #include -#include #include "envoy/common/random_generator.h" #include "envoy/config/core/v3/config_source.pb.h" @@ -16,6 +15,8 @@ #include "source/extensions/config_subscription/grpc/grpc_mux_impl.h" #include "source/extensions/config_subscription/grpc/grpc_subscription_impl.h" +#include "absl/strings/string_view.h" + namespace Envoy { namespace Cilium { @@ -44,7 +45,8 @@ class GrpcMuxImpl : public Config::GrpcMuxImpl { }; std::unique_ptr -subscribe(const std::string& type_url, const envoy::config::core::v3::ConfigSource& npds_config, +subscribe(const absl::string_view type_url, + const envoy::config::core::v3::ConfigSource& npds_config, const LocalInfo::LocalInfo& local_info, Upstream::ClusterManager& cm, Event::Dispatcher& dispatcher, Random::RandomGenerator& random, Stats::Scope& scope, Config::SubscriptionCallbacks& callbacks, diff --git a/cilium/host_map.cc b/cilium/host_map.cc index f8621b2ca..eabfa34bf 100644 --- a/cilium/host_map.cc +++ b/cilium/host_map.cc @@ -16,6 +16,8 @@ #include "envoy/config/subscription.h" #include "envoy/event/dispatcher.h" #include "envoy/server/factory_context.h" +#include "envoy/stats/scope.h" +#include "envoy/stats/stats_macros.h" #include "envoy/thread_local/thread_local.h" #include "envoy/thread_local/thread_local_object.h" @@ -24,6 +26,7 @@ #include "absl/numeric/int128.h" #include "absl/status/status.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "cilium/api/nphds.pb.h" #include "cilium/grpc_subscription.h" @@ -31,6 +34,11 @@ namespace Envoy { namespace Cilium { +namespace { + +static constexpr absl::string_view NetworkPolicyHostsTypeUrl = + "type.googleapis.com/cilium.NetworkPolicyHosts"; + template unsigned int checkPrefix(T addr, bool have_prefix, unsigned int plen, absl::string_view host) { const unsigned int plen_max = sizeof(T) * 8; @@ -47,6 +55,8 @@ unsigned int checkPrefix(T addr, bool have_prefix, unsigned int plen, absl::stri return plen; } +} // namespace + struct ThreadLocalHostMapInitializer : public PolicyHostMap::ThreadLocalHostMap { protected: friend class PolicyHostMap; // PolicyHostMap can insert(); @@ -154,7 +164,11 @@ struct ThreadLocalHostMapInitializer : public PolicyHostMap::ThreadLocalHostMap uint64_t PolicyHostMap::instance_id_ = 0; // This is used directly for testing with a file-based subscription -PolicyHostMap::PolicyHostMap(ThreadLocal::SlotAllocator& tls) : tls_(tls.allocateSlot()) { +PolicyHostMap::PolicyHostMap(ThreadLocal::SlotAllocator& tls, Stats::Scope& scope) + : tls_(tls.allocateSlot()), + name_(absl::StrCat("cilium.hostmap.", fmt::format("{}", instance_id_ + 1), ".")), + scope_(scope.createScope(name_)), stats_scope_(scope.createScope("cilium.hostmap.")), + stats_({CILIUM_POLICY_HOSTS_STATS(POOL_COUNTER(*stats_scope_))}) { instance_id_++; name_ = "cilium.hostmap." + fmt::format("{}", instance_id_) + "."; ENVOY_LOG(debug, "PolicyHostMap({}) created.", name_); @@ -167,16 +181,36 @@ PolicyHostMap::PolicyHostMap(ThreadLocal::SlotAllocator& tls) : tls_(tls.allocat // This is used in production PolicyHostMap::PolicyHostMap(Server::Configuration::CommonFactoryContext& context) - : PolicyHostMap(context.threadLocal()) { - scope_ = context.serverScope().createScope(name_); + : tls_(context.threadLocal().allocateSlot()), + name_(absl::StrCat("cilium.hostmap.", fmt::format("{}", instance_id_ + 1), ".")), + scope_(context.serverScope().createScope(name_)), + stats_scope_(context.serverScope().createScope("cilium.hostmap.")), + stats_({CILIUM_POLICY_HOSTS_STATS(POOL_COUNTER(*stats_scope_))}) { + instance_id_++; + ENVOY_LOG(debug, "PolicyHostMap({}) created.", name_); + + auto empty_map = std::make_shared(); + tls_->set([empty_map](Event::Dispatcher&) -> ThreadLocal::ThreadLocalObjectSharedPtr { + return empty_map; + }); } void PolicyHostMap::startSubscription(Server::Configuration::CommonFactoryContext& context, const envoy::config::core::v3::ConfigSource& npds_config) { - subscription_ = subscribe("type.googleapis.com/cilium.NetworkPolicyHosts", npds_config, - context.localInfo(), context.clusterManager(), - context.mainThreadDispatcher(), context.api().randomGenerator(), - *scope_, *this, std::make_shared()); + if (npds_config.config_source_specifier_case() == envoy::config::core::v3::ConfigSource::kAds) { + auto ads_mux = context.xdsManager().adsMux(); + subscription_ = THROW_OR_RETURN_VALUE( + context.clusterManager().subscriptionFactory().subscriptionOverAdsGrpcMux( + ads_mux, npds_config, NetworkPolicyHostsTypeUrl, *scope_, *this, + std::make_shared(), {}), + Config::SubscriptionPtr); + } else { + subscription_ = subscribe(NetworkPolicyHostsTypeUrl, npds_config, context.localInfo(), + context.clusterManager(), context.mainThreadDispatcher(), + context.api().randomGenerator(), *scope_, *this, + std::make_shared()); + } + subscription_->start({}); } @@ -211,6 +245,7 @@ PolicyHostMap::onConfigUpdate(const std::vector, public Logger::Loggable { public: PolicyHostMap(Server::Configuration::CommonFactoryContext& context); - PolicyHostMap(ThreadLocal::SlotAllocator& tls); + PolicyHostMap(ThreadLocal::SlotAllocator& tls, Stats::Scope& scope); ~PolicyHostMap() override { ENVOY_LOG(debug, "Cilium PolicyHostMap({}): PolicyHostMap is deleted NOW!", name_); } @@ -228,10 +241,12 @@ class PolicyHostMap : public Singleton::Instance, private: ThreadLocal::SlotPtr tls_; + std::string name_; Stats::ScopeSharedPtr scope_; + Stats::ScopeSharedPtr stats_scope_; std::unique_ptr subscription_; static uint64_t instance_id_; - std::string name_; + PolicyHostsStats stats_; }; } // namespace Cilium diff --git a/cilium/network_policy.cc b/cilium/network_policy.cc index 1e8dc5814..da9ed37ae 100644 --- a/cilium/network_policy.cc +++ b/cilium/network_policy.cc @@ -60,6 +60,13 @@ #include "cilium/ipcache.h" #include "cilium/secret_watcher.h" +namespace { + +static constexpr absl::string_view NetworkPolicyTypeUrl = + "type.googleapis.com/cilium.NetworkPolicy"; + +} // namespace + namespace fmt { template <> struct formatter { @@ -1838,7 +1845,7 @@ NetworkPolicyMap::NetworkPolicyMap(Server::Configuration::FactoryContext& contex } if (subscribe) { - getImpl().startSubscription(); + getImpl().startSubscription(npds_config); } } @@ -1877,8 +1884,7 @@ NetworkPolicyMapImpl::NetworkPolicyMapImpl(Server::Configuration::FactoryContext context_, *npds_stats_scope_, context_.messageValidationContext().dynamicValidationVisitor())), npds_config_(npds_config), - stats_{ALL_CILIUM_POLICY_STATS(POOL_COUNTER(*policy_stats_scope_), - POOL_HISTOGRAM(*policy_stats_scope_))} { + stats_{ALL_CILIUM_POLICY_STATS(POOL_COUNTER(*policy_stats_scope_))} { // Use listener init manager for subscription initialization context.initManager().add(init_target_); @@ -1894,11 +1900,23 @@ NetworkPolicyMapImpl::~NetworkPolicyMapImpl() { delete load(); } -void NetworkPolicyMapImpl::startSubscription() { - subscription_ = subscribe("type.googleapis.com/cilium.NetworkPolicy", npds_config_, - context_.localInfo(), context_.clusterManager(), - context_.mainThreadDispatcher(), context_.api().randomGenerator(), - *npds_stats_scope_, *this, std::make_shared()); +void NetworkPolicyMapImpl::startSubscription( + const envoy::config::core::v3::ConfigSource& npds_config) { + if (npds_config.config_source_specifier_case() == envoy::config::core::v3::ConfigSource::kAds) { + auto ads_mux = context_.xdsManager().adsMux(); + subscription_ = THROW_OR_RETURN_VALUE( + context_.clusterManager().subscriptionFactory().subscriptionOverAdsGrpcMux( + ads_mux, npds_config, NetworkPolicyTypeUrl, *npds_stats_scope_, *this, + std::make_shared(), {}), + Config::SubscriptionPtr); + } else { + subscription_ = subscribe(NetworkPolicyTypeUrl, npds_config, context_.localInfo(), + context_.clusterManager(), context_.mainThreadDispatcher(), + context_.api().randomGenerator(), *npds_stats_scope_, *this, + std::make_shared()); + } + + subscription_->start({}); } void NetworkPolicyMapImpl::tlsWrapperMissingPolicyInc() const { @@ -2027,7 +2045,7 @@ absl::Status NetworkPolicyMapImpl::onConfigUpdate( // Clean-up in the main thread after all threads have scheduled delete old_map; }); - + stats_.update_success_.inc(); return absl::OkStatus(); } diff --git a/cilium/network_policy.h b/cilium/network_policy.h index 3682b1252..be03f1bbc 100644 --- a/cilium/network_policy.h +++ b/cilium/network_policy.h @@ -215,17 +215,18 @@ class NetworkPolicyDecoder : public Envoy::Config::OpaqueResourceDecoder { * All Cilium L7 filter stats. @see stats_macros.h */ // clang-format off -#define ALL_CILIUM_POLICY_STATS(COUNTER, HISTOGRAM) \ +#define ALL_CILIUM_POLICY_STATS(COUNTER) \ COUNTER(updates_total) \ COUNTER(updates_rejected) \ - COUNTER(tls_wrapper_missing_policy) + COUNTER(tls_wrapper_missing_policy) \ + COUNTER(update_success) // clang-format on /** * Struct definition for all policy stats. @see stats_macros.h */ struct PolicyStats { - ALL_CILIUM_POLICY_STATS(GENERATE_COUNTER_STRUCT, GENERATE_HISTOGRAM_STRUCT) + ALL_CILIUM_POLICY_STATS(GENERATE_COUNTER_STRUCT) }; using RawPolicyMap = absl::flat_hash_map>; @@ -237,15 +238,15 @@ class NetworkPolicyMapImpl : public Envoy::Config::SubscriptionCallbacks, const envoy::config::core::v3::ConfigSource& npds_config); ~NetworkPolicyMapImpl() override; - void startSubscription(); - - const envoy::config::core::v3::ConfigSource& getConfigSource() const { return npds_config_; } + void startSubscription(const envoy::config::core::v3::ConfigSource& npds_config); // This is used for testing with a file-based subscription void startSubscription(std::unique_ptr&& subscription) { subscription_ = std::move(subscription); } + const envoy::config::core::v3::ConfigSource& getConfigSource() const { return npds_config_; } + // run the given function after all the threads have scheduled void runAfterAllThreads(std::function) const; diff --git a/tests/BUILD b/tests/BUILD index 81f0da418..2a9b3e349 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -328,6 +328,26 @@ envoy_cc_test( ], ) +envoy_cc_test( + name = "bpf_metadata_integration_test", + srcs = ["bpf_metadata_integration_test.cc"], + repository = "@envoy", + deps = [ + "//cilium:bpf_metadata_lib", + "//cilium/api:bpf_metadata_cc_proto", + "//cilium/api:npds_cc_proto", + "//cilium/api:nphds_cc_proto", + "@envoy//envoy/grpc:status", + "@envoy//source/extensions/clusters/original_dst:original_dst_cluster_lib", + "@envoy//source/extensions/clusters/static:static_cluster_lib", + "@envoy//source/extensions/filters/network/tcp_proxy:config", + "@envoy//test/common/grpc:grpc_client_integration_lib", + "@envoy//test/integration:integration_lib", + "@envoy//test/test_common:resources_lib", + "@envoy//test/test_common:utility_lib", + ], +) + envoy_cc_test( name = "health_check_sink_test", srcs = [ @@ -339,9 +359,14 @@ envoy_cc_test( deps = [ ":uds_server_lib", "//cilium:health_check_sink_lib", + "@envoy//envoy/grpc:status", + "@envoy//envoy/http:codec_interface", + "@envoy//envoy/network:address_interface", + "@envoy//source/common/common:assert_lib", "@envoy//test/mocks/access_log:access_log_mocks", "@envoy//test/mocks/server:health_checker_factory_context_mocks", "@envoy//test/test_common:environment_lib", "@envoy//test/test_common:utility_lib", + "@envoy_api//envoy/config/core/v3:pkg_cc_proto", ], ) diff --git a/tests/bpf_metadata_integration_test.cc b/tests/bpf_metadata_integration_test.cc new file mode 100644 index 000000000..338073cd8 --- /dev/null +++ b/tests/bpf_metadata_integration_test.cc @@ -0,0 +1,271 @@ +#include + +#include +#include + +#include "envoy/config/bootstrap/v3/bootstrap.pb.h" +#include "envoy/config/core/v3/config_source.pb.h" +#include "envoy/config/core/v3/grpc_service.pb.h" +#include "envoy/config/listener/v3/listener.pb.h" +#include "envoy/grpc/status.h" +#include "envoy/http/codec.h" +#include "envoy/network/address.h" +#include "envoy/service/discovery/v3/discovery.pb.h" + +#include "source/common/common/assert.h" +#include "source/common/protobuf/utility.h" + +#include "test/common/grpc/grpc_client_integration.h" +#include "test/config/utility.h" +#include "test/integration/base_integration_test.h" +#include "test/integration/fake_upstream.h" +#include "test/test_common/resources.h" +#include "test/test_common/utility.h" + +#include "cilium/api/bpf_metadata.pb.h" +#include "cilium/api/npds.pb.h" +#include "cilium/api/nphds.pb.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace { + +const std::string NetworkPolicyTypeUrl = "type.googleapis.com/cilium.NetworkPolicy"; + +const std::string NetworkPolicyHostsTypeUrl = "type.googleapis.com/cilium.NetworkPolicyHosts"; + +const std::string policy1 = R"EOF( + endpoint_ips: + - '10.1.1.1' + - 'face::1:1:1' + endpoint_id: 2048 + egress_per_port_policies: + - port: 80 + rules: + - remote_policies: [ 222 ] +)EOF"; + +const std::string policy2 = R"EOF( + endpoint_ips: + - '10.2.2.2' + - 'face::2:2:2' + endpoint_id: 4096 + ingress_per_port_policies: + - port: 80 + rules: + - remote_policies: [ 111 ] +)EOF"; + +const std::string policy_host1 = R"EOF( + policy: 111 + host_addresses: [ "10.1.1.1", "f00d::1:1:1" ] +)EOF"; + +const std::string policy_host2 = R"EOF( + policy: 222 + host_addresses: [ "10.2.2.2", "f00d::2:2:2" ] +)EOF"; + +class BpfMetadataIntegrationTest : public BaseIntegrationTest, + public Grpc::GrpcClientIntegrationParamTest { +public: + BpfMetadataIntegrationTest() + : BaseIntegrationTest(ipVersion(), ConfigHelper::baseConfig() + R"EOF( + filter_chains: + - filters: + - name: envoy.filters.network.tcp_proxy + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy + stat_prefix: tcp_stats + cluster: cluster_0 +)EOF") { + skip_tag_extraction_rule_check_ = true; + } + + ~BpfMetadataIntegrationTest() override { resetConnections(); } + + void setGrpcServiceHelper(envoy::config::core::v3::GrpcService& grpc_service, + const std::string& cluster_name, + Network::Address::InstanceConstSharedPtr address) { + setGrpcService(grpc_service, cluster_name, address); + } + + void setUpGrpcLds() { + config_helper_.addConfigModifier([this](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + listener_config_.Swap(bootstrap.mutable_static_resources()->mutable_listeners(0)); + listener_config_.set_name(listener_name_); + bootstrap.mutable_static_resources()->mutable_listeners()->Clear(); + + auto* lds_config_source = bootstrap.mutable_dynamic_resources()->mutable_lds_config(); + lds_config_source->set_resource_api_version(envoy::config::core::v3::ApiVersion::V3); + lds_config_source->mutable_ads(); + }); + } + + // Inject the cilium.bpf_metadata listener filter with npds_config into the listener. + void addBpfMetadataListenerFilter(envoy::config::listener::v3::Listener& listener, bool) { + auto* listener_filter = listener.add_listener_filters(); + listener_filter->set_name("cilium.bpf_metadata"); + + ::cilium::BpfMetadata bpf_config; + bpf_config.set_is_ingress(false); + bpf_config.set_use_nphds(true); + + auto* npds_config = bpf_config.mutable_npds_config(); + npds_config->set_resource_api_version(envoy::config::core::v3::ApiVersion::V3); + npds_config->mutable_ads(); + + listener_filter->mutable_typed_config()->PackFrom(bpf_config); + } + + void initialize() override { + use_lds_ = false; + setUpstreamCount(1); + defer_listener_finalization_ = true; + + config_helper_.addConfigModifier([](envoy::config::bootstrap::v3::Bootstrap& bootstrap) { + // Add the ADS gRPC cluster. + auto* ads_cluster = bootstrap.mutable_static_resources()->add_clusters(); + ads_cluster->MergeFrom(bootstrap.static_resources().clusters()[0]); + ads_cluster->set_name("ads_cluster"); + ConfigHelper::setHttp2(*ads_cluster); + + auto* cds_config = bootstrap.mutable_dynamic_resources()->mutable_cds_config(); + cds_config->set_resource_api_version(envoy::config::core::v3::ApiVersion::V3); + cds_config->mutable_ads(); + + // Configure ADS in bootstrap. + auto* ads_config = bootstrap.mutable_dynamic_resources()->mutable_ads_config(); + ads_config->set_api_type(envoy::config::core::v3::ApiConfigSource::GRPC); + ads_config->set_transport_api_version(envoy::config::core::v3::V3); + envoy::config::core::v3::GrpcService* grpc_service = ads_config->add_grpc_services(); + grpc_service->mutable_envoy_grpc()->set_cluster_name("ads_cluster"); + ads_config->set_set_node_on_first_message_only(true); + }); + + // Must be last modifier — it removes static listeners. + setUpGrpcLds(); + + BaseIntegrationTest::initialize(); + } + + void createUpstreams() override { + BaseIntegrationTest::createUpstreams(); + // ADS upstream (fake_upstreams_[1]). + addFakeUpstream(Envoy::Http::CodecType::HTTP2); + } + + FakeUpstream& getAdsFakeUpstream() const { return *fake_upstreams_[1]; } + + void createAdsStream() { + AssertionResult result = + getAdsFakeUpstream().waitForHttpConnection(*dispatcher_, ads_connection_); + RELEASE_ASSERT(result, result.message()); + auto result2 = ads_connection_->waitForNewStream(*dispatcher_, ads_stream_); + RELEASE_ASSERT(result2, result2.message()); + ads_stream_->startGrpcStream(); + } + + void sendCdsResponse(const std::string& version) { + envoy::service::discovery::v3::DiscoveryResponse response; + response.set_version_info(version); + response.set_type_url(Envoy::Config::TestTypeUrl::get().Cluster); + ASSERT_NE(nullptr, ads_stream_); + ads_stream_->sendGrpcMessage(response); + } + + void sendLdsResponse(const std::vector& listener_configs, + const std::string& version) { + envoy::service::discovery::v3::DiscoveryResponse response; + response.set_version_info(version); + response.set_type_url(Envoy::Config::TestTypeUrl::get().Listener); + for (const auto& listener_config : listener_configs) { + response.add_resources()->PackFrom(listener_config); + } + ASSERT_NE(nullptr, ads_stream_); + ads_stream_->sendGrpcMessage(response); + } + + void sendLdsResponse(const std::vector& listener_configs, + const std::string& version) { + std::vector proto_configs; + proto_configs.reserve(listener_configs.size()); + for (const auto& listener_blob : listener_configs) { + proto_configs.emplace_back( + TestUtility::parseYaml(listener_blob)); + } + sendLdsResponse(proto_configs, version); + } + + void sendNpdsResponse(const std::string& version) { + envoy::service::discovery::v3::DiscoveryResponse response; + response.set_version_info(version); + response.set_type_url(NetworkPolicyTypeUrl); + std::vector proto_configs; + proto_configs.emplace_back(TestUtility::parseYaml(policy1)); + proto_configs.emplace_back(TestUtility::parseYaml(policy2)); + for (const auto& policy_config : proto_configs) { + response.add_resources()->PackFrom(policy_config); + } + ASSERT_NE(nullptr, ads_stream_); + ads_stream_->sendGrpcMessage(response); + } + + void sendNphdsResponse(const std::string& version) { + envoy::service::discovery::v3::DiscoveryResponse response; + response.set_version_info(version); + response.set_type_url(NetworkPolicyHostsTypeUrl); + std::vector proto_configs; + proto_configs.emplace_back(TestUtility::parseYaml(policy_host1)); + proto_configs.emplace_back(TestUtility::parseYaml(policy_host2)); + for (const auto& policy_host_config : proto_configs) { + response.add_resources()->PackFrom(policy_host_config); + } + ASSERT_NE(nullptr, ads_stream_); + ads_stream_->sendGrpcMessage(response); + } + + void resetConnections() { + if (ads_connection_ != nullptr) { + AssertionResult result = ads_connection_->close(); + RELEASE_ASSERT(result, result.message()); + result = ads_connection_->waitForDisconnect(); + RELEASE_ASSERT(result, result.message()); + ads_connection_.reset(); + } + } + + envoy::config::listener::v3::Listener listener_config_; + std::string listener_name_{"testing-listener-0"}; + FakeHttpConnectionPtr ads_connection_; + FakeStreamPtr ads_stream_; +}; + +INSTANTIATE_TEST_SUITE_P(IpVersionsAndGrpcTypes, BpfMetadataIntegrationTest, + GRPC_CLIENT_INTEGRATION_PARAMS); + +TEST_P(BpfMetadataIntegrationTest, BpfMetadataWithNpdsAndNpdhsViaAds) { + on_server_init_function_ = [&]() { + createAdsStream(); + addBpfMetadataListenerFilter(listener_config_, /*use_ads=*/true); + EXPECT_TRUE(compareDiscoveryRequest( + Config::TestTypeUrl::get().Cluster, "", {}, {}, {}, + /*expect_node=*/true, Envoy::Grpc::Status::WellKnownGrpcStatus::Ok, "", ads_stream_.get())); + sendCdsResponse("1"); + EXPECT_TRUE(compareDiscoveryRequest( + Config::TestTypeUrl::get().Listener, "", {}, {}, {}, /*expect_node=*/false, + Grpc::Status::WellKnownGrpcStatus::Ok, "", ads_stream_.get())); + sendLdsResponse({MessageUtil::getYamlStringFromMessage(listener_config_)}, "1"); + }; + initialize(); + + test_server_->waitForCounterGe("listener_manager.lds.update_success", 1); + EXPECT_EQ(test_server_->server().listenerManager().listeners().size(), 1); + sendNpdsResponse("1"); + test_server_->waitForCounterGe("cilium.policy.update_success", 1); + sendNphdsResponse("1"); + test_server_->waitForCounterGe("cilium.hostmap.update_success", 1); +} + +} // namespace +} // namespace Envoy diff --git a/tests/cilium_http_integration_test.cc b/tests/cilium_http_integration_test.cc index 7dc31c734..7386ce2a7 100644 --- a/tests/cilium_http_integration_test.cc +++ b/tests/cilium_http_integration_test.cc @@ -410,7 +410,7 @@ class HostMapTest : public CiliumHttpIntegrationTest { THROW_IF_NOT_OK(MessageUtil::loadFromFile( path, message, ProtobufMessage::getNullValidationVisitor(), *api_.get())); - Envoy::Cilium::PolicyHostMap hmap(tls); + Envoy::Cilium::PolicyHostMap hmap(tls, api_->rootScope()); const auto decoded_resources = THROW_OR_RETURN_VALUE(Config::DecodedResourcesWrapper::create( host_decoder, message.resources(), message.version_info()), @@ -451,7 +451,7 @@ TEST_P(HostMapTest, HostMapValid) { THROW_IF_NOT_OK(MessageUtil::loadFromFile( path, message, ProtobufMessage::getNullValidationVisitor(), *api_.get())); - auto hmap = std::make_shared(tls); + auto hmap = std::make_shared(tls, api_->rootScope()); const auto decoded_resources = THROW_OR_RETURN_VALUE(Config::DecodedResourcesWrapper::create( host_decoder, message.resources(), message.version_info()),