diff --git a/cilium/bpf_metadata.cc b/cilium/bpf_metadata.cc index d67552845..9550bf657 100644 --- a/cilium/bpf_metadata.cc +++ b/cilium/bpf_metadata.cc @@ -303,6 +303,7 @@ Config::Config(const ::cilium::BpfMetadata& config, pin_for_ads); // update desired config source on the map npmap_->configure(config_source_); + npmap_->addListenerInitTarget(context.initManager()); } } diff --git a/cilium/network_policy.cc b/cilium/network_policy.cc index 90f924ed7..045d5155b 100644 --- a/cilium/network_policy.cc +++ b/cilium/network_policy.cc @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #include "envoy/config/core/v3/base.pb.h" #include "envoy/config/core/v3/config_source.pb.h" #include "envoy/config/subscription.h" +#include "envoy/event/timer.h" #include "envoy/http/header_map.h" #include "envoy/init/manager.h" #include "envoy/network/address.h" @@ -427,6 +429,8 @@ class NetworkPolicyMapImpl : public ManagedGrpcSubscription { // init target which starts gRPC subscription Init::TargetImpl init_target_; + Init::SharedTargetImpl initial_policy_target_{"Cilium NetworkPolicy first update", []() {}}; + Event::TimerPtr initial_policy_timer_; std::shared_ptr transport_factory_context_; // Declared after transport_factory_context_ so that the cache, which retains a shared reference @@ -1948,6 +1952,10 @@ NetworkPolicyMap::~NetworkPolicyMap() { context_.mainThreadDispatcher().post([impl = std::move(impl_)]() mutable { impl.reset(); }); } +void NetworkPolicyMap::addListenerInitTarget(Init::Manager& listener_init_manager) { + listener_init_manager.add(impl_->initial_policy_target_); +} + bool NetworkPolicyMap::exists(const std::string& endpoint_policy_name) const { return impl_->getPolicyInstanceImpl(endpoint_policy_name); } @@ -2000,6 +2008,14 @@ NetworkPolicyMapImpl::NetworkPolicyMapImpl( store(new PolicyMapSnapshot()); ENVOY_LOG(trace, "NetworkPolicyMapImpl({}) created.", instance_id_); + initial_policy_timer_ = context_.mainThreadDispatcher().createTimer([this]() { + if (initial_policy_target_.ready()) { + ENVOY_LOG(warn, "Cilium NetworkPolicy: no policy update within 5 seconds, starting listeners " + "without network policy"); + } + }); + initial_policy_timer_->enableTimer(std::chrono::seconds(5)); + if (context_.admin().has_value()) { ENVOY_LOG(debug, "Registering NetworkPolicies to config tracker"); config_tracker_entry_ = context_.admin()->getConfigTracker().add( @@ -2228,6 +2244,7 @@ absl::Status NetworkPolicyMapImpl::onConfigUpdate( installNewPolicyMap(std::move(pending_resource_map), version_init_manager, std::move(version_name), policy_stream_state); + initial_policy_target_.ready(); return absl::OkStatus(); } @@ -2340,6 +2357,7 @@ absl::Status NetworkPolicyMapImpl::onConfigUpdate( } installNewPolicyMap(std::move(pending_resource_map), version_init_manager, std::move(version_name), policy_stream_state); + initial_policy_target_.ready(); return absl::OkStatus(); } diff --git a/cilium/network_policy.h b/cilium/network_policy.h index e6d096b80..69eb7d790 100644 --- a/cilium/network_policy.h +++ b/cilium/network_policy.h @@ -12,6 +12,7 @@ #include "envoy/config/core/v3/config_source.pb.h" #include "envoy/config/subscription.h" #include "envoy/http/header_map.h" +#include "envoy/init/manager.h" #include "envoy/network/address.h" #include "envoy/protobuf/message_validator.h" #include "envoy/server/factory_context.h" @@ -202,6 +203,7 @@ class NetworkPolicyMap : public Singleton::Instance, public Logger::Loggable(&factory_context_.server_factory_context_.dispatcher_); policy_map_ = std::make_shared(factory_context_, Cilium::CILIUM_XDS_API_CONFIG, false); } @@ -279,6 +284,7 @@ class CiliumNetworkPolicyTest : public ::testing::Test { NiceMock factory_context_; NiceMock secret_manager_; std::shared_ptr policy_map_; + NiceMock* initial_policy_timer_; NiceMock store_; uint16_t proxy_id_ = 42; }; @@ -287,6 +293,48 @@ TEST_F(CiliumNetworkPolicyTest, UpdatesRejectedStatName) { EXPECT_EQ("cilium.policy.updates_rejected", updatesRejectedStatName()); } +TEST_F(CiliumNetworkPolicyTest, ListenerInitWaitsForFirstPolicyUpdate) { + Init::ManagerImpl listener_init_manager("listener"); + policy_map_->addListenerInitTarget(listener_init_manager); + bool initialized = false; + Init::WatcherImpl listener_watcher("listener", [&initialized]() { initialized = true; }); + listener_init_manager.initialize(listener_watcher); + EXPECT_FALSE(initialized); + EXPECT_TRUE(subscriptionCallbacks().onConfigUpdate({}, "1").ok()); + EXPECT_TRUE(initialized); +} + +TEST_F(CiliumNetworkPolicyTest, ListenerInitStartedAfterFirstPolicyUpdateDoesNotWait) { + Init::ManagerImpl listener_init_manager("listener"); + policy_map_->addListenerInitTarget(listener_init_manager); + EXPECT_TRUE(subscriptionCallbacks().onConfigUpdate({}, "1").ok()); + bool initialized = false; + Init::WatcherImpl listener_watcher("listener", [&initialized]() { initialized = true; }); + listener_init_manager.initialize(listener_watcher); + EXPECT_TRUE(initialized); +} + +TEST_F(CiliumNetworkPolicyTest, ListenerAddedAfterFirstPolicyUpdateDoesNotWait) { + EXPECT_TRUE(subscriptionCallbacks().onConfigUpdate({}, "1").ok()); + Init::ManagerImpl listener_init_manager("listener"); + policy_map_->addListenerInitTarget(listener_init_manager); + bool initialized = false; + Init::WatcherImpl listener_watcher("listener", [&initialized]() { initialized = true; }); + listener_init_manager.initialize(listener_watcher); + EXPECT_TRUE(initialized); +} + +TEST_F(CiliumNetworkPolicyTest, ListenerInitWaitsUntilFirstPolicyUpdateTimeout) { + Init::ManagerImpl listener_init_manager("listener"); + policy_map_->addListenerInitTarget(listener_init_manager); + bool initialized = false; + Init::WatcherImpl listener_watcher("listener", [&initialized]() { initialized = true; }); + listener_init_manager.initialize(listener_watcher); + EXPECT_FALSE(initialized); + initial_policy_timer_->invokeCallback(); + EXPECT_TRUE(initialized); +} + TEST_F(CiliumNetworkPolicyTest, EmptyPolicyUpdate) { EXPECT_TRUE(subscriptionCallbacks().onConfigUpdate({}, "1").ok()); EXPECT_FALSE(validate("10.1.2.3", "")); // Policy not found