You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by zw...@apache.org on 2020/03/06 16:41:10 UTC
[trafficserver] branch 9.0.x updated: Issue #6400 - Adds config
option to enable/disable dynamic reload feature for plugins =>
proxy.config.plugin.dynamic_reload_mode - 1 (default) enables the dynamic
reload feature,
0 disables it => Adds to and refactors unit-tests for the dynamic plugin
reload feature
This is an automated email from the ASF dual-hosted git repository.
zwoop pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/9.0.x by this push:
new c69cdc4 Issue #6400 - Adds config option to enable/disable dynamic reload feature for plugins => proxy.config.plugin.dynamic_reload_mode - 1 (default) enables the dynamic reload feature, 0 disables it => Adds to and refactors unit-tests for the dynamic plugin reload feature
c69cdc4 is described below
commit c69cdc4ea6098a79d734afc59269b8acd1c11a4f
Author: Lokesh Jindal <lj...@linkedin.com>
AuthorDate: Thu Feb 13 17:51:27 2020 -0800
Issue #6400 - Adds config option to enable/disable dynamic reload feature
for plugins
=> proxy.config.plugin.dynamic_reload_mode - 1 (default) enables the
dynamic reload feature, 0 disables it
=> Adds to and refactors unit-tests for the dynamic plugin reload feature
(cherry picked from commit 889e0faab514860a60672bf23e61bb10e2c43f5a)
---
doc/admin-guide/files/records.config.en.rst | 5 +
.../design-documents/reloading-plugins.en.rst | 6 +
mgmt/RecordsConfig.cc | 2 +
proxy/Plugin.cc | 27 +
proxy/Plugin.h | 12 +
proxy/http/remap/PluginDso.cc | 38 +-
proxy/http/remap/PluginDso.h | 6 +-
proxy/http/remap/PluginFactory.cc | 32 +-
proxy/http/remap/PluginFactory.h | 4 +-
proxy/http/remap/RemapConfig.cc | 3 +-
.../http/remap/unit-tests/plugin_testing_common.cc | 21 +
.../http/remap/unit-tests/plugin_testing_common.h | 4 +
proxy/http/remap/unit-tests/test_PluginFactory.cc | 583 ++++++++++++++++++---
src/traffic_server/traffic_server.cc | 2 +
14 files changed, 644 insertions(+), 101 deletions(-)
diff --git a/doc/admin-guide/files/records.config.en.rst b/doc/admin-guide/files/records.config.en.rst
index 9e9e5ed..da61549 100644
--- a/doc/admin-guide/files/records.config.en.rst
+++ b/doc/admin-guide/files/records.config.en.rst
@@ -4042,6 +4042,11 @@ Plug-in Configuration
Specifies the location of |TS| plugins.
+.. ts:cv:: CONFIG proxy.config.plugin.dynamic_reload_mode INT 1
+
+ Enables (``1``) or disables (``0``) the dynamic reload feature for remap
+ plugins (`remap.config`). Global plugins (`plugin.config`) do not have dynamic reload feature yet.
+
SOCKS Processor
===============
diff --git a/doc/developer-guide/design-documents/reloading-plugins.en.rst b/doc/developer-guide/design-documents/reloading-plugins.en.rst
index 7d57341..fb0d2ff 100644
--- a/doc/developer-guide/design-documents/reloading-plugins.en.rst
+++ b/doc/developer-guide/design-documents/reloading-plugins.en.rst
@@ -30,6 +30,8 @@ configuration reload, i.e.::
traffic_ctl config reload
+This feature is enabled by default. It can be turned off by setting the configuration variable :ts:cv:`proxy.config.plugin.dynamic_reload_mode` to ``0`` in :file:`records.config`. When the feature is turned off, once ATS is started one will be able load only one version of a plugin, and re-loading the same plugin would do nothing.
+
Although plugin reloading should be transparent to plugin developers, the following are some design considerations
and implementation details for this feature.
@@ -175,3 +177,7 @@ be reused by 'global' plugins in the future.
To make sure plugins are still loaded while their code is still in use there is reference counting done around ``PluginDso``
which inherits ``RefCountObj`` and implements ``acquire()`` and ``release()`` methods which are called by ``TSCreateCont``,
``TSVConnCreate`` and ``TSDestroyCont``.
+
+Other notes
+-----------
+When this feature for dynamic plugin reload is turned on (:ts:cv:`proxy.config.plugin.dynamic_reload_mode` is set to ``1``), there is one pitfall users should be aware of. Since "global" plugins do not support this feature while the "remap" plugins do, if a plugin is used as a global plugin as well as a remap plugin, there will be two different copies of the plugin loaded in memory with no state shared between them.
diff --git a/mgmt/RecordsConfig.cc b/mgmt/RecordsConfig.cc
index be8b05a..a6bb410 100644
--- a/mgmt/RecordsConfig.cc
+++ b/mgmt/RecordsConfig.cc
@@ -1050,6 +1050,8 @@ static const RecordElement RecordsConfig[] =
,
{RECT_CONFIG, "proxy.config.url_remap.pristine_host_hdr", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
,
+ {RECT_CONFIG, "proxy.config.plugin.dynamic_reload_mode", RECD_INT, "1", RECU_RESTART_TS, RR_NULL, RECC_INT, "[0-1]", RECA_NULL}
+ ,
//##############################################################################
//#
diff --git a/proxy/Plugin.cc b/proxy/Plugin.cc
index 79b3b08..3094743 100644
--- a/proxy/Plugin.cc
+++ b/proxy/Plugin.cc
@@ -34,6 +34,33 @@
#define MAX_PLUGIN_ARGS 64
+static PluginDynamicReloadMode plugin_dynamic_reload_mode = REMAP_PLUGIN_DYNAMIC_RELOAD_ON;
+
+bool
+isPluginDynamicReloadEnabled()
+{
+ return plugin_dynamic_reload_mode == REMAP_PLUGIN_DYNAMIC_RELOAD_ON;
+}
+
+void
+parsePluginDynamicReloadConfig()
+{
+ int int_plugin_dynamic_reload_mode;
+ REC_ReadConfigInteger(int_plugin_dynamic_reload_mode, "proxy.config.plugin.dynamic_reload_mode");
+ plugin_dynamic_reload_mode = (PluginDynamicReloadMode)int_plugin_dynamic_reload_mode;
+ if (plugin_dynamic_reload_mode < PLUGIN_DYNAMIC_RELOAD_MODE_MIN || plugin_dynamic_reload_mode > PLUGIN_DYNAMIC_RELOAD_MODE_MAX) {
+ Warning("proxy.config.plugin.dynamic_reload_mode out of range. using default value.");
+ plugin_dynamic_reload_mode = REMAP_PLUGIN_DYNAMIC_RELOAD_ON;
+ }
+ Note("Initialized plugin_dynamic_reload_mode: %d", plugin_dynamic_reload_mode);
+}
+
+void
+parsePluginConfig()
+{
+ parsePluginDynamicReloadConfig();
+}
+
static const char *plugin_dir = ".";
using init_func_t = void (*)(int, char **);
diff --git a/proxy/Plugin.h b/proxy/Plugin.h
index 8c445a9..8bca364 100644
--- a/proxy/Plugin.h
+++ b/proxy/Plugin.h
@@ -26,6 +26,18 @@
#include <string>
#include "tscore/List.h"
+enum PluginDynamicReloadMode {
+ PLUGIN_DYNAMIC_RELOAD_MODE_MIN = 0,
+ PLUGIN_DYNAMIC_RELOAD_OFF = 0,
+ REMAP_PLUGIN_DYNAMIC_RELOAD_ON = 1,
+ PLUGIN_DYNAMIC_RELOAD_MODE_MAX = 1
+};
+
+// read records.config to parse plugin related configs
+void parsePluginConfig();
+
+bool isPluginDynamicReloadEnabled();
+
struct PluginRegInfo {
PluginRegInfo();
~PluginRegInfo();
diff --git a/proxy/http/remap/PluginDso.cc b/proxy/http/remap/PluginDso.cc
index 7bd4dcf..eb4a895 100644
--- a/proxy/http/remap/PluginDso.cc
+++ b/proxy/http/remap/PluginDso.cc
@@ -41,6 +41,8 @@
PluginDso::PluginDso(const fs::path &configPath, const fs::path &effectivePath, const fs::path &runtimePath)
: _configPath(configPath), _effectivePath(effectivePath), _runtimePath(runtimePath)
{
+ PluginDebug(_tag, "PluginDso (%p) created _configPath: [%s] _effectivePath: [%s] _runtimePath: [%s]", this, _configPath.c_str(),
+ _effectivePath.c_str(), _runtimePath.c_str());
}
PluginDso::~PluginDso()
@@ -71,9 +73,9 @@ PluginDso::load(std::string &error)
} else {
PluginDebug(_tag, "plugin '%s' effective path: %s", _configPath.c_str(), _effectivePath.c_str());
- /* Copy the installed plugin DSO to a runtime directory */
+ /* Copy the installed plugin DSO to a runtime directory if dynamic reload enabled */
std::error_code ec;
- if (!copy(_effectivePath, _runtimePath, ec)) {
+ if (isDynamicReloadEnabled() && !copy(_effectivePath, _runtimePath, ec)) {
std::string temp_error;
temp_error.append("failed to create a copy: ").append(strerror(ec.value()));
error.assign(temp_error);
@@ -216,6 +218,18 @@ PluginDso::modTime() const
}
/**
+ * @brief file handle returned by dlopen syscall
+ *
+ * @return dlopen filehandle
+ */
+
+void *
+PluginDso::dlOpenHandle() const
+{
+ return _dlh;
+}
+
+/**
* @brief clean files created by the plugin instance and handle errors
*
* @param error a human readable error message if something goes wrong
@@ -224,6 +238,10 @@ PluginDso::modTime() const
void
PluginDso::clean(std::string &error)
{
+ if (!isDynamicReloadEnabled()) {
+ return;
+ }
+
if (false == remove(_runtimePath, _errorCode)) {
error.append("failed to remove runtime copy: ").append(_errorCode.message());
}
@@ -266,6 +284,12 @@ PluginDso::instanceCount()
return _instanceCount.refcount();
}
+bool
+PluginDso::isDynamicReloadEnabled() const
+{
+ return (_runtimePath != _effectivePath);
+}
+
void
PluginDso::LoadedPlugins::add(PluginDso *plugin)
{
@@ -283,8 +307,13 @@ PluginDso::LoadedPlugins::remove(PluginDso *plugin)
this_ethread()->schedule_imm(new DeleterContinuation<PluginDso>(plugin));
}
+/* check if need to reload the plugin DSO
+ * if dynamic reload not enabled: check if plugin Dso with same effective path aready loaded
+ * if dynamic reload enabled: check if plugin Dso with same effective path and same time stamp already loaded
+ * return pointer to already loaded plugin if found, else return null
+ */
PluginDso *
-PluginDso::LoadedPlugins::findByEffectivePath(const fs::path &path)
+PluginDso::LoadedPlugins::findByEffectivePath(const fs::path &path, bool dynamicReloadEnabled)
{
struct stat sb;
time_t mtime = 0;
@@ -295,7 +324,8 @@ PluginDso::LoadedPlugins::findByEffectivePath(const fs::path &path)
SCOPED_MUTEX_LOCK(lock, _mutex, this_ethread());
auto spot = std::find_if(_list.begin(), _list.end(), [&](PluginDso const &plugin) -> bool {
- return (0 == path.string().compare(plugin.effectivePath().string()) && (mtime == plugin.modTime()));
+ return ((!dynamicReloadEnabled || (mtime == plugin.modTime())) &&
+ (0 == path.string().compare(plugin.effectivePath().string())));
});
return spot == _list.end() ? nullptr : static_cast<PluginDso *>(spot);
}
diff --git a/proxy/http/remap/PluginDso.h b/proxy/http/remap/PluginDso.h
index 47fc080..be43803 100644
--- a/proxy/http/remap/PluginDso.h
+++ b/proxy/http/remap/PluginDso.h
@@ -43,6 +43,8 @@ namespace fs = ts::file;
#include "I_EventSystem.h"
#include "tscpp/util/IntrusiveDList.h"
+#include "Plugin.h"
+
class PluginThreadContext : public RefCountObj
{
public:
@@ -69,6 +71,7 @@ public:
const fs::path &effectivePath() const;
const fs::path &runtimePath() const;
time_t modTime() const;
+ void *dlOpenHandle() const;
/* List used by the plugin factory */
using self_type = PluginDso; ///< Self reference type.
@@ -89,6 +92,7 @@ public:
void incInstanceCount();
void decInstanceCount();
int instanceCount();
+ bool isDynamicReloadEnabled() const;
class LoadedPlugins : public RefCountObj
{
@@ -96,7 +100,7 @@ public:
LoadedPlugins() : _mutex(new_ProxyMutex()) {}
void add(PluginDso *plugin);
void remove(PluginDso *plugin);
- PluginDso *findByEffectivePath(const fs::path &path);
+ PluginDso *findByEffectivePath(const fs::path &path, bool dynamicReloadEnabled);
void indicatePreReload(const char *factoryId);
void indicatePostReload(bool reloadSuccessful, const std::unordered_map<PluginDso *, int> &pluginUsed, const char *factoryId);
diff --git a/proxy/http/remap/PluginFactory.cc b/proxy/http/remap/PluginFactory.cc
index 6773384..0cb5dfb 100644
--- a/proxy/http/remap/PluginFactory.cc
+++ b/proxy/http/remap/PluginFactory.cc
@@ -141,7 +141,7 @@ PluginFactory::getUuid()
* @return pointer to a plugin instance, nullptr if failure
*/
RemapPluginInst *
-PluginFactory::getRemapPlugin(const fs::path &configPath, int argc, char **argv, std::string &error)
+PluginFactory::getRemapPlugin(const fs::path &configPath, int argc, char **argv, std::string &error, bool dynamicReloadEnabled)
{
/* Discover the effective path by looking into the search dirs */
fs::path effectivePath = getEffectivePath(configPath);
@@ -153,7 +153,7 @@ PluginFactory::getRemapPlugin(const fs::path &configPath, int argc, char **argv,
}
/* Only one plugin with this effective path can be loaded by a plugin factory */
- RemapPluginInfo *plugin = dynamic_cast<RemapPluginInfo *>(findByEffectivePath(effectivePath));
+ RemapPluginInfo *plugin = dynamic_cast<RemapPluginInfo *>(findByEffectivePath(effectivePath, dynamicReloadEnabled));
RemapPluginInst *inst = nullptr;
if (nullptr == plugin) {
@@ -161,13 +161,23 @@ PluginFactory::getRemapPlugin(const fs::path &configPath, int argc, char **argv,
PluginDebug(_tag, "plugin '%s' has not been loaded yet, loading as remap plugin", configPath.c_str());
fs::path runtimePath;
- runtimePath /= _runtimeDir;
- runtimePath /= effectivePath.relative_path();
- fs::path parent = runtimePath.parent_path();
- if (!fs::create_directories(parent, _ec)) {
- error.assign("failed to create plugin runtime dir");
- return nullptr;
+ // if dynamic reload enabled then create a temporary location to copy .so and load from there
+ // else load from original location
+ if (dynamicReloadEnabled) {
+ runtimePath /= _runtimeDir;
+ runtimePath /= effectivePath.relative_path();
+
+ fs::path parent = runtimePath.parent_path();
+ PluginDebug(_tag, "Using effectivePath: [%s] runtimePath: [%s] parent: [%s]", effectivePath.c_str(), runtimePath.c_str(),
+ parent.c_str());
+ if (!fs::create_directories(parent, _ec)) {
+ error.assign("failed to create plugin runtime dir");
+ return nullptr;
+ }
+ } else {
+ runtimePath = effectivePath;
+ PluginDebug(_tag, "Using effectivePath: [%s] runtimePath: [%s]", effectivePath.c_str(), runtimePath.c_str());
}
plugin = new RemapPluginInfo(configPath, effectivePath, runtimePath);
@@ -187,7 +197,7 @@ PluginFactory::getRemapPlugin(const fs::path &configPath, int argc, char **argv,
delete plugin;
}
- if (_preventiveCleaning) {
+ if (dynamicReloadEnabled && _preventiveCleaning) {
clean(error);
}
} else {
@@ -244,9 +254,9 @@ PluginFactory::getEffectivePath(const fs::path &configPath)
* @return plugin found or nullptr if not found
*/
PluginDso *
-PluginFactory::findByEffectivePath(const fs::path &path)
+PluginFactory::findByEffectivePath(const fs::path &path, bool dynamicReloadEnabled)
{
- return PluginDso::loadedPlugins()->findByEffectivePath(path);
+ return PluginDso::loadedPlugins()->findByEffectivePath(path, dynamicReloadEnabled);
}
/**
diff --git a/proxy/http/remap/PluginFactory.h b/proxy/http/remap/PluginFactory.h
index 3c2902c..4f6ee6d 100644
--- a/proxy/http/remap/PluginFactory.h
+++ b/proxy/http/remap/PluginFactory.h
@@ -94,7 +94,7 @@ public:
PluginFactory &setRuntimeDir(const fs::path &runtimeDir);
PluginFactory &addSearchDir(const fs::path &searchDir);
- RemapPluginInst *getRemapPlugin(const fs::path &configPath, int argc, char **argv, std::string &error);
+ RemapPluginInst *getRemapPlugin(const fs::path &configPath, int argc, char **argv, std::string &error, bool dynamicReloadEnabled);
virtual const char *getUuid();
void clean(std::string &error);
@@ -104,7 +104,7 @@ public:
void indicatePostReload(bool reloadSuccessful);
protected:
- PluginDso *findByEffectivePath(const fs::path &path);
+ PluginDso *findByEffectivePath(const fs::path &path, bool dynamicReloadEnabled);
fs::path getEffectivePath(const fs::path &configPath);
std::vector<fs::path> _searchDirs; /** @brief ordered list of search paths where we look for plugins */
diff --git a/proxy/http/remap/RemapConfig.cc b/proxy/http/remap/RemapConfig.cc
index 985aee7..e14e869 100644
--- a/proxy/http/remap/RemapConfig.cc
+++ b/proxy/http/remap/RemapConfig.cc
@@ -817,7 +817,8 @@ remap_load_plugin(const char **argv, int argc, url_mapping *mp, char *errbuf, in
REC_ReadConfigInteger(elevate_access, "proxy.config.plugin.load_elevated");
ElevateAccess access(elevate_access ? ElevateAccess::FILE_PRIVILEGE : 0);
- pi = rewrite->pluginFactory.getRemapPlugin(ts::file::path(const_cast<const char *>(c)), parc, pargv, error);
+ pi = rewrite->pluginFactory.getRemapPlugin(ts::file::path(const_cast<const char *>(c)), parc, pargv, error,
+ isPluginDynamicReloadEnabled());
} // done elevating access
bool result = true;
diff --git a/proxy/http/remap/unit-tests/plugin_testing_common.cc b/proxy/http/remap/unit-tests/plugin_testing_common.cc
index a54422c..c2d3295 100644
--- a/proxy/http/remap/unit-tests/plugin_testing_common.cc
+++ b/proxy/http/remap/unit-tests/plugin_testing_common.cc
@@ -50,3 +50,24 @@ getTemporaryDir()
return fs::path(mkdtemp(dirNameTemplate));
}
+
+// implement functions to support unit-testing of option to enable/disable dynamic reload of plugins
+static PluginDynamicReloadMode plugin_dynamic_reload_mode = REMAP_PLUGIN_DYNAMIC_RELOAD_ON;
+
+bool
+isPluginDynamicReloadEnabled()
+{
+ return plugin_dynamic_reload_mode == REMAP_PLUGIN_DYNAMIC_RELOAD_ON;
+}
+
+void
+enablePluginDynamicReload()
+{
+ plugin_dynamic_reload_mode = REMAP_PLUGIN_DYNAMIC_RELOAD_ON;
+}
+
+void
+disablePluginDynamicReload()
+{
+ plugin_dynamic_reload_mode = PLUGIN_DYNAMIC_RELOAD_OFF;
+}
diff --git a/proxy/http/remap/unit-tests/plugin_testing_common.h b/proxy/http/remap/unit-tests/plugin_testing_common.h
index fd01dd0..1669a30 100644
--- a/proxy/http/remap/unit-tests/plugin_testing_common.h
+++ b/proxy/http/remap/unit-tests/plugin_testing_common.h
@@ -101,3 +101,7 @@ void PrintToStdErr(const char *fmt, ...);
#ifdef __cplusplus
}
#endif /* __cplusplus */
+
+// functions to support unit-testing of option to enable/disable dynamic reload of plugins
+void enablePluginDynamicReload();
+void disablePluginDynamicReload();
\ No newline at end of file
diff --git a/proxy/http/remap/unit-tests/test_PluginFactory.cc b/proxy/http/remap/unit-tests/test_PluginFactory.cc
index de80397..faa7e6f 100644
--- a/proxy/http/remap/unit-tests/test_PluginFactory.cc
+++ b/proxy/http/remap/unit-tests/test_PluginFactory.cc
@@ -114,6 +114,78 @@ clean()
fs::remove(sandboxDir, ec);
}
+static int
+getPluginVersion(const PluginDso &plugin)
+{
+ std::string error;
+ void *s = nullptr;
+ CHECK(plugin.getSymbol("pluginDsoVersionTest", s, error));
+ int (*version)() = reinterpret_cast<int (*)()>(s);
+ return version ? version() : -1;
+}
+
+// this is a simple class to simulate loading of Plugin Dso as done during loading of global plugins
+class GlobalPluginInfo
+{
+public:
+ GlobalPluginInfo() : _dlh(nullptr){};
+ ~GlobalPluginInfo(){};
+
+ bool
+ loadDso(const fs::path &configPath)
+ {
+ CHECK(fs::exists(configPath));
+
+ void *handle = dlopen(configPath.c_str(), RTLD_NOW);
+ if (!handle) {
+ return false;
+ }
+
+ _dlh = handle;
+ return true;
+ }
+
+ bool
+ getSymbol(const char *symbol, void *&address, std::string &error) const
+ {
+ /* Clear the errors */
+ dlerror();
+ error.clear();
+
+ address = dlsym(_dlh, symbol);
+ char *err = dlerror();
+
+ if (nullptr == address && nullptr != err) {
+ /* symbol really cannot be found */
+ error.assign(err);
+ return false;
+ }
+
+ return true;
+ }
+
+ void *
+ dlOpenHandle()
+ {
+ return _dlh;
+ }
+
+ int
+ getPluginVersion()
+ {
+ std::string error;
+ void *s = nullptr;
+ CHECK(getSymbol("pluginDsoVersionTest", s, error));
+ auto version = reinterpret_cast<int (*)()>(s);
+ return version ? version() : -1;
+ }
+
+private:
+ void *_dlh;
+};
+
+// copies the .so file (pluginBuildPath) in the target directory (effectivePath)
+// as the desired .so filename (configPath) with the desired timestamp (mtime)
static void
setupConfigPathTest(const fs::path &configPath, const fs::path &pluginBuildPath, const fs::path &uuid, fs::path &effectivePath,
fs::path &runtimePath, time_t mtime = 0, bool append = false)
@@ -124,7 +196,11 @@ setupConfigPathTest(const fs::path &configPath, const fs::path &pluginBuildPath,
}
effectivePath = configPath.is_absolute() ? configPath : searchDir / configPath;
- runtimePath = runtimeRootDir / uuid / effectivePath.relative_path();
+ if (isPluginDynamicReloadEnabled()) {
+ runtimePath = runtimeRootDir / uuid / effectivePath.relative_path();
+ } else {
+ runtimePath = effectivePath;
+ }
/* Create the directory structure and install plugins */
fs::create_directories(effectivePath.parent_path(), ec);
@@ -175,6 +251,7 @@ SCENARIO("loading plugins", "[plugin][core]")
fs::path effectivePath;
fs::path runtimePath;
std::string error;
+ enablePluginDynamicReload();
GIVEN("an existing plugin")
{
@@ -188,12 +265,12 @@ SCENARIO("loading plugins", "[plugin][core]")
setupConfigPathTest(configPath, buildPath, tempComponent, effectivePath, runtimePath);
PluginFactoryUnitTest *factory = getFactory(tempComponent);
- RemapPluginInst *plugin = factory->getRemapPlugin(configPath, 0, nullptr, error);
+ RemapPluginInst *plugin = factory->getRemapPlugin(configPath, 0, nullptr, error, isPluginDynamicReloadEnabled());
THEN("expect it to successfully load")
{
validateSuccessfulConfigPathTest(plugin, error, effectivePath, runtimePath);
- CHECK(nullptr != PluginDso::loadedPlugins()->findByEffectivePath(effectivePath));
+ CHECK(nullptr != PluginDso::loadedPlugins()->findByEffectivePath(effectivePath, isPluginDynamicReloadEnabled()));
}
teardownConfigPathTest(factory);
@@ -206,35 +283,63 @@ SCENARIO("loading plugins", "[plugin][core]")
setupConfigPathTest(configPath, buildPath, tempComponent, effectivePath, runtimePath);
PluginFactoryUnitTest *factory = getFactory(tempComponent);
- RemapPluginInst *plugin = factory->getRemapPlugin(configPath, 0, nullptr, error);
+ RemapPluginInst *plugin = factory->getRemapPlugin(configPath, 0, nullptr, error, isPluginDynamicReloadEnabled());
THEN("expect it to successfully load")
{
validateSuccessfulConfigPathTest(plugin, error, effectivePath, runtimePath);
- CHECK(nullptr != PluginDso::loadedPlugins()->findByEffectivePath(effectivePath));
+ CHECK(nullptr != PluginDso::loadedPlugins()->findByEffectivePath(effectivePath, isPluginDynamicReloadEnabled()));
}
teardownConfigPathTest(factory);
}
- WHEN("config is using plugin absolute path")
+ WHEN("config is using plugin absolute path - dynamic reload is ENABLED")
{
fs::path configPath = searchDir / "subdir" / pluginName;
CHECK(configPath.is_absolute()); /* make sure this is absolute path - this is what we are testing */
setupConfigPathTest(configPath, buildPath, tempComponent, effectivePath, runtimePath);
PluginFactoryUnitTest *factory = getFactory(tempComponent);
- RemapPluginInst *plugin = factory->getRemapPlugin(configPath, 0, nullptr, error);
+ RemapPluginInst *plugin = factory->getRemapPlugin(configPath, 0, nullptr, error, isPluginDynamicReloadEnabled());
THEN("expect it to successfully load")
{
validateSuccessfulConfigPathTest(plugin, error, effectivePath, runtimePath);
- CHECK(nullptr != PluginDso::loadedPlugins()->findByEffectivePath(effectivePath));
+ CHECK(nullptr != PluginDso::loadedPlugins()->findByEffectivePath(effectivePath, isPluginDynamicReloadEnabled()));
+
+ // check Dso at effective path still exists while copy at runtime path doesn't
+ CHECK(fs::exists(plugin->_plugin.effectivePath()));
+ CHECK(!fs::exists(plugin->_plugin.runtimePath()));
}
teardownConfigPathTest(factory);
}
+ WHEN("config is using plugin absolute path - dynamic reload is DISABLED")
+ {
+ disablePluginDynamicReload();
+ fs::path configPath = searchDir / "subdir" / pluginName;
+ CHECK(configPath.is_absolute()); /* make sure this is absolute path - this is what we are testing */
+
+ setupConfigPathTest(configPath, buildPath, tempComponent, effectivePath, runtimePath);
+ PluginFactoryUnitTest *factory = getFactory(tempComponent);
+ RemapPluginInst *plugin = factory->getRemapPlugin(configPath, 0, nullptr, error, isPluginDynamicReloadEnabled());
+
+ THEN("expect it to successfully load")
+ {
+ validateSuccessfulConfigPathTest(plugin, error, effectivePath, runtimePath);
+ CHECK(nullptr != PluginDso::loadedPlugins()->findByEffectivePath(effectivePath, isPluginDynamicReloadEnabled()));
+
+ // check Dso still exists
+ CHECK(plugin->_plugin.effectivePath() == plugin->_plugin.runtimePath());
+ CHECK(fs::exists(plugin->_plugin.effectivePath()));
+ }
+
+ teardownConfigPathTest(factory);
+ enablePluginDynamicReload();
+ }
+
WHEN("config using nonexisting relative plugin file name")
{
fs::path relativeExistingPath = pluginName;
@@ -245,7 +350,7 @@ SCENARIO("loading plugins", "[plugin][core]")
setupConfigPathTest(relativeExistingPath, buildPath, tempComponent, effectivePath, runtimePath);
PluginFactoryUnitTest *factory = getFactory(tempComponent);
- RemapPluginInst *plugin = factory->getRemapPlugin(relativeNonexistingPath, 0, nullptr, error);
+ RemapPluginInst *plugin = factory->getRemapPlugin(relativeNonexistingPath, 0, nullptr, error, isPluginDynamicReloadEnabled());
THEN("expect it to fail with appropriate error message")
{
@@ -267,7 +372,7 @@ SCENARIO("loading plugins", "[plugin][core]")
setupConfigPathTest(relativeExistingPath, buildPath, tempComponent, effectivePath, runtimePath);
PluginFactoryUnitTest *factory = getFactory(tempComponent);
- RemapPluginInst *plugin = factory->getRemapPlugin(absoluteNonexistingPath, 0, nullptr, error);
+ RemapPluginInst *plugin = factory->getRemapPlugin(absoluteNonexistingPath, 0, nullptr, error, isPluginDynamicReloadEnabled());
THEN("expect it to fail with appropriate error message")
{
@@ -286,12 +391,12 @@ SCENARIO("loading plugins", "[plugin][core]")
fs::path buildPath = pluginBuildDir / configPath;
setupConfigPathTest(configPath, buildPath, tempComponent, effectivePath, runtimePath);
PluginFactoryUnitTest *factory = getFactory(tempComponent);
- RemapPluginInst *plugin = factory->getRemapPlugin(configPath, 0, nullptr, error);
+ RemapPluginInst *plugin = factory->getRemapPlugin(configPath, 0, nullptr, error, isPluginDynamicReloadEnabled());
THEN("expect it to unload the plugin dso")
{
CHECK(nullptr == plugin);
- CHECK(nullptr == PluginDso::loadedPlugins()->findByEffectivePath(effectivePath));
+ CHECK(nullptr == PluginDso::loadedPlugins()->findByEffectivePath(effectivePath, isPluginDynamicReloadEnabled()));
}
teardownConfigPathTest(factory);
@@ -303,12 +408,12 @@ SCENARIO("loading plugins", "[plugin][core]")
fs::path buildPath = pluginBuildDir / configPath;
setupConfigPathTest(configPath, buildPath, tempComponent, effectivePath, runtimePath);
PluginFactoryUnitTest *factory = getFactory(tempComponent);
- RemapPluginInst *plugin = factory->getRemapPlugin(configPath, 0, nullptr, error);
+ RemapPluginInst *plugin = factory->getRemapPlugin(configPath, 0, nullptr, error, isPluginDynamicReloadEnabled());
THEN("expect it to unload the plugin dso")
{
CHECK(nullptr == plugin);
- CHECK(nullptr == PluginDso::loadedPlugins()->findByEffectivePath(effectivePath));
+ CHECK(nullptr == PluginDso::loadedPlugins()->findByEffectivePath(effectivePath, isPluginDynamicReloadEnabled()));
}
teardownConfigPathTest(factory);
@@ -319,6 +424,7 @@ SCENARIO("loading plugins", "[plugin][core]")
SCENARIO("multiple search dirs + multiple or no plugins installed", "[plugin][core]")
{
REQUIRE_FALSE(sandboxDir.empty());
+ enablePluginDynamicReload();
GIVEN("multiple search dirs specified for the plugin search")
{
@@ -368,7 +474,7 @@ SCENARIO("multiple search dirs + multiple or no plugins installed", "[plugin][co
CHECK(fs::exists(abEffectivePath));
/* Now use an absolute path containing the unregistered search directory */
- RemapPluginInst *pluginInst = factory.getRemapPlugin(abEffectivePath, 0, nullptr, error);
+ RemapPluginInst *pluginInst = factory.getRemapPlugin(abEffectivePath, 0, nullptr, error, isPluginDynamicReloadEnabled());
THEN("Expect it to successfully load")
{
@@ -382,7 +488,7 @@ SCENARIO("multiple search dirs + multiple or no plugins installed", "[plugin][co
WHEN("a valid plugin is found in the first search path")
{
- RemapPluginInst *pluginInst = factory.getRemapPlugin(configPath, 0, nullptr, error);
+ RemapPluginInst *pluginInst = factory.getRemapPlugin(configPath, 0, nullptr, error, isPluginDynamicReloadEnabled());
THEN("Expect it to successfully load the one found in the first search dir and copy it in the runtime dir")
{
@@ -397,7 +503,7 @@ SCENARIO("multiple search dirs + multiple or no plugins installed", "[plugin][co
WHEN("the first search dir is missing the plugin but the second search has it")
{
CHECK(fs::remove(effectivePath1, ec));
- RemapPluginInst *pluginInst = factory.getRemapPlugin(configPath, 0, nullptr, error);
+ RemapPluginInst *pluginInst = factory.getRemapPlugin(configPath, 0, nullptr, error, isPluginDynamicReloadEnabled());
THEN("Expect it to successfully load the one found in the second search dir")
{
@@ -413,7 +519,7 @@ SCENARIO("multiple search dirs + multiple or no plugins installed", "[plugin][co
{
CHECK(fs::remove(effectivePath1, ec));
CHECK(fs::remove(effectivePath2, ec));
- RemapPluginInst *pluginInst = factory.getRemapPlugin(configPath, 0, nullptr, error);
+ RemapPluginInst *pluginInst = factory.getRemapPlugin(configPath, 0, nullptr, error, isPluginDynamicReloadEnabled());
THEN("Expect it to successfully load the one found in the third search dir")
{
@@ -433,7 +539,7 @@ SCENARIO("multiple search dirs + multiple or no plugins installed", "[plugin][co
THEN("expect the plugin load to fail.")
{
- RemapPluginInst *pluginInst = factory.getRemapPlugin(configPath, 0, nullptr, error);
+ RemapPluginInst *pluginInst = factory.getRemapPlugin(configPath, 0, nullptr, error, isPluginDynamicReloadEnabled());
CHECK(nullptr == pluginInst);
CHECK(std::string("failed to find plugin '").append(configPath.string()).append("'") == error);
CHECK_FALSE(fs::exists(runtimePath1));
@@ -445,30 +551,103 @@ SCENARIO("multiple search dirs + multiple or no plugins installed", "[plugin][co
}
}
-static int
-getPluginVersion(const PluginDso &plugin)
+void
+checkTwoLoadedVersionsDifferent(const RemapPluginInst *plugin_v1, const RemapPluginInst *plugin_v2)
+{
+ void *tsRemapInitSym_v1_t2 = nullptr; /* callback address from DSO v1 at moment t2 */
+ void *tsRemapInitSym_v2_t2 = nullptr; /* callback address from DSO v2 at moment t2 */
+ std::string error;
+
+ /* Make sure we ended up with different DSO objects and runtime paths are different - new plugin was indeed loaded */
+ CHECK(&(plugin_v1->_plugin) != &(plugin_v2->_plugin));
+ CHECK(plugin_v1->_plugin.runtimePath() != plugin_v2->_plugin.runtimePath());
+ CHECK(plugin_v1->_plugin.dlOpenHandle() != plugin_v2->_plugin.dlOpenHandle());
+
+ /* Make sure what we installed and loaded first was v1 and after the plugin reload we run v2 */
+ CHECK(1 == getPluginVersion(plugin_v1->_plugin));
+ CHECK(2 == getPluginVersion(plugin_v2->_plugin));
+
+ /* Make sure the symbols we get from the 2 loaded plugins don't yield the same callback function pointer */
+ plugin_v1->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v1_t2, error);
+ plugin_v2->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v2_t2, error);
+ CHECK(nullptr != tsRemapInitSym_v1_t2);
+ CHECK(nullptr != tsRemapInitSym_v2_t2);
+ CHECK(tsRemapInitSym_v1_t2 != tsRemapInitSym_v2_t2);
+
+ // 2 versions can be different only when dynamic reload enabled
+ CHECK(isPluginDynamicReloadEnabled());
+
+ // check Dso at effective path still exists while the copy at runtime path doesn't
+ CHECK(plugin_v1->_plugin.effectivePath() != plugin_v1->_plugin.runtimePath());
+ CHECK(fs::exists(plugin_v1->_plugin.effectivePath()));
+ CHECK(!fs::exists(plugin_v1->_plugin.runtimePath()));
+ CHECK(plugin_v2->_plugin.effectivePath() != plugin_v2->_plugin.runtimePath());
+ CHECK(fs::exists(plugin_v2->_plugin.effectivePath()));
+ CHECK(!fs::exists(plugin_v2->_plugin.runtimePath()));
+}
+
+void
+checkTwoLoadedVersionsSame(RemapPluginInst *plugin_v1, RemapPluginInst *plugin_v2)
{
+ void *tsRemapInitSym_v1_t2 = nullptr; /* callback address from DSO v1 at moment t2 */
+ void *tsRemapInitSym_v2_t2 = nullptr; /* callback address from DSO v2 at moment t2 */
std::string error;
- void *s = nullptr;
- CHECK(plugin.getSymbol("pluginDsoVersionTest", s, error));
- int (*version)() = reinterpret_cast<int (*)()>(s);
- return version ? version() : -1;
+
+ /* Make sure we ended up with the same DSO object and runtime paths should be same - no new plugin was loaded */
+ CHECK(&(plugin_v1->_plugin) == &(plugin_v2->_plugin));
+ CHECK(plugin_v1->_plugin.runtimePath() == plugin_v2->_plugin.runtimePath());
+ CHECK(plugin_v1->_plugin.dlOpenHandle() == plugin_v2->_plugin.dlOpenHandle());
+
+ /* Make sure v2 DSO was NOT really loaded - both instances should return same v1 version */
+ CHECK(1 == getPluginVersion(plugin_v1->_plugin));
+ CHECK(1 == getPluginVersion(plugin_v2->_plugin));
+
+ /* Make sure the symbols we get from the 2 loaded plugins yield the same callback function pointer */
+ plugin_v1->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v1_t2, error);
+ plugin_v2->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v2_t2, error);
+ CHECK(nullptr != tsRemapInitSym_v1_t2);
+ CHECK(nullptr != tsRemapInitSym_v2_t2);
+ CHECK(tsRemapInitSym_v1_t2 == tsRemapInitSym_v2_t2);
+
+ // 2 versions can be same even when dynamic reload is enabled
+ // 2 versions must be same when dynamic reload is disabled
+ // check presence/absence of Dso files
+ if (!isPluginDynamicReloadEnabled()) {
+ CHECK(plugin_v1->_plugin.effectivePath() == plugin_v1->_plugin.runtimePath());
+ CHECK(fs::exists(plugin_v1->_plugin.effectivePath()));
+ } else {
+ CHECK(plugin_v1->_plugin.effectivePath() != plugin_v1->_plugin.runtimePath());
+ CHECK(fs::exists(plugin_v1->_plugin.effectivePath()));
+ CHECK(!fs::exists(plugin_v1->_plugin.runtimePath()));
+ }
+}
+
+std::tuple<RemapPluginInst *, PluginFactoryUnitTest *>
+testSetupLoadPlugin(const fs::path &configName, const fs::path &buildPath, const fs::path &uuid, const time_t mtime,
+ fs::path &effectivePath, fs::path &runtimePath)
+{
+ std::string error;
+ setupConfigPathTest(configName, buildPath, uuid, effectivePath, runtimePath, mtime);
+ auto factory = getFactory(uuid);
+ auto pluginInst = factory->getRemapPlugin(configName, 0, nullptr, error, isPluginDynamicReloadEnabled());
+
+ return {pluginInst, factory};
}
SCENARIO("loading multiple version of the same plugin at the same time", "[plugin][core]")
{
REQUIRE_FALSE(sandboxDir.empty());
+ enablePluginDynamicReload();
- static fs::path uuid_t1 = fs::path("c71e2bab-90dc-4770-9535-c9304c3de381"); /* UUID at moment t1 */
- static fs::path uuid_t2 = fs::path("c71e2bab-90dc-4770-9535-e7304c3ee732"); /* UUID at moment t2 */
+ static fs::path uuid_t1 = fs::path("c71e2bab-90dc-4770-9535-c9304c3de381"); /* UUID at moment t1 */
+ static fs::path uuid_t2 = fs::path("c71e2bab-90dc-4770-9535-e7304c3ee732"); /* UUID at moment t2 */
+ void *tsRemapInitSym_v1_t1 = nullptr; /* callback address from DSO v1 at moment t1 */
+ void *tsRemapInitSym_v1_t2 = nullptr; /* callback address from DSO v1 at moment t2 */
- fs::path effectivePath_v1; /* expected effective path for DSO v1 */
- fs::path effectivePath_v2; /* expected effective path for DSO v2 */
- fs::path runtimePath_v1; /* expected runtime path for DSO v1 */
- fs::path runtimePath_v2; /* expected runtime path for DSO v2 */
- void *tsRemapInitSym_v1_t1 = nullptr; /* callback address from DSO v1 at moment t1 */
- void *tsRemapInitSym_v1_t2 = nullptr; /* callback address from DSO v1 at moment t2 */
- void *tsRemapInitSym_v2_t2 = nullptr; /* callback address from DSO v2 at moment t2 */
+ fs::path effectivePath_v1; /* expected effective path for DSO v1 */
+ fs::path effectivePath_v2; /* expected effective path for DSO v2 */
+ fs::path runtimePath_v1; /* expected runtime path for DSO v1 */
+ fs::path runtimePath_v2; /* expected runtime path for DSO v2 */
std::string error;
std::string error1;
@@ -478,26 +657,26 @@ SCENARIO("loading multiple version of the same plugin at the same time", "[plugi
fs::path buildPath_v1 = pluginBuildDir / fs::path("plugin_v1.so"); /* DSO v1 */
fs::path buildPath_v2 = pluginBuildDir / fs::path("plugin_v2.so"); /* DSO v1 */
- GIVEN("two different versions v1 and v2 of same plugin")
+ GIVEN("two different versions v1 and v2 of same plugin with different time stamps - dynamic plugin reload ENABLED")
{
WHEN("(1) loading v1, (2) overwriting with v2 and then (3) reloading by using the same plugin name, "
"(*) v1 and v2 DSOs modification time are different (changed)")
{
+ enablePluginDynamicReload();
+
/* Simulate installing plugin plugin_v1.so (ver 1) as plugin.so and loading it at some point of time t1 */
- setupConfigPathTest(configName, buildPath_v1, uuid_t1, effectivePath_v1, runtimePath_v1, 1556825556);
- PluginFactoryUnitTest *factory1 = getFactory(uuid_t1);
- RemapPluginInst *plugin_v1 = factory1->getRemapPlugin(configName, 0, nullptr, error1);
+ auto [plugin_v1, factory1] =
+ testSetupLoadPlugin(configName, buildPath_v1, uuid_t1, 1556825556, effectivePath_v1, runtimePath_v1);
plugin_v1->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v1_t1, error);
/* Simulate installing plugin plugin_v2.so (v1) as plugin.so and loading it at some point of time t2 */
/* Note that during the installation plugin_v2.so (v2) is "barberically" overriding the existing plugin.so which was v1 */
- setupConfigPathTest(configName, buildPath_v2, uuid_t2, effectivePath_v2, runtimePath_v2, 1556825557);
- PluginFactoryUnitTest *factory2 = getFactory(uuid_t2);
- RemapPluginInst *plugin_v2 = factory2->getRemapPlugin(configName, 0, nullptr, error2);
+ auto [plugin_v2, factory2] =
+ testSetupLoadPlugin(configName, buildPath_v2, uuid_t2, 1556825557, effectivePath_v2, runtimePath_v2);
+ plugin_v1->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v1_t2, error);
/* Make sure plugin.so was overriden */
CHECK(effectivePath_v1 == effectivePath_v2);
-
/* Although effective path is the same runtime paths should be different */
CHECK(runtimePath_v1 != runtimePath_v2);
@@ -507,16 +686,7 @@ SCENARIO("loading multiple version of the same plugin at the same time", "[plugi
validateSuccessfulConfigPathTest(plugin_v1, error1, effectivePath_v1, runtimePath_v1);
validateSuccessfulConfigPathTest(plugin_v2, error2, effectivePath_v2, runtimePath_v2);
- /* Make sure what we installed and loaded first was v1 and after the plugin reload we run v2 */
- CHECK(1 == getPluginVersion(plugin_v1->_plugin));
- CHECK(2 == getPluginVersion(plugin_v2->_plugin));
-
- /* Make sure the symbols we get from the 2 loaded plugins don't yield the same callback function pointer */
- plugin_v1->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v1_t2, error);
- plugin_v2->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v2_t2, error);
- CHECK(nullptr != tsRemapInitSym_v1_t2);
- CHECK(nullptr != tsRemapInitSym_v2_t2);
- CHECK(tsRemapInitSym_v1_t2 != tsRemapInitSym_v2_t2);
+ checkTwoLoadedVersionsDifferent(plugin_v1, plugin_v2);
/* Make sure v1 callback functions addresses did not change for v1 after v2 was loaded */
CHECK(tsRemapInitSym_v1_t1 == tsRemapInitSym_v1_t2);
@@ -527,23 +697,25 @@ SCENARIO("loading multiple version of the same plugin at the same time", "[plugi
}
}
- GIVEN("two different versions v1 and v2 of same plugin")
+ GIVEN("two different versions v1 and v2 of same plugin with same time stamps - dynamic plugin reload ENABLED")
{
WHEN("(1) loading v1, (2) overwriting with v2 and then (3) reloading by using the same plugin name, "
"(*) v1 and v2 DSOs modification time are same (did NOT change)")
{
+ enablePluginDynamicReload();
+
/* Simulate installing plugin plugin_v1.so (ver 1) as plugin.so and loading it at some point of time t1 */
- setupConfigPathTest(configName, buildPath_v1, uuid_t1, effectivePath_v1, runtimePath_v1, 1556825556);
- PluginFactoryUnitTest *factory1 = getFactory(uuid_t1);
- RemapPluginInst *plugin_v1 = factory1->getRemapPlugin(configName, 0, nullptr, error1);
+ auto [plugin_v1, factory1] =
+ testSetupLoadPlugin(configName, buildPath_v1, uuid_t1, 1556825556, effectivePath_v1, runtimePath_v1);
+ plugin_v1->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v1_t1, error);
/* Simulate installing plugin plugin_v2.so (v1) as plugin.so and loading it at some point of time t2 */
/* Note that during the installation plugin_v2.so (v2) is "barberically" overriding the existing plugin.so
which was v1, since the modification time is exactly the same the new v2 plugin would not be loaded and
we should get the same PluginDso address and same effective and runtime paths */
- setupConfigPathTest(configName, buildPath_v2, uuid_t2, effectivePath_v2, runtimePath_v2, 1556825556);
- PluginFactoryUnitTest *factory2 = getFactory(uuid_t2);
- RemapPluginInst *plugin_v2 = factory2->getRemapPlugin(configName, 0, nullptr, error2);
+ auto [plugin_v2, factory2] =
+ testSetupLoadPlugin(configName, buildPath_v2, uuid_t2, 1556825556, effectivePath_v2, runtimePath_v2);
+ plugin_v1->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v1_t2, error);
/* Make sure plugin.so was overriden */
CHECK(effectivePath_v1 == effectivePath_v2);
@@ -554,24 +726,98 @@ SCENARIO("loading multiple version of the same plugin at the same time", "[plugi
validateSuccessfulConfigPathTest(plugin_v1, error1, effectivePath_v1, runtimePath_v1);
validateSuccessfulConfigPathTest(plugin_v2, error2, effectivePath_v2, runtimePath_v1);
- /* Make sure we ended up with the same DSO object and runtime paths should be same - no new plugin was loaded */
- CHECK(&(plugin_v1->_plugin) == &(plugin_v2->_plugin));
- CHECK(plugin_v1->_plugin.runtimePath() == plugin_v2->_plugin.runtimePath());
+ checkTwoLoadedVersionsSame(plugin_v1, plugin_v2);
- /* Make sure v2 DSO was NOT loaded both instances should return same v1 version */
- CHECK(1 == getPluginVersion(plugin_v1->_plugin));
- CHECK(1 == getPluginVersion(plugin_v2->_plugin));
+ /* Make sure v1 callback functions addresses did not change for v1 after v2 was loaded */
+ CHECK(tsRemapInitSym_v1_t1 == tsRemapInitSym_v1_t2);
+ }
- /* Make sure the symbols we get from the 2 loaded plugins yield the same callback function pointer */
- plugin_v1->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v1_t2, error);
- plugin_v2->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v2_t2, error);
- CHECK(nullptr != tsRemapInitSym_v1_t2);
- CHECK(nullptr != tsRemapInitSym_v2_t2);
- CHECK(tsRemapInitSym_v1_t2 == tsRemapInitSym_v2_t2);
+ teardownConfigPathTest(factory1);
+ teardownConfigPathTest(factory2);
+ }
+ }
+
+ GIVEN("two different versions v1 and v2 of same plugin with different time stamps - dynamic plugin reload DISABLED")
+ {
+ WHEN("(1) loading v1, (2) overwriting with v2 and then (3) reloading by using the same plugin name, "
+ "(*) v1 and v2 DSOs modification time are different (changed)")
+ {
+ disablePluginDynamicReload();
+
+ /* Simulate installing plugin plugin_v1.so (ver 1) as plugin.so and loading it at some point of time t1 */
+ auto [plugin_v1, factory1] =
+ testSetupLoadPlugin(configName, buildPath_v1, uuid_t1, 1556825556, effectivePath_v1, runtimePath_v1);
+ plugin_v1->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v1_t1, error);
+
+ /* Simulate installing plugin plugin_v2.so (v1) as plugin.so and loading it at some point of time t2 */
+ /* Note that during the installation plugin_v2.so (v2) is "barberically" overriding the existing plugin.so which was v1 */
+ auto [plugin_v2, factory2] =
+ testSetupLoadPlugin(configName, buildPath_v2, uuid_t2, 1556825557, effectivePath_v2, runtimePath_v2);
+ plugin_v1->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v1_t2, error);
+
+ /* Make sure plugin.so was overriden */
+ CHECK(effectivePath_v1 == effectivePath_v2);
+ /* since dynamic reload is disabled, runtimepaths should be same */
+ CHECK(runtimePath_v1 == runtimePath_v2);
+
+ THEN("expect v1 plugin to remain loaded since dynamic reload is disabled, even though the timestamp has changed")
+ {
+ /* Both getRemapPlugin() calls should succeed but only v1 plugin DSO should be used */
+ validateSuccessfulConfigPathTest(plugin_v1, error1, effectivePath_v1, runtimePath_v1);
+ validateSuccessfulConfigPathTest(plugin_v2, error2, effectivePath_v2, runtimePath_v1);
+
+ checkTwoLoadedVersionsSame(plugin_v1, plugin_v2);
+
+ /* Make sure v1 callback functions addresses did not change for v1 after v2 was loaded */
+ CHECK(tsRemapInitSym_v1_t1 == tsRemapInitSym_v1_t2);
}
teardownConfigPathTest(factory1);
teardownConfigPathTest(factory2);
+ enablePluginDynamicReload();
+ }
+ }
+
+ GIVEN("two different versions v1 and v2 of same plugin with same time stamp - dynamic plugin reload DISABLED")
+ {
+ WHEN("(1) loading v1, (2) overwriting with v2 and then (3) reloading by using the same plugin name, "
+ "(*) v1 and v2 DSOs modification time are same (did NOT change)")
+ {
+ disablePluginDynamicReload();
+
+ /* Simulate installing plugin plugin_v1.so (ver 1) as plugin.so and loading it at some point of time t1 */
+ auto [plugin_v1, factory1] =
+ testSetupLoadPlugin(configName, buildPath_v1, uuid_t1, 1556825556, effectivePath_v1, runtimePath_v1);
+ plugin_v1->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v1_t1, error);
+
+ /* Simulate installing plugin plugin_v2.so (v1) as plugin.so and loading it at some point of time t2 */
+ /* Note that during the installation plugin_v2.so (v2) is "barberically" overriding the existing plugin.so
+ which was v1, since dynamic reload is disabled the new v2 plugin would not be loaded and
+ we should get the same PluginDso address and same effective and runtime paths */
+ auto [plugin_v2, factory2] =
+ testSetupLoadPlugin(configName, buildPath_v2, uuid_t2, 1556825556, effectivePath_v2, runtimePath_v2);
+ plugin_v1->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v1_t2, error);
+
+ /* Make sure plugin.so was overriden */
+ CHECK(effectivePath_v1 == effectivePath_v2);
+ /* since dynamic reload is disabled, runtimepaths should be same */
+ CHECK(runtimePath_v1 == runtimePath_v2);
+
+ THEN("expect v1 plugin to be loaded since dynamic reload is disabled")
+ {
+ /* Both getRemapPlugin() calls should succeed but only v1 plugin DSO should be used */
+ validateSuccessfulConfigPathTest(plugin_v1, error1, effectivePath_v1, runtimePath_v1);
+ validateSuccessfulConfigPathTest(plugin_v2, error2, effectivePath_v2, runtimePath_v1);
+
+ checkTwoLoadedVersionsSame(plugin_v1, plugin_v2);
+
+ /* Make sure v1 callback functions addresses did not change for v1 after v2 was loaded */
+ CHECK(tsRemapInitSym_v1_t1 == tsRemapInitSym_v1_t2);
+ }
+
+ teardownConfigPathTest(factory1);
+ teardownConfigPathTest(factory2);
+ enablePluginDynamicReload();
}
}
@@ -585,9 +831,8 @@ SCENARIO("loading multiple version of the same plugin at the same time", "[plugi
PluginFactoryUnitTest *factory1 = getFactory(uuid_t1);
/* Now provision and load a plugin using a second factory */
- setupConfigPathTest(configName, buildPath_v2, uuid_t2, effectivePath_v2, runtimePath_v2, 1556825556);
- PluginFactoryUnitTest *factory2 = getFactory(uuid_t2);
- RemapPluginInst *plugin_v2 = factory2->getRemapPlugin(configName, 0, nullptr, error2);
+ auto [plugin_v2, factory2] =
+ testSetupLoadPlugin(configName, buildPath_v2, uuid_t2, 1556825556, effectivePath_v2, runtimePath_v2);
THEN("the plugin from the second factory to work")
{
@@ -603,9 +848,183 @@ SCENARIO("loading multiple version of the same plugin at the same time", "[plugi
}
}
+SCENARIO("loading multiple version of the same plugin in mixed mode - global as well as remap plugin", "[plugin][core]")
+{
+ REQUIRE_FALSE(sandboxDir.empty());
+ enablePluginDynamicReload();
+
+ static fs::path uuid_t1 = fs::path("c71e2bab-90dc-4770-9535-c9304c3de381"); /* UUID at moment t1 */
+ static fs::path uuid_t2 = fs::path("c71e2bab-90dc-4770-9535-e7304c3ee732"); /* UUID at moment t2 */
+
+ fs::path effectivePath_v1; /* expected effective path for DSO v1 */
+ fs::path effectivePath_v2; /* expected effective path for DSO v2 */
+ fs::path runtimePath_v1; /* expected runtime path for DSO v1 */
+ fs::path runtimePath_v2; /* expected runtime path for DSO v2 */
+ void *tsRemapInitSym_v1_t1 = nullptr; /* callback address from DSO v1 at moment t1 */
+ void *tsRemapInitSym_v1_t2 = nullptr; /* callback address from DSO v1 at moment t2 */
+ void *tsRemapInitSym_v2_t2 = nullptr; /* callback address from DSO v2 at moment t2 */
+
+ std::string error;
+ std::string error1;
+ std::string error2;
+
+ fs::path configName = fs::path("plugin.so"); /* use same config name for all following tests */
+ fs::path buildPath_v1 = pluginBuildDir / fs::path("plugin_v1.so"); /* DSO v1 */
+ fs::path buildPath_v2 = pluginBuildDir / fs::path("plugin_v2.so"); /* DSO v1 */
+
+ GIVEN("two different versions v1 and v2 of same plugin in mixed mode - dynamic plugin reload DISABLED")
+ {
+ WHEN("(1) loading v1, (2) overwriting with v2 and then (3) reloading by using the same plugin name, "
+ "(*) v1 and v2 DSOs modification time are different (changed)")
+ {
+ disablePluginDynamicReload();
+ /* Simulate installing plugin plugin_v1.so (ver 1) as plugin.so and loading it at some point of time t1 */
+ setupConfigPathTest(configName, buildPath_v1, uuid_t1, effectivePath_v1, runtimePath_v1, 1556825556);
+ GlobalPluginInfo global_plugin_v1;
+ CHECK(global_plugin_v1.loadDso(effectivePath_v1) == true);
+ global_plugin_v1.getSymbol("TSRemapInit", tsRemapInitSym_v1_t1, error);
+
+ /* Simulate installing plugin plugin_v2.so (v1) as plugin.so and loading it at some point of time t2 */
+ /* Note that during the installation plugin_v2.so (v2) is "barberically" overriding the existing plugin.so which was v1 */
+ setupConfigPathTest(configName, buildPath_v2, uuid_t2, effectivePath_v2, runtimePath_v2, 1556825557);
+ PluginFactoryUnitTest *factory2 = getFactory(uuid_t2);
+ RemapPluginInst *plugin_v2 = factory2->getRemapPlugin(configName, 0, nullptr, error2, isPluginDynamicReloadEnabled());
+
+ /* Make sure plugin.so was overriden */
+ CHECK(effectivePath_v1 == effectivePath_v2);
+
+ /* since dynamic reload is disabled, runtimepaths should be same */
+ CHECK(runtimePath_v1 == runtimePath_v2);
+
+ THEN("expect v1 plugin to remain loaded since dynamic reload is disabled, even though the timestamp has changed")
+ {
+ validateSuccessfulConfigPathTest(plugin_v2, error2, effectivePath_v2, runtimePath_v2);
+
+ /* Make sure we ended up with the same DSO object and runtime paths should be same - no new plugin was loaded */
+ CHECK(global_plugin_v1.dlOpenHandle() == plugin_v2->_plugin.dlOpenHandle());
+
+ /* Make sure v2 DSO was NOT really loaded - both instances should return same v1 version */
+ CHECK(1 == global_plugin_v1.getPluginVersion());
+ CHECK(1 == getPluginVersion(plugin_v2->_plugin));
+
+ /* Make sure the symbols we get from the 2 loaded plugins yield the same callback function pointer */
+ global_plugin_v1.getSymbol("TSRemapInit", tsRemapInitSym_v1_t2, error);
+ plugin_v2->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v2_t2, error);
+ CHECK(nullptr != tsRemapInitSym_v1_t2);
+ CHECK(nullptr != tsRemapInitSym_v2_t2);
+ CHECK(tsRemapInitSym_v1_t2 == tsRemapInitSym_v2_t2);
+
+ /* check that the symbol we got originally is still valid */
+ CHECK(tsRemapInitSym_v1_t1 == tsRemapInitSym_v1_t2);
+ }
+
+ teardownConfigPathTest(factory2);
+ enablePluginDynamicReload();
+ }
+ }
+
+ GIVEN("two different versions v1 and v2 of same plugin in mixed mode - dynamic plugin reload DISABLED")
+ {
+ WHEN("(1) loading v1, (2) overwriting with v2 and then (3) reloading by using the same plugin name, "
+ "(*) v1 and v2 DSOs modification times are unchanged")
+ {
+ disablePluginDynamicReload();
+ /* Simulate installing plugin plugin_v1.so (ver 1) as plugin.so and loading it at some point of time t1 */
+ setupConfigPathTest(configName, buildPath_v1, uuid_t1, effectivePath_v1, runtimePath_v1, 1556825556);
+ GlobalPluginInfo global_plugin_v1;
+ CHECK(global_plugin_v1.loadDso(effectivePath_v1) == true);
+ global_plugin_v1.getSymbol("TSRemapInit", tsRemapInitSym_v1_t1, error);
+
+ /* Simulate installing plugin plugin_v2.so (v1) as plugin.so and loading it at some point of time t2 */
+ /* Note that during the installation plugin_v2.so (v2) is "barberically" overriding the existing plugin.so which was v1 */
+ setupConfigPathTest(configName, buildPath_v2, uuid_t2, effectivePath_v2, runtimePath_v2, 1556825556);
+ PluginFactoryUnitTest *factory2 = getFactory(uuid_t2);
+ RemapPluginInst *plugin_v2 = factory2->getRemapPlugin(configName, 0, nullptr, error2, isPluginDynamicReloadEnabled());
+
+ /* Make sure plugin.so was overriden */
+ CHECK(effectivePath_v1 == effectivePath_v2);
+
+ /* since dynamic reload is disabled, runtimepaths should be same */
+ CHECK(runtimePath_v1 == runtimePath_v2);
+
+ THEN("expect v1 plugin to remain loaded since dynamic reload is disabled, even though the timestamp has changed")
+ {
+ validateSuccessfulConfigPathTest(plugin_v2, error2, effectivePath_v2, runtimePath_v2);
+
+ /* Make sure we ended up with the same DSO object and runtime paths should be same - no new plugin was loaded */
+ CHECK(global_plugin_v1.dlOpenHandle() == plugin_v2->_plugin.dlOpenHandle());
+
+ /* Make sure v2 DSO was NOT really loaded - both instances should return same v1 version */
+ CHECK(1 == global_plugin_v1.getPluginVersion());
+ CHECK(1 == getPluginVersion(plugin_v2->_plugin));
+
+ /* Make sure the symbols we get from the 2 loaded plugins yield the same callback function pointer */
+ global_plugin_v1.getSymbol("TSRemapInit", tsRemapInitSym_v1_t2, error);
+ plugin_v2->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v2_t2, error);
+ CHECK(nullptr != tsRemapInitSym_v1_t2);
+ CHECK(nullptr != tsRemapInitSym_v2_t2);
+ CHECK(tsRemapInitSym_v1_t2 == tsRemapInitSym_v2_t2);
+
+ /* check that the symbol we got originally is still valid */
+ CHECK(tsRemapInitSym_v1_t1 == tsRemapInitSym_v1_t2);
+ }
+
+ teardownConfigPathTest(factory2);
+ enablePluginDynamicReload();
+ }
+ }
+
+ GIVEN("two different versions v1 and v2 of same plugin in mixed mode - dynamic plugin reload ENABLED (negative test)")
+ {
+ WHEN("(1) loading v1, (2) overwriting with v2 and then (3) reloading by using the same plugin name, "
+ "(*) v1 and v2 DSOs modification time are different (changed)")
+ {
+ enablePluginDynamicReload();
+ /* Simulate installing plugin plugin_v1.so (ver 1) as plugin.so and loading it at some point of time t1 */
+ setupConfigPathTest(configName, buildPath_v1, uuid_t1, effectivePath_v1, runtimePath_v1, 1556825556);
+ GlobalPluginInfo global_plugin_v1;
+ CHECK(global_plugin_v1.loadDso(effectivePath_v1) == true);
+ global_plugin_v1.getSymbol("TSRemapInit", tsRemapInitSym_v1_t1, error);
+
+ /* Simulate installing plugin plugin_v2.so (v1) as plugin.so and loading it at some point of time t2 */
+ /* Note that during the installation plugin_v2.so (v2) is "barberically" overriding the existing plugin.so which was v1 */
+ auto [plugin_v2, factory2] =
+ testSetupLoadPlugin(configName, buildPath_v2, uuid_t2, 1556825557, effectivePath_v2, runtimePath_v2);
+
+ CHECK(effectivePath_v1 == effectivePath_v2);
+ /* since dynamic reload is enabled runtime paths would be different */
+ CHECK(runtimePath_v1 != runtimePath_v2);
+
+ THEN("expect both to be successfully loaded and used simultaneously")
+ {
+ validateSuccessfulConfigPathTest(plugin_v2, error2, effectivePath_v2, runtimePath_v2);
+
+ /* Make sure what we installed and loaded first was v1 and after the plugin reload we run v2 */
+ CHECK(1 == global_plugin_v1.getPluginVersion());
+ CHECK(2 == getPluginVersion(plugin_v2->_plugin));
+ CHECK(global_plugin_v1.dlOpenHandle() != plugin_v2->_plugin.dlOpenHandle());
+
+ /* Make sure the symbols we get from the 2 loaded plugins don't yield the same callback function pointer */
+ global_plugin_v1.getSymbol("TSRemapInit", tsRemapInitSym_v1_t2, error);
+ plugin_v2->_plugin.getSymbol("TSRemapInit", tsRemapInitSym_v2_t2, error);
+ CHECK(nullptr != tsRemapInitSym_v1_t2);
+ CHECK(nullptr != tsRemapInitSym_v2_t2);
+ CHECK(tsRemapInitSym_v1_t2 != tsRemapInitSym_v2_t2);
+
+ /* Make sure v1 callback functions addresses did not change for v1 after v2 was loaded */
+ CHECK(tsRemapInitSym_v1_t1 == tsRemapInitSym_v1_t2);
+ }
+
+ teardownConfigPathTest(factory2);
+ enablePluginDynamicReload();
+ }
+ }
+}
+
SCENARIO("notifying plugins of config reload", "[plugin][core]")
{
REQUIRE_FALSE(sandboxDir.empty());
+ enablePluginDynamicReload();
/* use 2 copies of the same plugin to test */
fs::path configName1 = fs::path("plugin_testing_calls_1.so");
@@ -629,7 +1048,7 @@ SCENARIO("notifying plugins of config reload", "[plugin][core]")
/* Simulate configuration with 1 factory and 1 plugin */
setupConfigPathTest(configName1, buildPath, uuid_t1, effectivePath1, runtimePath1, 1556825556);
PluginFactoryUnitTest *factory1 = getFactory(uuid_t1);
- RemapPluginInst *plugin1 = factory1->getRemapPlugin(configName1, 0, nullptr, error);
+ RemapPluginInst *plugin1 = factory1->getRemapPlugin(configName1, 0, nullptr, error, isPluginDynamicReloadEnabled());
/* check if loaded successfully */
validateSuccessfulConfigPathTest(plugin1, error, effectivePath1, runtimePath1);
@@ -687,8 +1106,8 @@ SCENARIO("notifying plugins of config reload", "[plugin][core]")
setupConfigPathTest(configName1, buildPath, uuid_t1, effectivePath1, runtimePath1, 1556825556);
setupConfigPathTest(configName2, buildPath, uuid_t1, effectivePath2, runtimePath2, 1556825556, /* append */ true);
PluginFactoryUnitTest *factory1 = getFactory(uuid_t1);
- RemapPluginInst *plugin1 = factory1->getRemapPlugin(configName1, 0, nullptr, error);
- RemapPluginInst *plugin2 = factory1->getRemapPlugin(configName2, 0, nullptr, error);
+ RemapPluginInst *plugin1 = factory1->getRemapPlugin(configName1, 0, nullptr, error, isPluginDynamicReloadEnabled());
+ RemapPluginInst *plugin2 = factory1->getRemapPlugin(configName2, 0, nullptr, error, isPluginDynamicReloadEnabled());
/* check if loaded successfully */
validateSuccessfulConfigPathTest(plugin1, error, effectivePath1, runtimePath1);
@@ -765,8 +1184,8 @@ SCENARIO("notifying plugins of config reload", "[plugin][core]")
setupConfigPathTest(configName1, buildPath, uuid_t1, effectivePath1, runtimePath1, 1556825556);
PluginFactoryUnitTest *factory1 = getFactory(uuid_t1);
PluginFactoryUnitTest *factory2 = getFactory(uuid_t2);
- RemapPluginInst *plugin1 = factory1->getRemapPlugin(configName1, 0, nullptr, error);
- RemapPluginInst *plugin2 = factory2->getRemapPlugin(configName1, 0, nullptr, error);
+ RemapPluginInst *plugin1 = factory1->getRemapPlugin(configName1, 0, nullptr, error, isPluginDynamicReloadEnabled());
+ RemapPluginInst *plugin2 = factory2->getRemapPlugin(configName1, 0, nullptr, error, isPluginDynamicReloadEnabled());
/* Prepare the debug objects */
PluginDebugObject *debugObject1 = getDebugObject(plugin1->_plugin);
@@ -806,11 +1225,11 @@ SCENARIO("notifying plugins of config reload", "[plugin][core]")
PluginFactoryUnitTest *factory2 = getFactory(uuid_t2);
/* 2 plugins loaded by the 1st factory */
- RemapPluginInst *pluginInst1 = factory1->getRemapPlugin(configName1, 0, nullptr, error);
- RemapPluginInst *pluginInst2 = factory1->getRemapPlugin(configName2, 0, nullptr, error);
+ RemapPluginInst *pluginInst1 = factory1->getRemapPlugin(configName1, 0, nullptr, error, isPluginDynamicReloadEnabled());
+ RemapPluginInst *pluginInst2 = factory1->getRemapPlugin(configName2, 0, nullptr, error, isPluginDynamicReloadEnabled());
/* only 1 plugin loaded by the 2st factory */
- RemapPluginInst *pluginInst3 = factory2->getRemapPlugin(configName1, 0, nullptr, error);
+ RemapPluginInst *pluginInst3 = factory2->getRemapPlugin(configName1, 0, nullptr, error, isPluginDynamicReloadEnabled());
/* pluginInst1 and pluginInst3 should be using the same plugin DSO named configName1
* pluginInst2 should be using plugin DSO named configName 2*/
diff --git a/src/traffic_server/traffic_server.cc b/src/traffic_server/traffic_server.cc
index d8658b3..dc1e361 100644
--- a/src/traffic_server/traffic_server.cc
+++ b/src/traffic_server/traffic_server.cc
@@ -2030,6 +2030,8 @@ main(int /* argc ATS_UNUSED */, const char **argv)
// initialize logging (after event and net processor)
Log::init(remote_management_flag ? 0 : Log::NO_REMOTE_MANAGEMENT);
+ (void)parsePluginConfig();
+
// Init plugins as soon as logging is ready.
(void)plugin_init(); // plugin.config