@@ -114,6 +114,10 @@ enum class Reporter {
114114 ServerSidecar,
115115 // Gateway listener for a set of destination workloads.
116116 ServerGateway,
117+ // The configuration does not specify the
118+ // reporter type and the traffic direction
119+ // will be used to infer the reporter type.
120+ Unspecified,
117121};
118122
119123// Detect if peer info read is completed by TCP metadata exchange.
@@ -474,30 +478,22 @@ struct MetricOverrides : public Logger::Loggable<Logger::Id::filter> {
474478
475479struct Config : public Logger ::Loggable<Logger::Id::filter> {
476480 Config (const stats::PluginConfig& proto_config,
477- Server::Configuration::FactoryContext& factory_context )
478- : context_(factory_context.serverFactoryContext() .singletonManager().getTyped<Context>(
481+ Server::Configuration::ServerFactoryContext& context, Stats::Scope& stats_scope )
482+ : context_(context .singletonManager().getTyped<Context>(
479483 SINGLETON_MANAGER_REGISTERED_NAME (Context),
480- [&factory_context] {
481- return std::make_shared<Context>(factory_context.serverFactoryContext ().scope (),
482- factory_context.serverFactoryContext ().localInfo ());
484+ [&context] {
485+ return std::make_shared<Context>(context.scope (), context.localInfo ());
483486 })),
484487 disable_host_header_fallback_ (proto_config.disable_host_header_fallback()),
485488 report_duration_(
486489 PROTOBUF_GET_MS_OR_DEFAULT (proto_config, tcp_reporting_duration, /* 5s */ 5000 )) {
487- recordVersion (factory_context );
490+ recordVersion (stats_scope );
488491 reporter_ = Reporter::ClientSidecar;
489492 switch (proto_config.reporter ()) {
490493 case stats::Reporter::UNSPECIFIED :
491- switch (factory_context.direction ()) {
492- case envoy::config::core::v3::TrafficDirection::INBOUND :
493- reporter_ = Reporter::ServerSidecar;
494- break ;
495- case envoy::config::core::v3::TrafficDirection::OUTBOUND :
496- reporter_ = Reporter::ClientSidecar;
497- break ;
498- default :
499- break ;
500- }
494+ // Mark as unspecified to allow the filter to infer the reporter type based on traffic
495+ // direction.
496+ reporter_ = Reporter::Unspecified;
501497 break ;
502498 case stats::Reporter::SERVER_GATEWAY :
503499 reporter_ = Reporter::ServerGateway;
@@ -743,14 +739,13 @@ struct Config : public Logger::Loggable<Logger::Id::filter> {
743739 bool evaluated_{false };
744740 };
745741
746- void recordVersion (Server::Configuration::FactoryContext& factory_context ) {
742+ void recordVersion (Stats::Scope& scope ) {
747743 Stats::StatNameTagVector tags;
748744 tags.push_back ({context_->component_ , context_->proxy_ });
749745 tags.push_back ({context_->tag_ , context_->istio_version_ .empty () ? context_->unknown_
750746 : context_->istio_version_ });
751747
752- Stats::Utility::gaugeFromStatNames (factory_context.scope (),
753- {context_->stat_namespace_ , context_->istio_build_ },
748+ Stats::Utility::gaugeFromStatNames (scope, {context_->stat_namespace_ , context_->istio_build_ },
754749 Stats::Gauge::ImportMode::Accumulate, tags)
755750 .set (1 );
756751 }
@@ -776,9 +771,46 @@ class IstioStatsFilter : public Http::PassThroughFilter,
776771public:
777772 IstioStatsFilter (ConfigSharedPtr config)
778773 : config_(config), context_(*config->context_), pool_(config->scope ().symbolTable()),
779- stream_(*config_, pool_) {
774+ stream_(*config_, pool_) {}
775+ ~IstioStatsFilter () override { ASSERT (report_timer_ == nullptr ); }
776+
777+ // The reporter type is only set explicitly by gateways/waypoints. Sidecars leave it
778+ // unspecified in the config and it is inferred here from the direction of the listener
779+ // that owns this stream or connection.
780+ Reporter resolveReporter () const {
781+ ASSERT (decoder_callbacks_ != nullptr || network_read_callbacks_ != nullptr );
782+ if (config_->reporter () != Reporter::Unspecified) {
783+ return config_->reporter ();
784+ }
785+
786+ OptRef<const Network::ListenerInfo> listener_info;
787+ if (decoder_callbacks_ != nullptr ) {
788+ listener_info = decoder_callbacks_->streamInfo ().downstreamAddressProvider ().listenerInfo ();
789+ } else if (network_read_callbacks_ != nullptr ) {
790+ listener_info = network_read_callbacks_->connection ().connectionInfoProvider ().listenerInfo ();
791+ }
792+ if (listener_info.has_value ()) {
793+ switch (listener_info->direction ()) {
794+ case envoy::config::core::v3::TrafficDirection::INBOUND :
795+ return Reporter::ServerSidecar;
796+ case envoy::config::core::v3::TrafficDirection::OUTBOUND :
797+ return Reporter::ClientSidecar;
798+ default :
799+ break ;
800+ }
801+ }
802+ // Retain the historical default for listeners without a traffic direction.
803+ return Reporter::ClientSidecar;
804+ }
805+
806+ // Resolved reporter type. Only valid once initializeTags() has run, which is the first
807+ // thing both the HTTP and the network entry point do.
808+ Reporter reporter () const { return reporter_; }
809+
810+ void initializeTags () {
811+ reporter_ = resolveReporter ();
780812 tags_.reserve (25 );
781- switch (config_-> reporter () ) {
813+ switch (reporter_ ) {
782814 case Reporter::ServerSidecar:
783815 tags_.push_back ({context_.reporter_ , context_.destination_ });
784816 break ;
@@ -788,11 +820,19 @@ class IstioStatsFilter : public Http::PassThroughFilter,
788820 case Reporter::ClientSidecar:
789821 tags_.push_back ({context_.reporter_ , context_.source_ });
790822 break ;
823+ case Reporter::Unspecified:
824+ // Unreachable: resolveReporter() never returns Unspecified.
825+ IS_ENVOY_BUG (" unresolved istio stats reporter" );
826+ tags_.push_back ({context_.reporter_ , context_.unknown_ });
827+ break ;
791828 }
792829 }
793- ~IstioStatsFilter () override { ASSERT (report_timer_ == nullptr ); }
794830
795831 // Http::StreamDecoderFilter
832+ void setDecoderFilterCallbacks (Http::StreamDecoderFilterCallbacks& callbacks) override {
833+ decoder_callbacks_ = &callbacks;
834+ initializeTags ();
835+ }
796836 Http::FilterHeadersStatus decodeHeaders (Http::RequestHeaderMap& request_headers, bool ) override {
797837 is_grpc_ = Grpc::Common::isGrpcRequestHeaders (request_headers);
798838 if (is_grpc_) {
@@ -866,6 +906,7 @@ class IstioStatsFilter : public Http::PassThroughFilter,
866906 }
867907 void initializeReadFilterCallbacks (Network::ReadFilterCallbacks& callbacks) override {
868908 network_read_callbacks_ = &callbacks;
909+ initializeTags ();
869910 network_read_callbacks_->connection ().addConnectionCallbacks (*this );
870911 }
871912 // Network::ConnectionCallbacks
@@ -893,7 +934,7 @@ class IstioStatsFilter : public Http::PassThroughFilter,
893934 if (decoder_callbacks_) {
894935 if (!peer_read_) {
895936 const auto & info = decoder_callbacks_->streamInfo ();
896- peer_read_ = peerInfoRead (config_-> reporter (), info.filterState ());
937+ peer_read_ = peerInfoRead (reporter (), info.filterState ());
897938 if (peer_read_ || end_stream) {
898939 ENVOY_LOG (trace, " Populating peer metadata from HTTP MX." );
899940 populatePeerInfo (info, info.filterState ());
@@ -922,7 +963,7 @@ class IstioStatsFilter : public Http::PassThroughFilter,
922963 const auto & info = network_read_callbacks_->connection ().streamInfo ();
923964 // TCP MX writes to upstream stream info instead.
924965 OptRef<const StreamInfo::UpstreamInfo> upstream_info;
925- if (config_-> reporter () == Reporter::ClientSidecar) {
966+ if (reporter () == Reporter::ClientSidecar) {
926967 upstream_info = info.upstreamInfo ();
927968 }
928969 const StreamInfo::FilterState& filter_state =
@@ -931,7 +972,7 @@ class IstioStatsFilter : public Http::PassThroughFilter,
931972 : info.filterState ();
932973
933974 if (!peer_read_) {
934- peer_read_ = peerInfoRead (config_-> reporter (), filter_state);
975+ peer_read_ = peerInfoRead (reporter (), filter_state);
935976 // Report connection open once peer info is read or connection is closed.
936977 if (peer_read_ || end_stream) {
937978 ENVOY_LOG (trace, " Populating peer metadata from TCP MX." );
@@ -979,10 +1020,10 @@ class IstioStatsFilter : public Http::PassThroughFilter,
9791020 const StreamInfo::FilterState& filter_state) {
9801021 // Compute peer info with client-side fallbacks.
9811022 std::optional<Istio::Common::WorkloadMetadataObject> peer;
982- auto object = peerInfo (config_-> reporter (), filter_state);
1023+ auto object = peerInfo (reporter (), filter_state);
9831024 if (object) {
9841025 peer.emplace (object.value ());
985- } else if (config_-> reporter () == Reporter::ClientSidecar) {
1026+ } else if (reporter () == Reporter::ClientSidecar) {
9861027 if (auto label_obj = extractEndpointMetadata (info); label_obj) {
9871028 peer.emplace (label_obj.value ());
9881029 }
@@ -1044,7 +1085,7 @@ class IstioStatsFilter : public Http::PassThroughFilter,
10441085
10451086 std::string peer_san;
10461087 absl::string_view local_san;
1047- switch (config_-> reporter ()) {
1088+ switch (reporter ()) {
10481089 case Reporter::ServerSidecar:
10491090 case Reporter::ServerGateway: {
10501091 auto peer_principal =
@@ -1078,7 +1119,7 @@ class IstioStatsFilter : public Http::PassThroughFilter,
10781119 peer_san = ssl_info->uriSanPeerCertificate ()[0 ];
10791120 }
10801121 if (peer_san.empty ()) {
1081- auto endpoint_object = peerInfo (config_-> reporter (), filter_state);
1122+ auto endpoint_object = peerInfo (reporter (), filter_state);
10821123 if (endpoint_object) {
10831124 endpoint_peer.emplace (endpoint_object.value ());
10841125 peer_san = endpoint_peer->identity_ ;
@@ -1091,6 +1132,8 @@ class IstioStatsFilter : public Http::PassThroughFilter,
10911132 }
10921133 break ;
10931134 }
1135+ case Reporter::Unspecified:
1136+ break ;
10941137 }
10951138 // Implements fallback from using the namespace from SAN if available to
10961139 // using peer metadata, otherwise.
@@ -1104,7 +1147,7 @@ class IstioStatsFilter : public Http::PassThroughFilter,
11041147 if (peer_namespace.empty () && peer) {
11051148 peer_namespace = peer->namespace_name_ ;
11061149 }
1107- switch (config_-> reporter ()) {
1150+ switch (reporter ()) {
11081151 case Reporter::ServerSidecar:
11091152 case Reporter::ServerGateway: {
11101153 tags_.push_back ({context_.source_workload_ , peer && !peer->workload_name_ .empty ()
@@ -1130,7 +1173,7 @@ class IstioStatsFilter : public Http::PassThroughFilter,
11301173 tags_.push_back ({context_.source_cluster_ , peer && !peer->cluster_name_ .empty ()
11311174 ? pool_.add (peer->cluster_name_ )
11321175 : context_.unknown_ });
1133- switch (config_-> reporter ()) {
1176+ switch (reporter ()) {
11341177 case Reporter::ServerGateway: {
11351178 std::optional<Istio::Common::WorkloadMetadataObject> endpoint_peer;
11361179 auto endpoint_object = peerInfo (Reporter::ClientSidecar, filter_state);
@@ -1256,8 +1299,9 @@ class IstioStatsFilter : public Http::PassThroughFilter,
12561299 Context& context_;
12571300 Stats::StatNameDynamicPool pool_;
12581301 Stats::StatNameTagVector tags_;
1302+ Reporter reporter_{Reporter::ClientSidecar};
12591303 Event::TimerPtr report_timer_{nullptr };
1260- Network::ReadFilterCallbacks* network_read_callbacks_;
1304+ Network::ReadFilterCallbacks* network_read_callbacks_{ nullptr } ;
12611305 bool peer_read_{false };
12621306 uint64_t bytes_sent_{0 };
12631307 uint64_t bytes_received_{0 };
@@ -1272,12 +1316,12 @@ class IstioStatsFilter : public Http::PassThroughFilter,
12721316} // namespace
12731317
12741318absl::StatusOr<Http::FilterFactoryCb>
1275- IstioStatsFilterConfigFactory::createFilterFactoryFromProtoTyped (
1276- const stats::PluginConfig& proto_config, const std::string& ,
1277- Server::Configuration::FactoryContext& factory_context ) {
1278- factory_context. serverFactoryContext (). api ().customStatNamespaces ().registerStatNamespace (
1279- CustomStatNamespace);
1280- ConfigSharedPtr config = std::make_shared<Config>(proto_config, factory_context );
1319+ IstioStatsFilterConfigFactory::createHttpFilterFactoryFromProtoTyped (
1320+ const stats::PluginConfig& proto_config, Server::Configuration::ServerFactoryContext& context ,
1321+ Server::Configuration::ExtraFactoryContext& extra_context ) {
1322+ context. api ().customStatNamespaces ().registerStatNamespace (CustomStatNamespace);
1323+ ConfigSharedPtr config =
1324+ std::make_shared<Config>(proto_config, context, extra_context. scopeOr (context) );
12811325 return [config](Http::FilterChainFactoryCallbacks& callbacks) {
12821326 auto filter = std::make_shared<IstioStatsFilter>(config);
12831327 callbacks.addStreamFilter (filter);
@@ -1295,8 +1339,9 @@ IstioStatsNetworkFilterConfigFactory::createFilterFactoryFromProto(
12951339 const Protobuf::Message& proto_config, Server::Configuration::FactoryContext& factory_context) {
12961340 factory_context.serverFactoryContext ().api ().customStatNamespaces ().registerStatNamespace (
12971341 CustomStatNamespace);
1298- ConfigSharedPtr config = std::make_shared<Config>(
1299- dynamic_cast <const stats::PluginConfig&>(proto_config), factory_context);
1342+ ConfigSharedPtr config =
1343+ std::make_shared<Config>(dynamic_cast <const stats::PluginConfig&>(proto_config),
1344+ factory_context.serverFactoryContext (), factory_context.scope ());
13001345 return [config](Network::FilterManager& filter_manager) {
13011346 filter_manager.addReadFilter (std::make_shared<IstioStatsFilter>(config));
13021347 };
0 commit comments