Skip to content

Commit aa2f87d

Browse files
committed
wasm: use root stats scope for upstream filter stats
Upstream HTTP Wasm filter stats currently use the cluster stats scope, while downstream HTTP Wasm filter stats use the server-wide root scope. This causes upstream custom metrics to inherit the cluster prefix and Prometheus cluster label, even though Wasm extension stats should be scoped consistently across downstream and upstream filters. Use the server-wide root stats scope for upstream HTTP Wasm filter stats and keep the old cluster-scope behavior behind the envoy.reloadable_features.upstream_wasm_filter_uses_root_scope runtime guard. Setting the guard to false temporarily reverts to the previous behavior. Signed-off-by: Yueshang zuo <zuoyueshang.zys@alibaba-inc.com>
1 parent 8492f41 commit aa2f87d

6 files changed

Lines changed: 101 additions & 3 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Upstream HTTP Wasm filter stats now use the server-wide root stats scope instead of the
2+
cluster stats scope, matching downstream HTTP Wasm filter scoping. For a custom counter ``foo``
3+
in the ``wasmcustom`` namespace, admin ``/stats`` output changes from
4+
``cluster.<cluster_name>.wasmcustom.foo`` to ``wasmcustom.foo``. Prometheus output changes from
5+
``envoy_cluster_wasmcustom_foo{envoy_cluster_name="X"}`` to ``foo`` because the leading registered
6+
custom namespace is stripped by the Prometheus formatter. Plugins that need per-cluster
7+
differentiation should encode the cluster name into the metric name themselves. Can be reverted
8+
by setting runtime guard ``envoy.reloadable_features.upstream_wasm_filter_uses_root_scope`` to
9+
``false``.

source/common/runtime/runtime_features.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ RUNTIME_GUARD(envoy_reloadable_features_tls_certificate_compression_brotli);
113113
RUNTIME_GUARD(envoy_reloadable_features_trace_refresh_after_route_refresh);
114114
RUNTIME_GUARD(envoy_reloadable_features_udp_set_do_not_fragment);
115115
RUNTIME_GUARD(envoy_reloadable_features_uhv_allow_malformed_url_encoding);
116+
RUNTIME_GUARD(envoy_reloadable_features_upstream_wasm_filter_uses_root_scope);
116117
RUNTIME_GUARD(envoy_reloadable_features_uri_template_match_on_asterisk);
117118
RUNTIME_GUARD(envoy_reloadable_features_use_migration_in_quiche);
118119
RUNTIME_GUARD(envoy_reloadable_features_use_response_decoder_handle);

source/extensions/filters/http/wasm/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ envoy_cc_library(
1919
"//envoy/http:codes_interface",
2020
"//envoy/server:filter_config_interface",
2121
"//envoy/upstream:cluster_manager_interface",
22+
"//source/common/runtime:runtime_lib",
2223
"//source/extensions/common/wasm:remote_async_datasource_lib",
2324
"//source/extensions/common/wasm:wasm_lib",
2425
"@envoy_api//envoy/extensions/filters/http/wasm/v3:pkg_cc_proto",

source/extensions/filters/http/wasm/wasm_filter.cc

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
#include "source/extensions/filters/http/wasm/wasm_filter.h"
22

3+
#include "source/common/runtime/runtime_features.h"
4+
35
namespace Envoy {
46
namespace Extensions {
57
namespace HttpFilters {
68
namespace Wasm {
79

10+
namespace {
11+
12+
Stats::Scope& upstreamWasmStatsScope(Server::Configuration::UpstreamFactoryContext& context) {
13+
if (Runtime::runtimeFeatureEnabled(
14+
"envoy.reloadable_features.upstream_wasm_filter_uses_root_scope")) {
15+
return context.serverFactoryContext().serverScope();
16+
}
17+
return context.scope();
18+
}
19+
20+
} // namespace
21+
822
FilterConfig::FilterConfig(const envoy::extensions::filters::http::wasm::v3::Wasm& config,
923
Server::Configuration::FactoryContext& context)
1024
: Extensions::Common::Wasm::PluginConfig(
@@ -13,9 +27,10 @@ FilterConfig::FilterConfig(const envoy::extensions::filters::http::wasm::v3::Was
1327

1428
FilterConfig::FilterConfig(const envoy::extensions::filters::http::wasm::v3::Wasm& config,
1529
Server::Configuration::UpstreamFactoryContext& context)
16-
: Extensions::Common::Wasm::PluginConfig(
17-
config.config(), context.serverFactoryContext(), context.scope(), context.initManager(),
18-
envoy::config::core::v3::TrafficDirection::OUTBOUND, nullptr, false) {}
30+
: Extensions::Common::Wasm::PluginConfig(config.config(), context.serverFactoryContext(),
31+
upstreamWasmStatsScope(context), context.initManager(),
32+
envoy::config::core::v3::TrafficDirection::OUTBOUND,
33+
nullptr, false) {}
1934

2035
FilterConfig::FilterConfig(const envoy::extensions::filters::http::wasm::v3::Wasm& config,
2136
Server::Configuration::ServerFactoryContext& context)

test/extensions/filters/http/wasm/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ envoy_extension_cc_test(
7676
"//test/mocks/network:network_mocks",
7777
"//test/mocks/server:server_mocks",
7878
"//test/test_common:environment_lib",
79+
"//test/test_common:test_runtime_lib",
7980
"@envoy_api//envoy/extensions/filters/http/wasm/v3:pkg_cc_proto",
8081
],
8182
)

test/extensions/filters/http/wasm/config_test.cc

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "test/mocks/network/mocks.h"
1717
#include "test/mocks/server/mocks.h"
1818
#include "test/test_common/environment.h"
19+
#include "test/test_common/test_runtime.h"
1920

2021
#include "gmock/gmock.h"
2122
#include "gtest/gtest.h"
@@ -120,6 +121,29 @@ class WasmFilterConfigTest
120121
return std::make_unique<FilterConfig>(proto_config, upstream_factory_context_);
121122
}
122123

124+
envoy::extensions::filters::http::wasm::v3::Wasm localWasmConfig(const std::string& name) {
125+
const std::string yaml =
126+
TestEnvironment::substitute(absl::StrCat(R"EOF(
127+
config:
128+
name: ")EOF",
129+
name, R"EOF("
130+
vm_config:
131+
vm_id: ")EOF",
132+
name, R"EOF("
133+
runtime: "envoy.wasm.runtime.)EOF",
134+
std::get<0>(GetParam()), R"EOF("
135+
configuration:
136+
"@type": "type.googleapis.com/google.protobuf.StringValue"
137+
value: "some configuration"
138+
code:
139+
local:
140+
filename: "{{ test_rundir }}/test/extensions/filters/http/wasm/test_data/test_cpp.wasm"
141+
)EOF"));
142+
envoy::extensions::filters::http::wasm::v3::Wasm proto_config;
143+
TestUtility::loadFromYaml(yaml, proto_config);
144+
return proto_config;
145+
}
146+
123147
NiceMock<Network::MockListenerInfo> listener_info_;
124148
Stats::IsolatedStoreImpl stats_store_;
125149
Stats::Scope& stats_scope_{*stats_store_.rootScope()};
@@ -254,6 +278,53 @@ TEST_P(WasmFilterConfigTest, YamlLoadFromFileWasm) {
254278
EXPECT_TRUE(api_->customStatNamespaces().registered("wasmcustom"));
255279
}
256280

281+
TEST_P(WasmFilterConfigTest, UpstreamWasmUsesServerScopeWhenRuntimeGuardEnabled) {
282+
if (std::get<2>(GetParam())) {
283+
GTEST_SKIP() << "Only applies to upstream Wasm filters.";
284+
}
285+
286+
TestScopedRuntime scoped_runtime;
287+
scoped_runtime.mergeValues(
288+
{{"envoy.reloadable_features.upstream_wasm_filter_uses_root_scope", "true"}});
289+
auto cluster_scope =
290+
upstream_factory_context_.store_.rootScope()->createScope("cluster.test_cluster.");
291+
ON_CALL(upstream_factory_context_, scope()).WillByDefault(ReturnRef(*cluster_scope));
292+
293+
const auto proto_config = localWasmConfig("upstream_wasm_root_scope");
294+
EXPECT_CALL(init_watcher_, ready());
295+
auto filter_config = getFilterConfig(proto_config);
296+
initializeContextInitManager(init_watcher_);
297+
298+
const std::string full_stat_name =
299+
absl::StrCat("wasm.envoy.wasm.runtime.", std::get<0>(GetParam()), ".created");
300+
const auto counter = TestUtility::findCounter(
301+
upstream_factory_context_.server_factory_context_.store_, full_stat_name);
302+
ASSERT_NE(nullptr, counter);
303+
}
304+
305+
TEST_P(WasmFilterConfigTest, UpstreamWasmUsesClusterScopeWhenRuntimeGuardDisabled) {
306+
if (std::get<2>(GetParam())) {
307+
GTEST_SKIP() << "Only applies to upstream Wasm filters.";
308+
}
309+
310+
TestScopedRuntime scoped_runtime;
311+
scoped_runtime.mergeValues(
312+
{{"envoy.reloadable_features.upstream_wasm_filter_uses_root_scope", "false"}});
313+
auto cluster_scope =
314+
upstream_factory_context_.store_.rootScope()->createScope("cluster.test_cluster.");
315+
ON_CALL(upstream_factory_context_, scope()).WillByDefault(ReturnRef(*cluster_scope));
316+
317+
const auto proto_config = localWasmConfig("upstream_wasm_cluster_scope");
318+
EXPECT_CALL(init_watcher_, ready());
319+
auto filter_config = getFilterConfig(proto_config);
320+
initializeContextInitManager(init_watcher_);
321+
322+
const std::string full_stat_name = absl::StrCat("cluster.test_cluster.wasm.envoy.wasm.runtime.",
323+
std::get<0>(GetParam()), ".created");
324+
const auto counter = TestUtility::findCounter(upstream_factory_context_.store_, full_stat_name);
325+
ASSERT_NE(nullptr, counter);
326+
}
327+
257328
TEST_P(WasmFilterConfigTest, DEPRECATED_FEATURE_TEST(YamlLoadFromFileWasmFailOpenOk)) {
258329
const std::string yaml = TestEnvironment::substitute(absl::StrCat(R"EOF(
259330
config:

0 commit comments

Comments
 (0)