diff --git a/bazel/repository_locations.bzl b/bazel/repository_locations.bzl index cd541bd8f9f67..9e79863a26f15 100644 --- a/bazel/repository_locations.bzl +++ b/bazel/repository_locations.bzl @@ -43,10 +43,10 @@ REPOSITORY_LOCATIONS = dict( urls = ["https://commondatastorage.googleapis.com/chromium-boringssl-docs/fips/boringssl-66005f41fbc3529ffe8d007708756720529da20d.tar.xz"], ), com_google_absl = dict( - sha256 = "190b0c9e65ef0866b44c54b517b5a3e15b67a1001b34547f03f8f4d8553c2851", - strip_prefix = "abseil-cpp-63ee2f8877915a3565c29707dba8fe4d7822596a", - # 2020-01-08 - urls = ["https://github.com/abseil/abseil-cpp/archive/63ee2f8877915a3565c29707dba8fe4d7822596a.tar.gz"], + sha256 = "19391fb4882601a65cb648d638c11aa301ce5f525ef02da1a9eafd22f72d7c59", + strip_prefix = "abseil-cpp-37dd2562ec830d547a1524bb306be313ac3f2556", + # 2020-01-29 + urls = ["https://github.com/abseil/abseil-cpp/archive/37dd2562ec830d547a1524bb306be313ac3f2556.tar.gz"], ), com_github_apache_thrift = dict( sha256 = "7d59ac4fdcb2c58037ebd4a9da5f9a49e3e034bf75b3f26d9fe48ba3d8806e6b", diff --git a/source/extensions/access_loggers/wasm/config.cc b/source/extensions/access_loggers/wasm/config.cc index 624880d6eb1f4..a043af984b4ab 100644 --- a/source/extensions/access_loggers/wasm/config.cc +++ b/source/extensions/access_loggers/wasm/config.cc @@ -45,7 +45,7 @@ WasmAccessLogFactory::createAccessLogInstance(const Protobuf::Message& proto_con // NB: the Slot set() call doesn't complete inline, so all arguments must outlive this call. tls_slot->set([base_wasm, plugin, configuration](Event::Dispatcher& dispatcher) { return std::static_pointer_cast( - Common::Wasm::getOrCreateThreadLocalWasm(*base_wasm, plugin, *configuration, dispatcher)); + Common::Wasm::getOrCreateThreadLocalWasm(base_wasm, plugin, *configuration, dispatcher)); }); access_log->setTlsSlot(std::move(tls_slot)); }; diff --git a/source/extensions/common/wasm/wasm.cc b/source/extensions/common/wasm/wasm.cc index 2d67672f89bf7..83dfa9e4fe8c0 100644 --- a/source/extensions/common/wasm/wasm.cc +++ b/source/extensions/common/wasm/wasm.cc @@ -55,7 +55,7 @@ namespace { std::atomic active_wasm_; -std::string base64Sha256(absl::string_view data) { +std::string Sha256(absl::string_view data) { std::vector digest(SHA256_DIGEST_LENGTH); EVP_MD_CTX* ctx(EVP_MD_CTX_new()); auto rc = EVP_DigestInit(ctx, EVP_sha256()); @@ -65,11 +65,24 @@ std::string base64Sha256(absl::string_view data) { rc = EVP_DigestFinal(ctx, digest.data(), nullptr); RELEASE_ASSERT(rc == 1, "Failed to finalize digest"); EVP_MD_CTX_free(ctx); - return Base64::encode(reinterpret_cast(&digest[0]), digest.size()); + return std::string(reinterpret_cast(&digest[0]), digest.size()); } -// Map from Wasm ID to the local Wasm instance. -thread_local absl::flat_hash_map> local_wasms; +std::string Xor(absl::string_view a, absl::string_view b) { + ASSERT(a.size() == b.size()); + std::string result; + result.reserve(a.size()); + for (size_t i = 0; i < a.size(); i++) { + result.push_back(a[i] ^ b[i]); + } + return result; +} + +// Map from Wasm Key to the local Wasm instance. +thread_local absl::flat_hash_map> local_wasms_; +// Map from Wasm Key to the base Wasm instance, using a pointer to avoid the initialization fiasco. +ABSL_CONST_INIT absl::Mutex base_wasms_mutex_(absl::kConstInit); +absl::flat_hash_map>* base_wasms_ = nullptr; const std::string INLINE_STRING = ""; @@ -92,10 +105,11 @@ const uint8_t* decodeVarint(const uint8_t* pos, const uint8_t* end, uint32_t* ou } // namespace Wasm::Wasm(absl::string_view runtime, absl::string_view vm_id, absl::string_view vm_configuration, - Stats::ScopeSharedPtr scope, Upstream::ClusterManager& cluster_manager, - Event::Dispatcher& dispatcher) - : vm_id_(std::string(vm_id)), wasm_vm_(Common::Wasm::createWasmVm(runtime, scope)), - scope_(scope), cluster_manager_(cluster_manager), dispatcher_(dispatcher), + absl::string_view vm_key, Stats::ScopeSharedPtr scope, + Upstream::ClusterManager& cluster_manager, Event::Dispatcher& dispatcher) + : vm_id_(std::string(vm_id)), vm_key_(std::string(vm_key)), + wasm_vm_(Common::Wasm::createWasmVm(runtime, scope)), scope_(scope), + cluster_manager_(cluster_manager), dispatcher_(dispatcher), time_source_(dispatcher.timeSource()), vm_configuration_(vm_configuration), wasm_stats_(WasmStats{ ALL_WASM_STATS(POOL_COUNTER_PREFIX(*scope_, absl::StrCat("wasm.", runtime, ".")), @@ -243,18 +257,22 @@ void Wasm::getFunctions() { } } -Wasm::Wasm(const Wasm& wasm, Event::Dispatcher& dispatcher) - : std::enable_shared_from_this(wasm), vm_id_(wasm.vm_id_), - vm_id_with_hash_(wasm.vm_id_with_hash_), started_from_(wasm.wasm_vm()->cloneable()), - scope_(wasm.scope_), cluster_manager_(wasm.cluster_manager_), dispatcher_(dispatcher), - time_source_(dispatcher.timeSource()), wasm_stats_(wasm.wasm_stats_), - stat_name_set_(wasm.stat_name_set_) { +Wasm::Wasm(WasmHandleSharedPtr& base_wasm_handle, Event::Dispatcher& dispatcher) + : std::enable_shared_from_this(*base_wasm_handle->wasm()), + vm_id_(base_wasm_handle->wasm()->vm_id_), vm_key_(base_wasm_handle->wasm()->vm_key_), + started_from_(base_wasm_handle->wasm()->wasm_vm()->cloneable()), + scope_(base_wasm_handle->wasm()->scope_), + cluster_manager_(base_wasm_handle->wasm()->cluster_manager_), dispatcher_(dispatcher), + time_source_(dispatcher.timeSource()), base_wasm_handle_(base_wasm_handle), + wasm_stats_(base_wasm_handle->wasm()->wasm_stats_), + stat_name_set_(base_wasm_handle->wasm()->stat_name_set_) { if (started_from_ != Cloneable::NotCloneable) { - wasm_vm_ = wasm.wasm_vm()->clone(); + wasm_vm_ = base_wasm_handle->wasm()->wasm_vm()->clone(); } else { - wasm_vm_ = Common::Wasm::createWasmVm(wasm.wasm_vm()->runtime(), scope_); + wasm_vm_ = Common::Wasm::createWasmVm(base_wasm_handle->wasm()->wasm_vm()->runtime(), scope_); } - if (!initialize(wasm.code(), wasm.allow_precompiled())) { + if (!initialize(base_wasm_handle->wasm()->code(), + base_wasm_handle->wasm()->allow_precompiled())) { throw WasmException("Failed to load WASM code"); } active_wasm_++; @@ -280,10 +298,6 @@ bool Wasm::initialize(const std::string& code, bool allow_precompiled) { } if (started_from_ == Cloneable::NotCloneable) { - // Construct a unique identifier for the VM based on the provided vm_id and a hash of the - // code. - vm_id_with_hash_ = vm_id_ + ":" + base64Sha256(code); - auto ok = wasm_vm_->load(code, allow_precompiled); if (!ok) { return false; @@ -338,6 +352,16 @@ bool Wasm::initialize(const std::string& code, bool allow_precompiled) { return true; } +Context* Wasm::getOrCreateRootContext(const PluginSharedPtr& plugin) { + auto root_context = getRootContext(plugin->root_id_); + if (!root_context) { + auto context = std::make_unique(this, plugin); + root_context = context.get(); + root_contexts_[plugin->root_id_] = std::move(context); + } + return root_context; +} + void Wasm::startVm(Context* root_context) { /* Call "_start" function, and fallback to "__wasm_call_ctors" if the former is not available. */ if (_start_) { @@ -482,10 +506,6 @@ static void createWasmInternal(const VmConfig& vm_config, PluginSharedPtr plugin Api::Api& api, std::unique_ptr root_context_for_testing, Config::DataSource::RemoteAsyncDataProviderPtr& remote_data_provider, CreateWasmCallback&& cb) { - auto wasm = std::make_shared( - std::make_shared(vm_config.runtime(), vm_config.vm_id(), vm_config.configuration(), - scope, cluster_manager, dispatcher)); - std::string source, code; if (vm_config.code().has_remote()) { source = vm_config.code().remote().http_uri().uri(); @@ -495,22 +515,50 @@ static void createWasmInternal(const VmConfig& vm_config, PluginSharedPtr plugin .value_or(code.empty() ? EMPTY_STRING : INLINE_STRING); } - auto callback = [wasm, plugin, cb, source, allow_precompiled = vm_config.allow_precompiled(), + auto callback = [vm_config, scope, &cluster_manager, &dispatcher, plugin, cb, source, context_ptr = root_context_for_testing ? root_context_for_testing.release() : nullptr](const std::string& code) { std::unique_ptr context(context_ptr); if (code.empty()) { throw WasmException(fmt::format("Failed to load WASM code from {}", source)); } - if (!wasm->wasm()->initialize(code, allow_precompiled)) { - throw WasmException(fmt::format("Failed to initialize WASM code from {}", source)); - } - if (!context) { - wasm->wasm()->start(plugin); - } else { - wasm->wasm()->startForTesting(std::move(context), plugin); + // Construct a unique identifier for the VM based on a hash of the code, vm configuration data + // and vm_id. + std::string vm_key = Sha256(vm_config.vm_id()); + vm_key = Xor(vm_key, Sha256(vm_config.configuration())); + vm_key = Xor(vm_key, Sha256(code)); + vm_key = Base64::encode(&*vm_key.begin(), vm_key.size()); + + std::shared_ptr wasm; + { + absl::MutexLock l(&base_wasms_mutex_); + if (!base_wasms_) { + base_wasms_ = new std::remove_reference::type; + } + auto it = base_wasms_->find(vm_key); + if (it != base_wasms_->end()) { + wasm = it->second.lock(); + if (!wasm) { + base_wasms_->erase(it); + } + } + if (!wasm) { + wasm = std::make_shared(std::make_shared( + vm_config.runtime(), vm_config.vm_id(), vm_config.configuration(), vm_key, scope, + cluster_manager, dispatcher)); + if (!wasm->wasm()->initialize(code, vm_config.allow_precompiled())) { + throw WasmException(fmt::format("Failed to initialize WASM code from {}", source)); + } + if (!context) { + wasm->wasm()->start(plugin); + } else { + wasm->wasm()->startForTesting(std::move(context), plugin); + } + (*base_wasms_)[vm_key] = wasm; + } } - cb(wasm); + + cb(std::move(wasm)); }; if (vm_config.code().has_remote()) { @@ -543,40 +591,41 @@ void createWasmForTesting(const envoy::extensions::wasm::v3::VmConfig& vm_config std::move(root_context_for_testing), remote_data_provider, std::move(cb)); } -WasmHandleSharedPtr createThreadLocalWasm(WasmHandle& base_wasm, PluginSharedPtr plugin, +WasmHandleSharedPtr createThreadLocalWasm(WasmHandleSharedPtr& base_wasm, PluginSharedPtr plugin, absl::string_view configuration, Event::Dispatcher& dispatcher) { - auto wasm = std::make_shared(std::make_shared(*base_wasm.wasm(), dispatcher)); - Context* root_context = wasm->wasm()->start(plugin); - if (!wasm->wasm()->configure(root_context, plugin, configuration)) { + auto wasm_handle = std::make_shared(std::make_shared(base_wasm, dispatcher)); + Context* root_context = wasm_handle->wasm()->start(plugin); + if (!wasm_handle->wasm()->configure(root_context, plugin, configuration)) { throw WasmException("Failed to configure WASM code"); } - local_wasms[wasm->wasm()->vm_id_with_hash()] = wasm; - return wasm; + local_wasms_[wasm_handle->wasm()->vm_key()] = wasm_handle; + return wasm_handle; } -WasmHandleSharedPtr getThreadLocalWasmPtr(absl::string_view vm_id) { - auto it = local_wasms.find(vm_id); - if (it == local_wasms.end()) { +WasmHandleSharedPtr getThreadLocalWasmPtr(absl::string_view vm_key) { + auto it = local_wasms_.find(vm_key); + if (it == local_wasms_.end()) { return nullptr; } auto wasm = it->second.lock(); if (!wasm) { - local_wasms.erase(vm_id); + local_wasms_.erase(vm_key); } return wasm; } -WasmHandleSharedPtr getOrCreateThreadLocalWasm(WasmHandle& base_wasm, PluginSharedPtr plugin, +WasmHandleSharedPtr getOrCreateThreadLocalWasm(WasmHandleSharedPtr base_wasm, + PluginSharedPtr plugin, absl::string_view configuration, Event::Dispatcher& dispatcher) { - auto wasm = getThreadLocalWasmPtr(base_wasm.wasm()->vm_id_with_hash()); - if (wasm) { - auto root_context = wasm->wasm()->start(plugin); - if (!wasm->wasm()->configure(root_context, plugin, configuration)) { + auto wasm_handle = getThreadLocalWasmPtr(base_wasm->wasm()->vm_key()); + if (wasm_handle) { + auto root_context = wasm_handle->wasm()->getOrCreateRootContext(plugin); + if (!wasm_handle->wasm()->configure(root_context, plugin, configuration)) { throw WasmException("Failed to configure WASM code"); } - return wasm; + return wasm_handle; } return createThreadLocalWasm(base_wasm, plugin, configuration, dispatcher); } diff --git a/source/extensions/common/wasm/wasm.h b/source/extensions/common/wasm/wasm.h index 34055d9d39f63..1a326e40eb276 100644 --- a/source/extensions/common/wasm/wasm.h +++ b/source/extensions/common/wasm/wasm.h @@ -53,13 +53,15 @@ using VmConfig = envoy::extensions::wasm::v3::VmConfig; using WasmForeignFunction = std::function)>; +class WasmHandle; + // Wasm execution instance. Manages the Envoy side of the Wasm interface. class Wasm : public Logger::Loggable, public std::enable_shared_from_this { public: Wasm(absl::string_view runtime, absl::string_view vm_id, absl::string_view vm_configuration, - Stats::ScopeSharedPtr scope, Upstream::ClusterManager& cluster_manager, - Event::Dispatcher& dispatcher); - Wasm(const Wasm& other, Event::Dispatcher& dispatcher); + absl::string_view vm_key, Stats::ScopeSharedPtr scope, + Upstream::ClusterManager& cluster_manager, Event::Dispatcher& dispatcher); + Wasm(std::shared_ptr& other, Event::Dispatcher& dispatcher); ~Wasm(); bool initialize(const std::string& code, bool allow_precompiled = false); @@ -68,11 +70,12 @@ class Wasm : public Logger::Loggable, public std::enable_share Context* start(PluginSharedPtr plugin); // returns the root Context. absl::string_view vm_id() const { return vm_id_; } - absl::string_view vm_id_with_hash() const { return vm_id_with_hash_; } + absl::string_view vm_key() const { return vm_key_; } WasmVm* wasm_vm() const { return wasm_vm_.get(); } Context* vm_context() const { return vm_context_.get(); } Stats::StatNameSetSharedPtr stat_name_set() const { return stat_name_set_; } Context* getRootContext(absl::string_view root_id) { return root_contexts_[root_id].get(); } + Context* getOrCreateRootContext(const PluginSharedPtr& plugin); Context* getContext(uint32_t id) { auto it = contexts_.find(id); if (it != contexts_.end()) @@ -169,8 +172,8 @@ class Wasm : public Logger::Loggable, public std::enable_share void establishEnvironment(); // Language specific environments. void getFunctions(); // Get functions call into WASM. - std::string vm_id_; // User-provided vm_id. - std::string vm_id_with_hash_; // vm_id + hash of code. + std::string vm_id_; // User-provided vm_id. + std::string vm_key_; // Hash(code, vm configuration data, vm_id_) std::unique_ptr wasm_vm_; Cloneable started_from_{Cloneable::NotCloneable}; Stats::ScopeSharedPtr scope_; @@ -234,6 +237,8 @@ class Wasm : public Logger::Loggable, public std::enable_share WasmCallVoid<1> on_log_; WasmCallVoid<1> on_delete_; + std::shared_ptr base_wasm_handle_; + // Used by the base_wasm to enable non-clonable thread local Wasm(s) to be constructed. std::string code_; std::string vm_configuration_; @@ -269,14 +274,9 @@ class WasmHandle : public Envoy::Server::Wasm, public std::enable_shared_from_this { public: explicit WasmHandle(WasmSharedPtr wasm) : wasm_(wasm) {} - ~WasmHandle() { - auto wasm = wasm_; - // NB: V8 will stack overflow during the stress test if we shutdown with the call stack in the - // ThreadLocal set call so shift to a fresh call stack. - wasm_->dispatcher().post([wasm] { wasm->shutdown(); }); - } + ~WasmHandle() { wasm_->shutdown(); } - const WasmSharedPtr& wasm() { return wasm_; } + WasmSharedPtr& wasm() { return wasm_; } private: WasmSharedPtr wasm_; @@ -308,8 +308,10 @@ void createWasmForTesting(const VmConfig& vm_config, PluginSharedPtr plugin, // Get an existing ThreadLocal VM matching 'vm_id' or nullptr if there isn't one. WasmHandleSharedPtr getThreadLocalWasmPtr(absl::string_view vm_id); // Get an existing ThreadLocal VM matching 'vm_id' or create one using 'base_wavm' by cloning or by -// using it it as a template. -WasmHandleSharedPtr getOrCreateThreadLocalWasm(WasmHandle& base_wasm, PluginSharedPtr plugin, +// using it it as a template. Note that 'base_wasm' typically is a const lambda capture and needs +// to be copied to be passed, hence the pass-by-value interface. +WasmHandleSharedPtr getOrCreateThreadLocalWasm(WasmHandleSharedPtr base_wasm, + PluginSharedPtr plugin, absl::string_view configuration, Event::Dispatcher& dispatcher); diff --git a/source/extensions/filters/http/wasm/wasm_filter.cc b/source/extensions/filters/http/wasm/wasm_filter.cc index 097a4462631d4..9e2a7644dc8aa 100644 --- a/source/extensions/filters/http/wasm/wasm_filter.cc +++ b/source/extensions/filters/http/wasm/wasm_filter.cc @@ -27,7 +27,7 @@ FilterConfig::FilterConfig(const envoy::extensions::filters::http::wasm::v3::Was // NB: the Slot set() call doesn't complete inline, so all arguments must outlive this call. tls_slot_->set([base_wasm, plugin, configuration](Event::Dispatcher& dispatcher) { return std::static_pointer_cast( - Common::Wasm::getOrCreateThreadLocalWasm(*base_wasm, plugin, *configuration, dispatcher)); + Common::Wasm::getOrCreateThreadLocalWasm(base_wasm, plugin, *configuration, dispatcher)); }); }; diff --git a/source/extensions/filters/network/wasm/wasm_filter.cc b/source/extensions/filters/network/wasm/wasm_filter.cc index c0914b257a68d..9a8369df51edf 100644 --- a/source/extensions/filters/network/wasm/wasm_filter.cc +++ b/source/extensions/filters/network/wasm/wasm_filter.cc @@ -22,7 +22,7 @@ FilterConfig::FilterConfig(const envoy::extensions::filters::network::wasm::v3:: // NB: the Slot set() call doesn't complete inline, so all arguments must outlive this call. tls_slot_->set([base_wasm, plugin, configuration](Event::Dispatcher& dispatcher) { return std::static_pointer_cast( - Common::Wasm::getOrCreateThreadLocalWasm(*base_wasm, plugin, *configuration, dispatcher)); + Common::Wasm::getOrCreateThreadLocalWasm(base_wasm, plugin, *configuration, dispatcher)); }); }; diff --git a/source/extensions/wasm/config.cc b/source/extensions/wasm/config.cc index 69aff754a82d9..bb47e599d3e74 100644 --- a/source/extensions/wasm/config.cc +++ b/source/extensions/wasm/config.cc @@ -23,8 +23,7 @@ void WasmFactory::createWasm(const envoy::extensions::wasm::v3::WasmService& con config.config().name(), config.config().root_id(), config.config().vm_config().vm_id(), envoy::config::core::v3::TrafficDirection::UNSPECIFIED, context.localInfo(), nullptr); - auto callback = [&context, &config, plugin, - cb](std::shared_ptr base_wasm) { + auto callback = [&context, &config, plugin, cb](Common::Wasm::WasmHandleSharedPtr base_wasm) { if (config.singleton()) { // Return the WASM VM which will be stored as a singleton by the Server. auto root_context = base_wasm->wasm()->start(plugin); @@ -40,7 +39,7 @@ void WasmFactory::createWasm(const envoy::extensions::wasm::v3::WasmService& con context.threadLocal().allocateSlot()->set([base_wasm, plugin, configuration](Event::Dispatcher& dispatcher) { return std::static_pointer_cast( - Common::Wasm::getOrCreateThreadLocalWasm(*base_wasm, plugin, *configuration, dispatcher)); + Common::Wasm::getOrCreateThreadLocalWasm(base_wasm, plugin, *configuration, dispatcher)); }); // Do not return this WASM VM since this is per-thread. Returning it would indicate that // this is a singleton. diff --git a/test/extensions/access_loggers/wasm/config_test.cc b/test/extensions/access_loggers/wasm/config_test.cc index a5c3474edfaf3..5212b91a55929 100644 --- a/test/extensions/access_loggers/wasm/config_test.cc +++ b/test/extensions/access_loggers/wasm/config_test.cc @@ -62,7 +62,7 @@ TEST_P(WasmAccessLogConfigTest, CreateWasmFromEmpty) { AccessLog::InstanceSharedPtr instance; EXPECT_THROW_WITH_MESSAGE( instance = factory->createAccessLogInstance(*message, std::move(filter), context), - Common::Wasm::WasmVmException, "Failed to create WASM VM with unspecified runtime."); + Common::Wasm::WasmException, "Failed to load WASM code from "); } TEST_P(WasmAccessLogConfigTest, CreateWasmFromWASM) { diff --git a/test/extensions/common/wasm/wasm_test.cc b/test/extensions/common/wasm/wasm_test.cc index 455b7c89b8a37..df9fae5fbd75a 100644 --- a/test/extensions/common/wasm/wasm_test.cc +++ b/test/extensions/common/wasm/wasm_test.cc @@ -23,7 +23,9 @@ namespace Wasm { class TestContext : public Extensions::Common::Wasm::Context { public: - TestContext(Extensions::Common::Wasm::Wasm* wasm) : Extensions::Common::Wasm::Context(wasm) {} + TestContext() : Extensions::Common::Wasm::Context() {} + explicit TestContext(Extensions::Common::Wasm::Wasm* wasm) + : Extensions::Common::Wasm::Context(wasm) {} ~TestContext() override {} void scriptLog(spdlog::level::level_enum level, absl::string_view message) override { std::cerr << std::string(message) << "\n"; @@ -52,11 +54,12 @@ TEST_P(WasmCommonTest, Logging) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = "logging"; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_shared( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); auto wasm_weak = std::weak_ptr(wasm); @@ -107,11 +110,12 @@ TEST_P(WasmCommonTest, BadSignature) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = ""; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); const auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -136,11 +140,12 @@ TEST_P(WasmCommonTest, Segv) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = "segv"; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); const auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/common/wasm/test_data/test_cpp.wasm")); @@ -173,11 +178,12 @@ TEST_P(WasmCommonTest, DivByZero) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = "divbyzero"; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); const auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -211,11 +217,12 @@ TEST_P(WasmCommonTest, EmscriptenVersion) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = ""; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); const auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -244,11 +251,12 @@ TEST_P(WasmCommonTest, IntrinsicGlobals) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = "globals"; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); std::string code; @@ -278,11 +286,12 @@ TEST_P(WasmCommonTest, Stats) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = "stats"; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); std::string code; @@ -319,11 +328,12 @@ TEST_P(WasmCommonTest, Foreign) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = "foreign"; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); std::string code; @@ -344,6 +354,66 @@ TEST_P(WasmCommonTest, Foreign) { wasm->startForTesting(std::move(context), plugin); } +TEST_P(WasmCommonTest, VmCache) { + Stats::IsolatedStoreImpl stats_store; + Api::ApiPtr api = Api::createApiForTest(stats_store); + NiceMock cluster_manager; + NiceMock init_manager; + Event::DispatcherPtr dispatcher(api->allocateDispatcher()); + Config::DataSource::RemoteAsyncDataProviderPtr remote_data_provider; + auto scope = Stats::ScopeSharedPtr(stats_store.createScope("wasm.")); + NiceMock local_info; + auto name = ""; + auto root_id = ""; + auto vm_id = ""; + auto vm_configuration = "vm_cache"; + auto plugin = std::make_shared( + name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, + nullptr); + + VmConfig vm_config; + vm_config.set_runtime(absl::StrCat("envoy.wasm.runtime.", GetParam())); + vm_config.set_configuration(vm_configuration); + std::string code; + if (GetParam() != "null") { + code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( + absl::StrCat("{{ test_rundir }}/test/extensions/common/wasm/test_data/test_cpp.wasm"))); + } else { + // The name of the Null VM plugin. + code = "CommonWasmTestCpp"; + } + EXPECT_FALSE(code.empty()); + vm_config.mutable_code()->mutable_local()->set_inline_bytes(code); + WasmHandleSharedPtr wasm_handle; + auto root_context = new Extensions::Common::Wasm::TestContext(); + EXPECT_CALL(*root_context, scriptLog_(spdlog::level::info, Eq("on_vm_start vm_cache"))); + EXPECT_CALL(*root_context, scriptLog_(spdlog::level::info, Eq("on_done logging"))); + EXPECT_CALL(*root_context, scriptLog_(spdlog::level::info, Eq("on_delete logging"))); + createWasmForTesting(vm_config, plugin, scope, cluster_manager, init_manager, *dispatcher, *api, + std::unique_ptr(root_context), remote_data_provider, + [&wasm_handle](WasmHandleSharedPtr w) { wasm_handle = w; }); + + EXPECT_NE(wasm_handle, nullptr); + + WasmHandleSharedPtr wasm_handle2; + auto root_context2 = new Extensions::Common::Wasm::Context(); + createWasmForTesting(vm_config, plugin, scope, cluster_manager, init_manager, *dispatcher, *api, + std::unique_ptr(root_context2), remote_data_provider, + [&wasm_handle2](WasmHandleSharedPtr w) { wasm_handle2 = w; }); + EXPECT_NE(wasm_handle2, nullptr); + EXPECT_EQ(wasm_handle, wasm_handle2); + + plugin.reset(); + auto wasm = wasm_handle->wasm().get(); + wasm_handle.reset(); + wasm_handle2.reset(); + + dispatcher->run(Event::Dispatcher::RunType::NonBlock); + wasm->configure(root_context, plugin, "done"); + dispatcher->run(Event::Dispatcher::RunType::NonBlock); + dispatcher->clearDeferredDeleteList(); +} + } // namespace Wasm } // namespace Common } // namespace Extensions diff --git a/test/extensions/filters/http/wasm/wasm_filter_test.cc b/test/extensions/filters/http/wasm/wasm_filter_test.cc index 5919ad89bb96c..9ad05dcf3c4a5 100644 --- a/test/extensions/filters/http/wasm/wasm_filter_test.cc +++ b/test/extensions/filters/http/wasm/wasm_filter_test.cc @@ -549,7 +549,6 @@ TEST_P(WasmHttpFilterTest, SharedQueue) { scriptLog_(spdlog::level::info, Eq(absl::string_view("onQueueReady")))); EXPECT_CALL(*root_context_, scriptLog_(spdlog::level::debug, Eq(absl::string_view("data data1 Ok")))); - EXPECT_CALL(dispatcher_, post(_)).Times(2).WillRepeatedly(Return()); Http::TestHeaderMapImpl request_headers{{":path", "/"}}; EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true)); auto token = Common::Wasm::resolveQueueForTest("vm_id", "my_shared_queue"); diff --git a/test/extensions/wasm/config_test.cc b/test/extensions/wasm/config_test.cc index 6275927283fbb..4e55c5e96bb93 100644 --- a/test/extensions/wasm/config_test.cc +++ b/test/extensions/wasm/config_test.cc @@ -58,7 +58,6 @@ TEST_P(WasmFactoryTest, CreateWasmFromWASM) { EXPECT_CALL(init_watcher, ready()); init_manager.initialize(init_watcher); EXPECT_NE(wasmptr, nullptr); - EXPECT_CALL(dispatcher, post(_)); wasmptr.reset(); } @@ -83,7 +82,6 @@ TEST_P(WasmFactoryTest, CreateWasmFromWASMPerThread) { auto scope = Stats::ScopeSharedPtr(stats_store.createScope("wasm.")); Server::Configuration::WasmFactoryContextImpl context(cluster_manager, init_manager, dispatcher, tls, *api, scope, local_info); - EXPECT_CALL(dispatcher, post(_)); factory->createWasm(config, context, [](Server::WasmSharedPtr wasm) { EXPECT_EQ(wasm, nullptr); }); EXPECT_CALL(init_watcher, ready()); @@ -112,7 +110,6 @@ TEST_P(WasmFactoryTest, MissingImport) { auto scope = Stats::ScopeSharedPtr(stats_store.createScope("wasm.")); Server::Configuration::WasmFactoryContextImpl context(cluster_manager, init_manager, dispatcher, tls, *api, scope, local_info); - EXPECT_CALL(dispatcher, post(_)); EXPECT_THROW_WITH_REGEX(factory->createWasm(config, context, [](Server::WasmSharedPtr) {}); , Extensions::Common::Wasm::WasmVmException, "Failed to load WASM module due to a missing import: env.missing"); diff --git a/test/extensions/wasm/wasm_speed_test.cc b/test/extensions/wasm/wasm_speed_test.cc index dca343f19256d..fd0e6f974c866 100644 --- a/test/extensions/wasm/wasm_speed_test.cc +++ b/test/extensions/wasm/wasm_speed_test.cc @@ -54,12 +54,13 @@ static void BM_WasmSimpleCallSpeedTest(benchmark::State& state, std::string test auto root_id = "some_long_root_id"; auto vm_id = ""; auto vm_configuration = test; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", vm), vm_id, vm_configuration, scope, cluster_manager, - *dispatcher); + absl::StrCat("envoy.wasm.runtime.", vm), vm_id, vm_configuration, vm_key, scope, + cluster_manager, *dispatcher); std::string code; if (vm == "null") { code = "WasmSpeedCpp"; diff --git a/test/extensions/wasm/wasm_test.cc b/test/extensions/wasm/wasm_test.cc index 82fd51677b738..9deb40e86c499 100644 --- a/test/extensions/wasm/wasm_test.cc +++ b/test/extensions/wasm/wasm_test.cc @@ -64,12 +64,13 @@ TEST_P(WasmTestMatrix, Logging) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = ""; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_shared( - absl::StrCat("envoy.wasm.runtime.", std::get<0>(GetParam())), vm_id, vm_configuration, scope, - cluster_manager, *dispatcher); + absl::StrCat("envoy.wasm.runtime.", std::get<0>(GetParam())), vm_id, vm_configuration, vm_key, + scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); auto wasm_weak = std::weak_ptr(wasm); auto wasm_handler = std::make_unique(std::move(wasm)); @@ -111,11 +112,12 @@ TEST_P(WasmTest, BadSignature) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = ""; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); const auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -137,11 +139,12 @@ TEST_P(WasmTest, Segv) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = ""; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); const auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( "{{ test_rundir }}/test/extensions/wasm/test_data/segv_cpp.wasm")); @@ -174,11 +177,12 @@ TEST_P(WasmTest, DivByZero) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = ""; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); const auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -212,11 +216,12 @@ TEST_P(WasmTest, EmscriptenVersion) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = ""; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); const auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -244,11 +249,12 @@ TEST_P(WasmTest, IntrinsicGlobals) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = ""; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); const auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -278,11 +284,12 @@ TEST_P(WasmTest, Asm2Wasm) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = ""; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); const auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -305,11 +312,12 @@ TEST_P(WasmTest, Stats) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = ""; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); const auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -340,11 +348,12 @@ TEST_P(WasmTest, StatsHigherLevel) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = ""; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); const auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute( @@ -380,11 +389,12 @@ TEST_P(WasmTest, StatsHighLevel) { auto root_id = ""; auto vm_id = ""; auto vm_configuration = ""; + auto vm_key = ""; auto plugin = std::make_shared( name, root_id, vm_id, envoy::config::core::v3::TrafficDirection::UNSPECIFIED, local_info, nullptr); auto wasm = std::make_unique( - absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, scope, + absl::StrCat("envoy.wasm.runtime.", GetParam()), vm_id, vm_configuration, vm_key, scope, cluster_manager, *dispatcher); EXPECT_NE(wasm, nullptr); const auto code = TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(