Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Listener filter ECDS Support #21133

Merged
merged 30 commits into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a4125b6
Listener filter ECDS Support
yanjunxiang-google May 3, 2022
5e88f69
fixing format error
yanjunxiang-google May 3, 2022
dd55ab9
Split UDP listener filter support to a separate PR.
yanjunxiang-google May 8, 2022
9f2b442
fixing CI error
yanjunxiang-google May 8, 2022
f9dd5a0
addressing comments
yanjunxiang-google May 10, 2022
b2eed54
fixing CI error
yanjunxiang-google May 10, 2022
3d8265c
comments
yanjunxiang-google May 10, 2022
bf16c3d
fix CI error
yanjunxiang-google May 11, 2022
7011774
fix CI error
yanjunxiang-google May 12, 2022
e59f26f
fixing server_test failure
yanjunxiang-google May 12, 2022
7a8dc70
fixing clangTidy error
yanjunxiang-google May 12, 2022
7e70c2d
fixing the dynamic_cast
yanjunxiang-google May 14, 2022
7291bba
Merge branch 'main' of https://github.com/envoyproxy/envoy into ecds_…
yanjunxiang-google May 14, 2022
c87f049
merge upstream
yanjunxiang-google May 14, 2022
265b52d
addressing comments
yanjunxiang-google May 14, 2022
a9e7dd4
Merge branch 'main' of https://github.com/envoyproxy/envoy into ecds_…
yanjunxiang-google May 17, 2022
92eb4fe
addressing comments
yanjunxiang-google May 17, 2022
ade2ea8
addressing comments
yanjunxiang-google May 18, 2022
d5570d8
removing debug logs
yanjunxiang-google May 19, 2022
c2d39dd
mock interface
yanjunxiang-google May 21, 2022
fa94ee1
revert api_listener_test.cc change
yanjunxiang-google May 21, 2022
60d9a0c
revert change in test/mocks/server/BUILD
yanjunxiang-google May 21, 2022
dc5ed5c
adding more integration tests
yanjunxiang-google May 21, 2022
dfa9386
adding missing config stats counter
yanjunxiang-google May 24, 2022
05234bd
addressing comments
yanjunxiang-google May 24, 2022
c3b0974
changing to return plain pointer in the interface function
yanjunxiang-google May 25, 2022
ed09a1d
adding matcher test
yanjunxiang-google May 27, 2022
79679db
fixing CI error due to .h not generated correctly
yanjunxiang-google May 27, 2022
2864e0b
adding integration tests for listener filter matcher
yanjunxiang-google May 30, 2022
fc1e9c1
adding matcher integration tests for static filter
yanjunxiang-google May 30, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion envoy/filter/config_provider_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ using DynamicFilterConfigProvider = Envoy::Config::DynamicExtensionConfigProvide
template <class FactoryCb>
using DynamicFilterConfigProviderPtr = std::unique_ptr<DynamicFilterConfigProvider<FactoryCb>>;

// Listener filter config provider aliases
using ListenerFilterFactoriesList =
std::vector<FilterConfigProviderPtr<Network::ListenerFilterFactoryCb>>;

/**
* The FilterConfigProviderManager exposes the ability to get an FilterConfigProvider
* for both static and dynamic filter config providers.
Expand All @@ -37,12 +41,15 @@ template <class FactoryCb, class FactoryCtx> class FilterConfigProviderManager {
* @param last_filter_in_filter_chain indicates whether this filter is the last filter in the
* configured chain
* @param filter_chain_type is the filter chain type
* @param listener_filter_matcher is the filter matcher for TCP listener filter. nullptr for other
* filter types.
*/
virtual DynamicFilterConfigProviderPtr<FactoryCb> createDynamicFilterConfigProvider(
const envoy::config::core::v3::ExtensionConfigSource& config_source,
const std::string& filter_config_name, FactoryCtx& factory_context,
const std::string& stat_prefix, bool last_filter_in_filter_chain,
const std::string& filter_chain_type) PURE;
const std::string& filter_chain_type,
const Network::ListenerFilterMatcherSharedPtr& listener_filter_matcher) PURE;
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved

/**
* Get an FilterConfigProviderPtr for a statically inlined filter config.
Expand Down
1 change: 1 addition & 0 deletions envoy/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ envoy_cc_library(
":drain_manager_interface",
":filter_config_interface",
":guarddog_interface",
"//envoy/filter:config_provider_manager_interface",
"//envoy/network:filter_interface",
"//envoy/network:listen_socket_interface",
"//envoy/network:socket_interface_interface",
Expand Down
5 changes: 3 additions & 2 deletions envoy/server/listener_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "envoy/config/core/v3/config_source.pb.h"
#include "envoy/config/listener/v3/listener.pb.h"
#include "envoy/config/listener/v3/listener_components.pb.h"
#include "envoy/filter/config_provider_manager.h"
#include "envoy/network/filter.h"
#include "envoy/network/listen_socket.h"
#include "envoy/network/listener.h"
Expand Down Expand Up @@ -88,9 +89,9 @@ class ListenerComponentFactory {
* Creates a list of listener filter factories.
* @param filters supplies the JSON configuration.
* @param context supplies the factory creation context.
* @return std::vector<Network::ListenerFilterFactoryCb> the list of filter factories.
* @return ListenerFilterFactoriesList the list of filter factories.
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved
*/
virtual std::vector<Network::ListenerFilterFactoryCb> createListenerFilterFactoryList(
virtual Filter::ListenerFilterFactoriesList createListenerFilterFactoryList(
const Protobuf::RepeatedPtrField<envoy::config::listener::v3::ListenerFilter>& filters,
Configuration::ListenerFactoryContext& context) PURE;

Expand Down
28 changes: 17 additions & 11 deletions source/common/filter/config_discovery_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ class HttpDynamicFilterConfigProviderImpl
ProtobufTypes::MessagePtr&& default_config,
bool last_filter_in_filter_chain,
const std::string& filter_chain_type,
absl::string_view stat_prefix)
absl::string_view stat_prefix,
const Network::ListenerFilterMatcherSharedPtr&)
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved
: DynamicFilterConfigProviderImpl(subscription, require_type_urls, factory_context,
std::move(default_config), last_filter_in_filter_chain,
filter_chain_type, stat_prefix),
Expand Down Expand Up @@ -203,17 +204,19 @@ class ListenerDynamicFilterConfigProviderImpl : public DynamicFilterConfigProvid
const absl::flat_hash_set<std::string>& require_type_urls,
Server::Configuration::ListenerFactoryContext& factory_context,
ProtobufTypes::MessagePtr&& default_config, bool last_filter_in_filter_chain,
const std::string& filter_chain_type, absl::string_view stat_prefix)
const std::string& filter_chain_type, absl::string_view stat_prefix,
const Network::ListenerFilterMatcherSharedPtr& listener_filter_matcher)
: DynamicFilterConfigProviderImpl<FactoryCb>(
subscription, require_type_urls, factory_context, std::move(default_config),
last_filter_in_filter_chain, filter_chain_type, stat_prefix),
factory_context_(factory_context) {}
factory_context_(factory_context), listener_filter_matcher_(listener_filter_matcher) {}

void validateMessage(const std::string&, const Protobuf::Message&,
const std::string&) const override {}

protected:
Server::Configuration::ListenerFactoryContext& factory_context_;
const Network::ListenerFilterMatcherSharedPtr listener_filter_matcher_;
};

class TcpListenerDynamicFilterConfigProviderImpl
Expand All @@ -227,8 +230,8 @@ class TcpListenerDynamicFilterConfigProviderImpl
auto* factory =
Registry::FactoryRegistry<Server::Configuration::NamedListenerFilterConfigFactory>::
getFactoryByType(message.GetTypeName());
// TODO(yanjunxiang): Change nullptr to actual listener filter matcher.
return factory->createListenerFilterFactoryFromProto(message, nullptr, factory_context_);
return factory->createListenerFilterFactoryFromProto(message, listener_filter_matcher_,
factory_context_);
}
};

Expand Down Expand Up @@ -385,7 +388,8 @@ class FilterConfigProviderManagerImpl : public FilterConfigProviderManagerImplBa
const envoy::config::core::v3::ExtensionConfigSource& config_source,
const std::string& filter_config_name, FactoryCtx& factory_context,
const std::string& stat_prefix, bool last_filter_in_filter_chain,
const std::string& filter_chain_type) override {
const std::string& filter_chain_type,
const Network::ListenerFilterMatcherSharedPtr& listener_filter_matcher) override {
std::string subscription_stat_prefix;
absl::string_view provider_stat_prefix;
if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.top_level_ecds_stats")) {
Expand Down Expand Up @@ -419,9 +423,10 @@ class FilterConfigProviderManagerImpl : public FilterConfigProviderManagerImplBa
last_filter_in_filter_chain, filter_chain_type, require_type_urls);
}

auto provider = createFilterConfigProviderImpl(
subscription, require_type_urls, factory_context, std::move(default_config),
last_filter_in_filter_chain, filter_chain_type, provider_stat_prefix);
auto provider = createFilterConfigProviderImpl(subscription, require_type_urls, factory_context,
std::move(default_config),
last_filter_in_filter_chain, filter_chain_type,
provider_stat_prefix, listener_filter_matcher);

// Ensure the subscription starts if it has not already.
if (config_source.apply_default_config_without_warming()) {
Expand Down Expand Up @@ -476,10 +481,11 @@ class FilterConfigProviderManagerImpl : public FilterConfigProviderManagerImplBa
FilterConfigSubscriptionSharedPtr& subscription,
const absl::flat_hash_set<std::string>& require_type_urls, FactoryCtx& factory_context,
ProtobufTypes::MessagePtr&& default_config, bool last_filter_in_filter_chain,
const std::string& filter_chain_type, absl::string_view stat_prefix) {
const std::string& filter_chain_type, absl::string_view stat_prefix,
const Network::ListenerFilterMatcherSharedPtr& listener_filter_matcher) {
return std::make_unique<DynamicFilterConfigImpl>(
subscription, require_type_urls, factory_context, std::move(default_config),
last_filter_in_filter_chain, filter_chain_type, stat_prefix);
last_filter_in_filter_chain, filter_chain_type, stat_prefix, listener_filter_matcher);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ void HttpConnectionManagerConfig::processDynamicFilterConfig(

auto filter_config_provider = filter_config_provider_manager_.createDynamicFilterConfigProvider(
config_discovery, name, context_, stats_prefix_, last_filter_in_current_config,
filter_chain_type);
filter_chain_type, nullptr);
filter_factories.push_back(std::move(filter_config_provider));
}

Expand Down
1 change: 1 addition & 0 deletions source/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ envoy_cc_library(
hdrs = ["configuration_impl.h"],
deps = [
"//envoy/config:typed_config_interface",
"//envoy/filter:config_provider_manager_interface",
"//envoy/http:filter_interface",
"//envoy/network:connection_interface",
"//envoy/network:filter_interface",
Expand Down
5 changes: 3 additions & 2 deletions source/server/config_validation/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,11 @@ class ValidationInstance final : Logger::Loggable<Logger::Id::main>,
return ProdListenerComponentFactory::createNetworkFilterFactoryList_(
filters, filter_chain_factory_context);
}
std::vector<Network::ListenerFilterFactoryCb> createListenerFilterFactoryList(
Filter::ListenerFilterFactoriesList createListenerFilterFactoryList(
const Protobuf::RepeatedPtrField<envoy::config::listener::v3::ListenerFilter>& filters,
Configuration::ListenerFactoryContext& context) override {
return ProdListenerComponentFactory::createListenerFilterFactoryList_(filters, context);
return ProdListenerComponentFactory::createListenerFilterFactoryList_(
filters, context, listener_manager_->getTcpListenerConfigProviderManager());
}
std::vector<Network::UdpListenerFilterFactoryCb> createUdpListenerFilterFactoryList(
const Protobuf::RepeatedPtrField<envoy::config::listener::v3::ListenerFilter>& filters,
Expand Down
47 changes: 42 additions & 5 deletions source/server/configuration_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,48 @@ bool FilterChainUtility::buildFilterChain(Network::FilterManager& filter_manager
return filter_manager.initializeReadFilters();
}

bool FilterChainUtility::buildFilterChain(
Network::ListenerFilterManager& filter_manager,
const std::vector<Network::ListenerFilterFactoryCb>& factories) {
for (const Network::ListenerFilterFactoryCb& factory : factories) {
factory(filter_manager);
class MissingConfigTcpListenerFilter : public Network::ListenerFilter,
public Logger::Loggable<Logger::Id::filter> {
public:
MissingConfigTcpListenerFilter() = default;

// Network::ListenerFilter
Network::FilterStatus onAccept(Network::ListenerFilterCallbacks& cb) override {
cb_ = &cb;
ENVOY_LOG(warn, "Listener filter: new connection accepted while missing configuration. "
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved
"Close socket and stop the iteration onAccept.");
cb_->socket().ioHandle().close();
return Network::FilterStatus::StopIteration;
}
Network::FilterStatus onData(Network::ListenerFilterBuffer&) override {
// The socket is already closed onAccept. Just return StopIteration here.
return Network::FilterStatus::StopIteration;
}
size_t maxReadBytes() const override { return 1024; }
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved

private:
Network::ListenerFilterCallbacks* cb_{};
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved
};

bool FilterChainUtility::buildFilterChain(Network::ListenerFilterManager& filter_manager,
const Filter::ListenerFilterFactoriesList& factories) {
bool added_missing_config_filter = false;
for (const auto& filter_config_provider : factories) {
auto config = filter_config_provider->config();
if (config.has_value()) {
auto config_value = config.value();
config_value(filter_manager);
continue;
}
// If a filter config is missing after warming, stop iteration.
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved
if (!added_missing_config_filter) {
ENVOY_LOG_MISC(trace, "Missing filter config for a provider {}",
filter_config_provider->name());
filter_manager.addAcceptFilter(nullptr, std::make_unique<MissingConfigTcpListenerFilter>());
added_missing_config_filter = true;
} else {
ENVOY_LOG_MISC(trace, "Provider {} missing a filter config", filter_config_provider->name());
}
}

return true;
Expand Down
3 changes: 2 additions & 1 deletion source/server/configuration_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "envoy/config/bootstrap/v3/bootstrap.pb.h"
#include "envoy/config/trace/v3/http_tracer.pb.h"
#include "envoy/config/typed_config.h"
#include "envoy/filter/config_provider_manager.h"
#include "envoy/http/filter.h"
#include "envoy/network/filter.h"
#include "envoy/server/configuration.h"
Expand Down Expand Up @@ -81,7 +82,7 @@ class FilterChainUtility {
* TODO(sumukhs): Coalesce with the above as they are very similar
*/
static bool buildFilterChain(Network::ListenerFilterManager& filter_manager,
const std::vector<Network::ListenerFilterFactoryCb>& factories);
const Filter::ListenerFilterFactoriesList& factories);

/**
* Given a UdpListenerFilterManager and a list of factories, create a new filter chain. Chain
Expand Down
20 changes: 14 additions & 6 deletions source/server/listener_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -701,9 +701,12 @@ void ListenerImpl::buildOriginalDstListenerFilter() {
Config::Utility::getAndCheckFactoryByName<Configuration::NamedListenerFilterConfigFactory>(
"envoy.filters.listener.original_dst");

listener_filter_factories_.push_back(factory.createListenerFilterFactoryFromProto(
Envoy::ProtobufWkt::Empty(),
/*listener_filter_matcher=*/nullptr, *listener_factory_context_));
Network::ListenerFilterFactoryCb callback = factory.createListenerFilterFactoryFromProto(
Envoy::ProtobufWkt::Empty(), nullptr, *listener_factory_context_);
auto cfg_provider = parent_.getTcpListenerConfigProviderManager();
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved
auto filter_config_provider = cfg_provider.createStaticFilterConfigProvider(
callback, "envoy.filters.listener.original_dst");
listener_filter_factories_.push_back(std::move(filter_config_provider));
}
}

Expand All @@ -716,9 +719,14 @@ void ListenerImpl::buildProxyProtocolListenerFilter() {
auto& factory =
Config::Utility::getAndCheckFactoryByName<Configuration::NamedListenerFilterConfigFactory>(
"envoy.filters.listener.proxy_protocol");
listener_filter_factories_.push_back(factory.createListenerFilterFactoryFromProto(
envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol(),
/*listener_filter_matcher=*/nullptr, *listener_factory_context_));

Network::ListenerFilterFactoryCb callback = factory.createListenerFilterFactoryFromProto(
envoy::extensions::filters::listener::proxy_protocol::v3::ProxyProtocol(), nullptr,
*listener_factory_context_);
auto cfg_provider = parent_.getTcpListenerConfigProviderManager();
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved
auto filter_config_provider = cfg_provider.createStaticFilterConfigProvider(
callback, "envoy.filters.listener.proxy_protocol");
listener_filter_factories_.push_back(std::move(filter_config_provider));
}
}

Expand Down
3 changes: 2 additions & 1 deletion source/server/listener_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "envoy/config/core/v3/base.pb.h"
#include "envoy/config/listener/v3/listener.pb.h"
#include "envoy/config/typed_metadata.h"
#include "envoy/filter/config_provider_manager.h"
#include "envoy/network/drain_decision.h"
#include "envoy/network/filter.h"
#include "envoy/network/listener.h"
Expand Down Expand Up @@ -438,7 +439,7 @@ class ListenerImpl final : public Network::ListenerConfig,
// RdsRouteConfigSubscription::init_target_, so the listener can wait for route configs.
std::unique_ptr<Init::Manager> dynamic_init_manager_;

std::vector<Network::ListenerFilterFactoryCb> listener_filter_factories_;
Filter::ListenerFilterFactoriesList listener_filter_factories_;
std::vector<Network::UdpListenerFilterFactoryCb> udp_listener_filter_factories_;
std::vector<AccessLog::InstanceSharedPtr> access_logs_;
const envoy::config::listener::v3::Listener config_;
Expand Down
61 changes: 49 additions & 12 deletions source/server/listener_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -108,27 +108,61 @@ std::vector<Network::FilterFactoryCb> ProdListenerComponentFactory::createNetwor
return ret;
}

std::vector<Network::ListenerFilterFactoryCb>
ProdListenerComponentFactory::createListenerFilterFactoryList_(
Filter::ListenerFilterFactoriesList ProdListenerComponentFactory::createListenerFilterFactoryList_(
const Protobuf::RepeatedPtrField<envoy::config::listener::v3::ListenerFilter>& filters,
Configuration::ListenerFactoryContext& context) {
std::vector<Network::ListenerFilterFactoryCb> ret;
Configuration::ListenerFactoryContext& context,
Filter::TcpListenerFilterConfigProviderManagerImpl& config_provider_manager) {
Filter::ListenerFilterFactoriesList ret;
for (ssize_t i = 0; i < filters.size(); i++) {
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved
const auto& proto_config = filters[i];
ENVOY_LOG(debug, " filter #{}:", i);
ENVOY_LOG(debug, " name: {}", proto_config.name());
ENVOY_LOG(debug, " config: {}",
MessageUtil::getJsonStringFromMessageOrError(
static_cast<const Protobuf::Message&>(proto_config.typed_config())));
// dynamic listener filter configuration
if (proto_config.config_type_case() ==
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved
envoy::config::listener::v3::ListenerFilter::ConfigTypeCase::kConfigDiscovery) {
auto& config_discovery = proto_config.config_discovery();
auto& name = proto_config.name();
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved
ENVOY_LOG(debug, " Listener filter: dynamic filter name: {}", name);
if (config_discovery.apply_default_config_without_warming() &&
!config_discovery.has_default_config()) {
throw EnvoyException(fmt::format(
"Error: listener filter config {} applied without warming but has no default config.",
name));
}
for (const auto& type_url : config_discovery.type_urls()) {
auto factory_type_url = TypeUtil::typeUrlToDescriptorFullName(type_url);
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved
auto* factory =
Registry::FactoryRegistry<Server::Configuration::NamedListenerFilterConfigFactory>::
getFactoryByType(factory_type_url);
if (factory == nullptr) {
throw EnvoyException(fmt::format(
"Error: no listener factory found for a required type URL {}.", factory_type_url));
}
}
auto filter_config_provider = config_provider_manager.createDynamicFilterConfigProvider(
config_discovery, name, context, "tcp_listener.", false, "listener",
createListenerFilterMatcher(proto_config));
ret.push_back(std::move(filter_config_provider));
} else {
// For static configuration, now see if there is a factory that will accept the config.
auto& factory =
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved
Config::Utility::getAndCheckFactory<Configuration::NamedListenerFilterConfigFactory>(
proto_config);
auto message = Config::Utility::translateToFactoryConfig(
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved
proto_config, context.messageValidationVisitor(), factory);

// Now see if there is a factory that will accept the config.
auto& factory =
Config::Utility::getAndCheckFactory<Configuration::NamedListenerFilterConfigFactory>(
proto_config);
auto message = Config::Utility::translateToFactoryConfig(
proto_config, context.messageValidationVisitor(), factory);
ret.push_back(factory.createListenerFilterFactoryFromProto(
*message, createListenerFilterMatcher(proto_config), context));
Network::ListenerFilterFactoryCb callback = factory.createListenerFilterFactoryFromProto(
*message, createListenerFilterMatcher(proto_config), context);

auto filter_config_provider =
config_provider_manager.createStaticFilterConfigProvider(callback, proto_config.name());

ENVOY_LOG(debug, " name: {}", filter_config_provider->name());
ret.push_back(std::move(filter_config_provider));
}
}
return ret;
}
Expand Down Expand Up @@ -253,6 +287,9 @@ ListenerManagerImpl::ListenerManagerImpl(Instance& server,
workers_.emplace_back(
worker_factory.createWorker(i, server.overloadManager(), absl::StrCat("worker_", i)));
}
// Create TCP listener filter config provider manager instance.
tcp_listener_config_provider_manager_ =
std::make_unique<Filter::TcpListenerFilterConfigProviderManagerImpl>();
yanjunxiang-google marked this conversation as resolved.
Show resolved Hide resolved
}

ProtobufTypes::MessagePtr
Expand Down
Loading