You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by ki...@apache.org on 2023/03/26 14:56:56 UTC

[trafficserver] branch master updated: Add support for multiple yaml config files for wasm plugin (#9483)

This is an automated email from the ASF dual-hosted git repository.

kichan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/master by this push:
     new a5b1dd2e4 Add support for multiple yaml config files for wasm plugin (#9483)
a5b1dd2e4 is described below

commit a5b1dd2e40aff3d391df91e8be744cb87520c141
Author: Kit Chan <ki...@apache.org>
AuthorDate: Sun Mar 26 07:56:47 2023 -0700

    Add support for multiple yaml config files for wasm plugin (#9483)
    
    * Add support for multiple yaml config files
    
    * fix clang-format issues
    
    * fix clang-format issues
    
    * Update wasm.en.rst
    
    * Fix problem with breaks and add error message related to file read
---
 doc/admin-guide/plugins/wasm.en.rst    |   3 +-
 plugins/experimental/wasm/wasm_main.cc | 502 ++++++++++++++++++---------------
 2 files changed, 275 insertions(+), 230 deletions(-)

diff --git a/doc/admin-guide/plugins/wasm.en.rst b/doc/admin-guide/plugins/wasm.en.rst
index ec129cbbe..9b83b8b8e 100644
--- a/doc/admin-guide/plugins/wasm.en.rst
+++ b/doc/admin-guide/plugins/wasm.en.rst
@@ -97,6 +97,8 @@ generated wasm modules with the plugin.
 Runtime can be chosen by changing the ``runtime`` field inside the yaml configuration file for the plugin.
 ``ats.wasm.runtime.wamr`` is for WAMR while ``ats.wasm.runtime.wasmedge`` is for WasmEdge.
 
+The plugin can also take more than one yaml file as arguments and can thus load more than one wasm modules.
+
 TODO
 ====
 
@@ -104,7 +106,6 @@ TODO
 * Need to support functionality for retrieving and setting request/response body
 * Need to support functionality for making async request call
 * Need to support L4 lifecycle handler functions
-* Support loading more than one Wasm module
 
 Limitations
 ===========
diff --git a/plugins/experimental/wasm/wasm_main.cc b/plugins/experimental/wasm/wasm_main.cc
index c9ac17e26..5267914db 100644
--- a/plugins/experimental/wasm/wasm_main.cc
+++ b/plugins/experimental/wasm/wasm_main.cc
@@ -37,10 +37,9 @@
 
 // struct for storing plugin configuration
 struct WasmInstanceConfig {
-  std::string config_filename;
-  std::string wasm_filename;
-  std::shared_ptr<ats_wasm::Wasm> wasm           = nullptr;
-  std::shared_ptr<proxy_wasm::PluginBase> plugin = nullptr;
+  std::list<std::string> config_filenames = {};
+
+  std::list<std::pair<std::shared_ptr<ats_wasm::Wasm>, std::shared_ptr<proxy_wasm::PluginBase>>> configs = {};
 
   std::list<std::pair<std::shared_ptr<ats_wasm::Wasm>, std::shared_ptr<proxy_wasm::PluginBase>>> deleted_configs = {};
 };
@@ -60,23 +59,31 @@ schedule_handler(TSCont contp, TSEvent /*event*/, void * /*data*/)
 
   c->onTick(0); // use 0 as  token
 
-  if (!wasm_config->wasm) {
-    TSError("[wasm][%s] Configuration object is null", __FUNCTION__);
+  if (wasm_config->configs.empty()) {
+    TSError("[wasm][%s] Configuration objects are empty", __FUNCTION__);
     TSMutexUnlock(old_wasm->mutex());
     return 0;
   }
 
-  if (c->wasm() == wasm_config->wasm.get()) {
-    auto *wasm               = static_cast<ats_wasm::Wasm *>(c->wasm());
-    uint32_t root_context_id = c->id();
-    if (wasm->existsTimerPeriod(root_context_id)) {
-      TSDebug(WASM_DEBUG_TAG, "[%s] reschedule continuation", __FUNCTION__);
-      std::chrono::milliseconds period = wasm->getTimerPeriod(root_context_id);
-      TSContScheduleOnPool(contp, static_cast<TSHRTime>(period.count()), TS_THREAD_POOL_NET);
-    } else {
-      TSDebug(WASM_DEBUG_TAG, "[%s] can't find period for root context id: %d", __FUNCTION__, root_context_id);
+  bool found = false;
+  for (auto it = wasm_config->configs.begin(); it != wasm_config->configs.end(); it++) {
+    std::shared_ptr<ats_wasm::Wasm> wbp = it->first;
+    if (wbp.get() == old_wasm) {
+      found                    = true;
+      auto *wasm               = static_cast<ats_wasm::Wasm *>(c->wasm());
+      uint32_t root_context_id = c->id();
+      if (wasm->existsTimerPeriod(root_context_id)) {
+        TSDebug(WASM_DEBUG_TAG, "[%s] reschedule continuation", __FUNCTION__);
+        std::chrono::milliseconds period = wasm->getTimerPeriod(root_context_id);
+        TSContScheduleOnPool(contp, static_cast<TSHRTime>(period.count()), TS_THREAD_POOL_NET);
+      } else {
+        TSDebug(WASM_DEBUG_TAG, "[%s] can't find period for root context id: %d", __FUNCTION__, root_context_id);
+      }
+      break;
     }
-  } else {
+  }
+
+  if (!found) {
     std::shared_ptr<ats_wasm::Wasm> temp = nullptr;
     uint32_t root_context_id             = c->id();
     old_wasm->removeTimerPeriod(root_context_id);
@@ -181,11 +188,20 @@ http_event_handler(TSCont contp, TSEvent event, void *data)
   case TS_EVENT_HTTP_READ_CACHE_HDR:
     break;
 
-  case TS_EVENT_HTTP_TXN_CLOSE:
+  case TS_EVENT_HTTP_TXN_CLOSE: {
     context->onDone();
     context->onDelete();
 
-    if (context->wasm() == wasm_config->wasm.get()) {
+    bool found = false;
+    for (auto it = wasm_config->configs.begin(); it != wasm_config->configs.end(); it++) {
+      std::shared_ptr<ats_wasm::Wasm> wbp = it->first;
+      if (wbp.get() == context->wasm()) {
+        found = true;
+        break;
+      }
+    }
+
+    if (found) {
       TSDebug(WASM_DEBUG_TAG, "[%s] config wasm has not changed", __FUNCTION__);
     } else {
       if (old_wasm->readyShutdown()) {
@@ -220,7 +236,7 @@ http_event_handler(TSCont contp, TSEvent event, void *data)
     TSContDestroy(contp);
     result = 0;
     break;
-
+  }
   default:
     break;
   }
@@ -244,24 +260,28 @@ http_event_handler(TSCont contp, TSEvent event, void *data)
 static int
 global_hook_handler(TSCont /*contp*/, TSEvent /*event*/, void *data)
 {
-  auto *wasm = wasm_config->wasm.get();
-  TSMutexLock(wasm->mutex());
-  auto *rootContext = wasm->getRootContext(wasm_config->plugin, false);
-  auto *context     = new ats_wasm::Context(wasm, rootContext->id(), wasm_config->plugin);
-  auto *txnp        = static_cast<TSHttpTxn>(data);
-  context->initialize(txnp);
-  context->onCreate();
-  TSMutexUnlock(wasm->mutex());
-
-  // create continuation for transaction
-  TSCont txn_contp = TSContCreate(http_event_handler, nullptr);
-  TSHttpTxnHookAdd(txnp, TS_HTTP_READ_REQUEST_HDR_HOOK, txn_contp);
-  TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, txn_contp);
-  TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, txn_contp);
-  // add send response hook for local reply if needed
-  TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, txn_contp);
-
-  TSContDataSet(txn_contp, context);
+  auto *txnp = static_cast<TSHttpTxn>(data);
+  for (auto it = wasm_config->configs.begin(); it != wasm_config->configs.end(); it++) {
+    std::shared_ptr<ats_wasm::Wasm> wbp         = it->first;
+    std::shared_ptr<proxy_wasm::PluginBase> plg = it->second;
+    auto *wasm                                  = wbp.get();
+    TSMutexLock(wasm->mutex());
+    auto *rootContext = wasm->getRootContext(plg, false);
+    auto *context     = new ats_wasm::Context(wasm, rootContext->id(), plg);
+    context->initialize(txnp);
+    context->onCreate();
+    TSMutexUnlock(wasm->mutex());
+
+    // create continuation for transaction
+    TSCont txn_contp = TSContCreate(http_event_handler, nullptr);
+    TSHttpTxnHookAdd(txnp, TS_HTTP_READ_REQUEST_HDR_HOOK, txn_contp);
+    TSHttpTxnHookAdd(txnp, TS_HTTP_READ_RESPONSE_HDR_HOOK, txn_contp);
+    TSHttpTxnHookAdd(txnp, TS_HTTP_TXN_CLOSE_HOOK, txn_contp);
+    // add send response hook for local reply if needed
+    TSHttpTxnHookAdd(txnp, TS_HTTP_SEND_RESPONSE_HDR_HOOK, txn_contp);
+
+    TSContDataSet(txn_contp, context);
+  }
 
   TSHttpTxnReenable(txnp, TS_EVENT_HTTP_CONTINUE);
   return 0;
@@ -273,13 +293,26 @@ read_file(const std::string &fn, std::string *s)
 {
   auto fd = open(fn.c_str(), O_RDONLY);
   if (fd < 0) {
+    char *errmsg = strerror(errno);
+    TSError("[wasm][%s] wasm unable to open: %s", __FUNCTION__, errmsg);
     return -1;
   }
   auto n = ::lseek(fd, 0, SEEK_END);
+  if (n < 0) {
+    char *errmsg = strerror(errno);
+    TSError("[wasm][%s] wasm unable to lseek: %s", __FUNCTION__, errmsg);
+    return -1;
+  }
   ::lseek(fd, 0, SEEK_SET);
   s->resize(n);
   auto nn = ::read(fd, const_cast<char *>(&*s->begin()), n);
+  if (nn < 0) {
+    char *errmsg = strerror(errno);
+    TSError("[wasm][%s] wasm unable to read: %s", __FUNCTION__, errmsg);
+    return -1;
+  }
   if (nn != static_cast<ssize_t>(n)) {
+    TSError("[wasm][%s] wasm unable to read: size different from buffer", __FUNCTION__);
     return -1;
   }
   return 0;
@@ -289,239 +322,248 @@ read_file(const std::string &fn, std::string *s)
 static bool
 read_configuration()
 {
-  // PluginBase parameters
-  std::string name          = "";
-  std::string root_id       = "";
-  std::string configuration = "";
-  bool fail_open            = true;
-
-  // WasmBase parameters
-  std::string runtime          = "";
-  std::string vm_id            = "";
-  std::string vm_configuration = "";
-  std::string wasm_filename    = "";
-  bool allow_precompiled       = true;
-
-  proxy_wasm::AllowedCapabilitiesMap cap_maps;
-  std::unordered_map<std::string, std::string> envs;
-
-  try {
-    YAML::Node config = YAML::LoadFile(wasm_config->config_filename);
-
-    for (YAML::const_iterator it = config.begin(); it != config.end(); ++it) {
-      const std::string &node_name = it->first.as<std::string>();
-      YAML::NodeType::value type   = it->second.Type();
-
-      if (node_name != "config" || type != YAML::NodeType::Map) {
-        TSError("[wasm][%s] Invalid YAML Configuration format for wasm: %s, reason: Top level nodes must be named config and be of "
-                "type map",
-                __FUNCTION__, wasm_config->config_filename.c_str());
-        return false;
-      }
+  std::list<std::pair<std::shared_ptr<ats_wasm::Wasm>, std::shared_ptr<proxy_wasm::PluginBase>>> new_configs = {};
+
+  for (auto const &cfn : wasm_config->config_filenames) {
+    // PluginBase parameters
+    std::string name          = "";
+    std::string root_id       = "";
+    std::string configuration = "";
+    bool fail_open            = true;
+
+    // WasmBase parameters
+    std::string runtime          = "";
+    std::string vm_id            = "";
+    std::string vm_configuration = "";
+    std::string wasm_filename    = "";
+    bool allow_precompiled       = true;
+
+    proxy_wasm::AllowedCapabilitiesMap cap_maps;
+    std::unordered_map<std::string, std::string> envs;
+
+    try {
+      YAML::Node config = YAML::LoadFile(cfn);
+
+      for (YAML::const_iterator it = config.begin(); it != config.end(); ++it) {
+        const std::string &node_name = it->first.as<std::string>();
+        YAML::NodeType::value type   = it->second.Type();
+
+        if (node_name != "config" || type != YAML::NodeType::Map) {
+          TSError(
+            "[wasm][%s] Invalid YAML Configuration format for wasm: %s, reason: Top level nodes must be named config and be of "
+            "type map",
+            __FUNCTION__, cfn.c_str());
+          return false;
+        }
 
-      for (YAML::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) {
-        const YAML::Node first  = it2->first;
-        const YAML::Node second = it2->second;
+        for (YAML::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) {
+          const YAML::Node first  = it2->first;
+          const YAML::Node second = it2->second;
 
-        const std::string &key = first.as<std::string>();
-        if (second.IsScalar()) {
-          const std::string &value = second.as<std::string>();
-          if (key == "name") {
-            name = value;
-          }
-          if (key == "root_id" || key == "rootId") {
-            root_id = value;
-          }
-          if (key == "configuration") {
-            configuration = value;
-          }
-          if (key == "fail_open") {
-            if (value == "false") {
-              fail_open = false;
+          const std::string &key = first.as<std::string>();
+          if (second.IsScalar()) {
+            const std::string &value = second.as<std::string>();
+            if (key == "name") {
+              name = value;
+            }
+            if (key == "root_id" || key == "rootId") {
+              root_id = value;
+            }
+            if (key == "configuration") {
+              configuration = value;
+            }
+            if (key == "fail_open") {
+              if (value == "false") {
+                fail_open = false;
+              }
             }
           }
-        }
-        if (second.IsMap() && (key == "capability_restriction_config")) {
-          if (second["allowed_capabilities"]) {
-            const YAML::Node ac_node = second["allowed_capabilities"];
-            if (ac_node.IsSequence()) {
-              for (const auto &i : ac_node) {
-                auto ac = i.as<std::string>();
-                proxy_wasm::SanitizationConfig sc;
-                cap_maps[ac] = sc;
+          if (second.IsMap() && (key == "capability_restriction_config")) {
+            if (second["allowed_capabilities"]) {
+              const YAML::Node ac_node = second["allowed_capabilities"];
+              if (ac_node.IsSequence()) {
+                for (const auto &i : ac_node) {
+                  auto ac = i.as<std::string>();
+                  proxy_wasm::SanitizationConfig sc;
+                  cap_maps[ac] = sc;
+                }
               }
             }
           }
-        }
 
-        if (second.IsMap() && (key == "vm_config" || key == "vmConfig")) {
-          for (YAML::const_iterator it3 = second.begin(); it3 != second.end(); ++it3) {
-            const YAML::Node vm_config_first  = it3->first;
-            const YAML::Node vm_config_second = it3->second;
+          if (second.IsMap() && (key == "vm_config" || key == "vmConfig")) {
+            for (YAML::const_iterator it3 = second.begin(); it3 != second.end(); ++it3) {
+              const YAML::Node vm_config_first  = it3->first;
+              const YAML::Node vm_config_second = it3->second;
 
-            const std::string &vm_config_key = vm_config_first.as<std::string>();
-            if (vm_config_second.IsScalar()) {
-              const std::string &vm_config_value = vm_config_second.as<std::string>();
-              if (vm_config_key == "runtime") {
-                runtime = vm_config_value;
-              }
-              if (vm_config_key == "vm_id" || vm_config_key == "vmId") {
-                vm_id = vm_config_value;
-              }
-              if (vm_config_key == "configuration") {
-                vm_configuration = vm_config_value;
-              }
-              if (vm_config_key == "allow_precompiled") {
-                if (vm_config_value == "false") {
-                  allow_precompiled = false;
+              const std::string &vm_config_key = vm_config_first.as<std::string>();
+              if (vm_config_second.IsScalar()) {
+                const std::string &vm_config_value = vm_config_second.as<std::string>();
+                if (vm_config_key == "runtime") {
+                  runtime = vm_config_value;
+                }
+                if (vm_config_key == "vm_id" || vm_config_key == "vmId") {
+                  vm_id = vm_config_value;
+                }
+                if (vm_config_key == "configuration") {
+                  vm_configuration = vm_config_value;
+                }
+                if (vm_config_key == "allow_precompiled") {
+                  if (vm_config_value == "false") {
+                    allow_precompiled = false;
+                  }
                 }
               }
-            }
 
-            if (vm_config_key == "environment_variables" && vm_config_second.IsMap()) {
-              if (vm_config_second["host_env_keys"]) {
-                const YAML::Node ek_node = vm_config_second["host_env_keys"];
-                if (ek_node.IsSequence()) {
-                  for (const auto &i : ek_node) {
-                    auto ek = i.as<std::string>();
-                    if (auto *value = std::getenv(ek.data())) {
-                      envs[ek] = value;
+              if (vm_config_key == "environment_variables" && vm_config_second.IsMap()) {
+                if (vm_config_second["host_env_keys"]) {
+                  const YAML::Node ek_node = vm_config_second["host_env_keys"];
+                  if (ek_node.IsSequence()) {
+                    for (const auto &i : ek_node) {
+                      auto ek = i.as<std::string>();
+                      if (auto *value = std::getenv(ek.data())) {
+                        envs[ek] = value;
+                      }
                     }
                   }
                 }
-              }
-              if (vm_config_second["key_values"]) {
-                const YAML::Node kv_node = vm_config_second["key_values"];
-                if (kv_node.IsMap()) {
-                  for (YAML::const_iterator it4 = kv_node.begin(); it4 != kv_node.end(); ++it4) {
-                    envs[it4->first.as<std::string>()] = it4->second.as<std::string>();
+                if (vm_config_second["key_values"]) {
+                  const YAML::Node kv_node = vm_config_second["key_values"];
+                  if (kv_node.IsMap()) {
+                    for (YAML::const_iterator it4 = kv_node.begin(); it4 != kv_node.end(); ++it4) {
+                      envs[it4->first.as<std::string>()] = it4->second.as<std::string>();
+                    }
                   }
                 }
               }
-            }
 
-            if (vm_config_key == "code" && vm_config_second.IsMap()) {
-              if (vm_config_second["local"]) {
-                const YAML::Node local_node = vm_config_second["local"];
-                if (local_node["filename"]) {
-                  wasm_filename = local_node["filename"].as<std::string>();
+              if (vm_config_key == "code" && vm_config_second.IsMap()) {
+                if (vm_config_second["local"]) {
+                  const YAML::Node local_node = vm_config_second["local"];
+                  if (local_node["filename"]) {
+                    wasm_filename = local_node["filename"].as<std::string>();
+                  }
                 }
               }
             }
           }
         }
-      }
 
-      // only allowed one config block (first one) for now
-      break;
+        // only allowed one config block (first one) for now
+        break;
+      }
+    } catch (const YAML::Exception &e) {
+      TSError("[wasm][%s] YAML::Exception %s when parsing YAML config file %s for wasm", __FUNCTION__, e.what(), cfn.c_str());
+      return false;
     }
-  } catch (const YAML::Exception &e) {
-    TSError("[wasm][%s] YAML::Exception %s when parsing YAML config file %s for wasm", __FUNCTION__, e.what(),
-            wasm_config->config_filename.c_str());
-    return false;
-  }
 
-  std::shared_ptr<ats_wasm::Wasm> wasm;
-  if (runtime == "ats.wasm.runtime.wasmedge") {
+    std::shared_ptr<ats_wasm::Wasm> wasm;
+    if (runtime == "ats.wasm.runtime.wasmedge") {
 #ifdef WASMEDGE
-    wasm = std::make_shared<ats_wasm::Wasm>(proxy_wasm::createWasmEdgeVm(), // VM
-                                            vm_id,                          // vm_id
-                                            vm_configuration,               // vm_configuration
-                                            "",                             // vm_key,
-                                            envs,                           // envs
-                                            cap_maps                        // allowed capabilities
-    );
+      wasm = std::make_shared<ats_wasm::Wasm>(proxy_wasm::createWasmEdgeVm(), // VM
+                                              vm_id,                          // vm_id
+                                              vm_configuration,               // vm_configuration
+                                              "",                             // vm_key,
+                                              envs,                           // envs
+                                              cap_maps                        // allowed capabilities
+      );
 #else
-    TSError("[wasm][%s] wasm unable to use WasmEdge runtime", __FUNCTION__);
-    return false;
+      TSError("[wasm][%s] wasm unable to use WasmEdge runtime", __FUNCTION__);
+      return false;
 #endif
-  } else if (runtime == "ats.wasm.runtime.wamr") {
+    } else if (runtime == "ats.wasm.runtime.wamr") {
 #ifdef WAMR
-    wasm = std::make_shared<ats_wasm::Wasm>(proxy_wasm::createWamrVm(), // VM
-                                            vm_id,                      // vm_id
-                                            vm_configuration,           // vm_configuration
-                                            "",                         // vm_key,
-                                            envs,                       // envs
-                                            cap_maps                    // allowed capabilities
-    );
+      wasm = std::make_shared<ats_wasm::Wasm>(proxy_wasm::createWamrVm(), // VM
+                                              vm_id,                      // vm_id
+                                              vm_configuration,           // vm_configuration
+                                              "",                         // vm_key,
+                                              envs,                       // envs
+                                              cap_maps                    // allowed capabilities
+      );
 #else
-    TSError("[wasm][%s] wasm unable to use WAMR runtime", __FUNCTION__);
-    return false;
+      TSError("[wasm][%s] wasm unable to use WAMR runtime", __FUNCTION__);
+      return false;
 #endif
-  } else {
-    TSError("[wasm][%s] wasm unable to use %s runtime", __FUNCTION__, runtime.c_str());
-    return false;
-  }
-  wasm->wasm_vm()->integration() = std::make_unique<ats_wasm::ATSWasmVmIntegration>();
-
-  auto plugin = std::make_shared<proxy_wasm::PluginBase>(name,          // name
-                                                         root_id,       // root_id
-                                                         vm_id,         // vm_id
-                                                         runtime,       // engine
-                                                         configuration, // plugin_configuration
-                                                         fail_open,     // failopen
-                                                         ""             // TODO: plugin key from where ?
-  );
-
-  wasm_config->wasm_filename = wasm_filename;
-  if (*wasm_config->wasm_filename.begin() != '/') {
-    wasm_config->wasm_filename = std::string(TSConfigDirGet()) + "/" + wasm_config->wasm_filename;
-  }
-  std::string code;
-  if (read_file(wasm_config->wasm_filename, &code) < 0) {
-    TSError("[wasm][%s] wasm unable to read file '%s'", __FUNCTION__, wasm_config->wasm_filename.c_str());
-    return false;
-  }
+    } else {
+      TSError("[wasm][%s] wasm unable to use %s runtime", __FUNCTION__, runtime.c_str());
+      return false;
+    }
+    wasm->wasm_vm()->integration() = std::make_unique<ats_wasm::ATSWasmVmIntegration>();
+
+    auto plugin = std::make_shared<proxy_wasm::PluginBase>(name,          // name
+                                                           root_id,       // root_id
+                                                           vm_id,         // vm_id
+                                                           runtime,       // engine
+                                                           configuration, // plugin_configuration
+                                                           fail_open,     // failopen
+                                                           ""             // TODO: plugin key from where ?
+    );
 
-  if (code.empty()) {
-    TSError("[wasm][%s] code is empty", __FUNCTION__);
-    return false;
-  }
+    if (*wasm_filename.begin() != '/') {
+      wasm_filename = std::string(TSConfigDirGet()) + "/" + wasm_filename;
+    }
+    std::string code;
+    if (read_file(wasm_filename, &code) < 0) {
+      TSError("[wasm][%s] wasm unable to read file '%s'", __FUNCTION__, wasm_filename.c_str());
+      return false;
+    }
 
-  if (!wasm) {
-    TSError("[wasm][%s] wasm wasm wasm unable to create vm", __FUNCTION__);
-    return false;
-  }
-  if (!wasm->load(code, allow_precompiled)) {
-    TSError("[wasm][%s] Failed to load Wasm code", __FUNCTION__);
-    return false;
-  }
-  if (!wasm->initialize()) {
-    TSError("[wasm][%s] Failed to initialize Wasm code", __FUNCTION__);
-    return false;
-  }
+    if (code.empty()) {
+      TSError("[wasm][%s] code is empty", __FUNCTION__);
+      return false;
+    }
+
+    if (!wasm) {
+      TSError("[wasm][%s] wasm wasm wasm unable to create vm", __FUNCTION__);
+      return false;
+    }
+    if (!wasm->load(code, allow_precompiled)) {
+      TSError("[wasm][%s] Failed to load Wasm code", __FUNCTION__);
+      return false;
+    }
+    if (!wasm->initialize()) {
+      TSError("[wasm][%s] Failed to initialize Wasm code", __FUNCTION__);
+      return false;
+    }
 
-  TSCont contp      = TSContCreate(schedule_handler, TSMutexCreate());
-  auto *rootContext = wasm->start(plugin, contp);
+    TSCont contp      = TSContCreate(schedule_handler, TSMutexCreate());
+    auto *rootContext = wasm->start(plugin, contp);
 
-  if (!wasm->configure(rootContext, plugin)) {
-    TSError("[wasm][%s] Failed to configure Wasm", __FUNCTION__);
-    return false;
+    if (!wasm->configure(rootContext, plugin)) {
+      TSError("[wasm][%s] Failed to configure Wasm", __FUNCTION__);
+      return false;
+    }
+
+    auto new_config = std::make_pair(wasm, plugin);
+    new_configs.push_front(new_config);
   }
 
-  auto old_wasm   = wasm_config->wasm;
-  auto old_plugin = wasm_config->plugin;
+  auto old_configs = wasm_config->configs;
 
-  wasm_config->wasm   = wasm;
-  wasm_config->plugin = plugin;
+  wasm_config->configs = new_configs;
 
-  if (old_wasm != nullptr) {
-    TSDebug(WASM_DEBUG_TAG, "[%s] previous WasmBase exists", __FUNCTION__);
-    TSMutexLock(old_wasm->mutex());
-    if (old_wasm->readyShutdown()) {
-      TSDebug(WASM_DEBUG_TAG, "[%s] starting WasmBase Shutdown", __FUNCTION__);
-      old_wasm->startShutdown();
-      if (!old_wasm->readyDelete()) {
-        TSDebug(WASM_DEBUG_TAG, "[%s] not ready to delete WasmBase/PluginBase", __FUNCTION__);
+  for (auto it = old_configs.begin(); it != old_configs.end(); it++) {
+    std::shared_ptr<ats_wasm::Wasm> old_wasm           = it->first;
+    std::shared_ptr<proxy_wasm::PluginBase> old_plugin = it->second;
+
+    if (old_wasm != nullptr) {
+      TSDebug(WASM_DEBUG_TAG, "[%s] previous WasmBase exists", __FUNCTION__);
+      TSMutexLock(old_wasm->mutex());
+      if (old_wasm->readyShutdown()) {
+        TSDebug(WASM_DEBUG_TAG, "[%s] starting WasmBase Shutdown", __FUNCTION__);
+        old_wasm->startShutdown();
+        if (!old_wasm->readyDelete()) {
+          TSDebug(WASM_DEBUG_TAG, "[%s] not ready to delete WasmBase/PluginBase", __FUNCTION__);
+          auto deleted_config = std::make_pair(old_wasm, old_plugin);
+          wasm_config->deleted_configs.push_front(deleted_config);
+        }
+      } else {
+        TSDebug(WASM_DEBUG_TAG, "[%s] not ready to shutdown WasmBase", __FUNCTION__);
         auto deleted_config = std::make_pair(old_wasm, old_plugin);
         wasm_config->deleted_configs.push_front(deleted_config);
       }
-    } else {
-      TSDebug(WASM_DEBUG_TAG, "[%s] not ready to shutdown WasmBase", __FUNCTION__);
-      auto deleted_config = std::make_pair(old_wasm, old_plugin);
-      wasm_config->deleted_configs.push_front(deleted_config);
+      TSMutexUnlock(old_wasm->mutex());
     }
-    TSMutexUnlock(old_wasm->mutex());
   }
 
   return true;
@@ -556,11 +598,13 @@ TSPluginInit(int argc, const char *argv[])
 
   wasm_config = std::make_unique<WasmInstanceConfig>();
 
-  std::string filename = std::string(argv[1]);
-  if (*filename.begin() != '/') {
-    filename = std::string(TSConfigDirGet()) + "/" + filename;
+  for (int i = 1; i < argc; i++) {
+    std::string filename = std::string(argv[i]);
+    if (*filename.begin() != '/') {
+      filename = std::string(TSConfigDirGet()) + "/" + filename;
+    }
+    wasm_config->config_filenames.push_front(filename);
   }
-  wasm_config->config_filename = filename;
 
   if (!read_configuration()) {
     return;