Skip to content

Commit

Permalink
Allow applications to load plugins (#953)
Browse files Browse the repository at this point in the history
* Move plugins loading in Runtime init

* Plugins support is behind conditional feature

* Cargo.toml format

* Update config format

* Update config format

* Admin space config

* Runtime::new_with_plugins_manager

* RuntimeBuilder

* RuntimeBuilder

* zenohd uses zenoh::open
  • Loading branch information
Mallets authored Apr 22, 2024
1 parent 7e52c20 commit cbeffcf
Show file tree
Hide file tree
Showing 19 changed files with 432 additions and 263 deletions.
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 12 additions & 4 deletions DEFAULT_CONFIG.json5
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,8 @@
/// Configure the Admin Space
/// Unstable: this configuration part works as advertised, but may change in a future release
adminspace: {
// Enables the admin space
enabled: false,
// read and/or write permissions on the admin space
permissions: {
read: true,
Expand All @@ -374,9 +376,15 @@
///
/// Plugins configurations
///
// /// Directories where plugins configured by name should be looked for. Plugins configured by __path__ are not subject to lookup
// plugins_search_dirs: [],
// /// Plugins are only loaded if present in the configuration. When starting
//
// plugins_loading: {
// // Enable plugins loading.
// enabled: false,
// /// Directories where plugins configured by name should be looked for. Plugins configured by __path__ are not subject to lookup.
// /// If `enabled: true` and `search_dirs` is not specified then `search_dirs` falls back to the default value: ".:~/.zenoh/lib:/opt/homebrew/lib:/usr/local/lib:/usr/lib"
// search_dirs: [],
// },
// /// Plugins are only loaded if `plugins_loading: { enabled: true }` and present in the configuration when starting.
// /// Once loaded, they may react to changes in the configuration made through the zenoh instance's adminspace.
// plugins: {
// /// If no `__path__` is given to a plugin, zenohd will automatically search for a shared library matching the plugin's name (here, `libzenoh_plugin_rest.so` would be searched for on linux)
Expand All @@ -385,7 +393,7 @@
// /// - If `__config__` is specified, it's content is merged into plugin configuration
// /// - Properties loaded from `__config__` file overrides existing properties
// /// - If json objects in loaded file contains `__config__` properties, they are processed recursively
// /// This is used in the 'storcge_manager' which supports subplugins, each with it's own config
// /// This is used in the 'storage_manager' which supports subplugins, each with it's own config
// ///
// /// See below exapmle of plugin configuration using `__config__` property
//
Expand Down
18 changes: 14 additions & 4 deletions commons/zenoh-config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,9 @@ validated_struct::validator! {
/// To use it, you must enable zenoh's <code>unstable</code> feature flag.
/// </div>
AdminSpaceConf {
/// Enable the admin space
#[serde(default = "set_false")]
pub enabled: bool,
/// Permissions on the admin space
pub permissions:
PermissionsConf {
Expand All @@ -507,7 +510,11 @@ validated_struct::validator! {

/// A list of directories where plugins may be searched for if no `__path__` was specified for them.
/// The executable's current directory will be added to the search paths.
plugins_search_dirs: Vec<String>, // TODO (low-prio): Switch this String to a PathBuf? (applies to other paths in the config as well)
pub plugins_loading: #[derive(Default)]
PluginsLoading {
pub enabled: bool,
pub search_dirs: Option<Vec<String>>, // TODO (low-prio): Switch this String to a PathBuf? (applies to other paths in the config as well)
},
#[validated(recursive_accessors)]
/// The configuration for plugins.
///
Expand Down Expand Up @@ -721,10 +728,13 @@ impl Config {
}

pub fn libloader(&self) -> LibLoader {
if self.plugins_search_dirs.is_empty() {
LibLoader::default()
if self.plugins_loading.enabled {
match self.plugins_loading.search_dirs() {
Some(dirs) => LibLoader::new(dirs, true),
None => LibLoader::default(),
}
} else {
LibLoader::new(&self.plugins_search_dirs, true)
LibLoader::empty()
}
}
}
Expand Down
7 changes: 7 additions & 0 deletions commons/zenoh-util/src/std_only/lib_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,13 @@ pub struct LibLoader {
}

impl LibLoader {
/// Return an empty `LibLoader`.
pub fn empty() -> LibLoader {
LibLoader {
search_paths: Vec::new(),
}
}

/// Returns the list of search paths used by `LibLoader::default()`
pub fn default_search_paths() -> &'static str {
&LIB_DEFAULT_SEARCH_PATHS
Expand Down
6 changes: 3 additions & 3 deletions plugins/zenoh-plugin-storage-manager/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ impl StorageRuntimeInner {
.unwrap_or_default();

let plugins_manager = PluginsManager::dynamic(lib_loader.clone(), BACKEND_LIB_PREFIX)
.declare_static_plugin::<MemoryBackend>();
.declare_static_plugin::<MemoryBackend>(true);

let session = Arc::new(zenoh::init(runtime.clone()).res_sync()?);

Expand Down Expand Up @@ -198,10 +198,10 @@ impl StorageRuntimeInner {
declared
} else if let Some(paths) = config.paths() {
self.plugins_manager
.declare_dynamic_plugin_by_paths(volume_id, paths)?
.declare_dynamic_plugin_by_paths(volume_id, paths, true)?
} else {
self.plugins_manager
.declare_dynamic_plugin_by_name(volume_id, backend_name)?
.declare_dynamic_plugin_by_name(volume_id, backend_name, true)?
};
let loaded = declared.load()?;
loaded.start(config)?;
Expand Down
19 changes: 13 additions & 6 deletions plugins/zenoh-plugin-trait/src/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub trait DeclaredPlugin<StartArgs, Instance>: PluginStatus {
}
pub trait LoadedPlugin<StartArgs, Instance>: PluginStatus {
fn as_status(&self) -> &dyn PluginStatus;
fn required(&self) -> bool;
fn start(&mut self, args: &StartArgs) -> ZResult<&mut dyn StartedPlugin<StartArgs, Instance>>;
fn started(&self) -> Option<&dyn StartedPlugin<StartArgs, Instance>>;
fn started_mut(&mut self) -> Option<&mut dyn StartedPlugin<StartArgs, Instance>>;
Expand All @@ -44,11 +45,11 @@ pub trait StartedPlugin<StartArgs, Instance>: PluginStatus {
}

struct PluginRecord<StartArgs: PluginStartArgs, Instance: PluginInstance>(
Box<dyn DeclaredPlugin<StartArgs, Instance> + Send>,
Box<dyn DeclaredPlugin<StartArgs, Instance> + Send + Sync>,
);

impl<StartArgs: PluginStartArgs, Instance: PluginInstance> PluginRecord<StartArgs, Instance> {
fn new<P: DeclaredPlugin<StartArgs, Instance> + Send + 'static>(plugin: P) -> Self {
fn new<P: DeclaredPlugin<StartArgs, Instance> + Send + Sync + 'static>(plugin: P) -> Self {
Self(Box::new(plugin))
}
}
Expand Down Expand Up @@ -126,8 +127,9 @@ impl<StartArgs: PluginStartArgs + 'static, Instance: PluginInstance + 'static>
P: Plugin<StartArgs = StartArgs, Instance = Instance> + Send + Sync,
>(
mut self,
required: bool,
) -> Self {
let plugin_loader: StaticPlugin<StartArgs, Instance, P> = StaticPlugin::new();
let plugin_loader: StaticPlugin<StartArgs, Instance, P> = StaticPlugin::new(required);
self.plugins.push(PluginRecord::new(plugin_loader));
tracing::debug!(
"Declared static plugin {}",
Expand All @@ -141,6 +143,7 @@ impl<StartArgs: PluginStartArgs + 'static, Instance: PluginInstance + 'static>
&mut self,
name: S,
plugin_name: &str,
required: bool,
) -> ZResult<&mut dyn DeclaredPlugin<StartArgs, Instance>> {
let name = name.into();
let plugin_name = format!("{}{}", self.default_lib_prefix, plugin_name);
Expand All @@ -150,8 +153,11 @@ impl<StartArgs: PluginStartArgs + 'static, Instance: PluginInstance + 'static>
.ok_or("Dynamic plugin loading is disabled")?
.clone();
tracing::debug!("Declared dynamic plugin {} by name {}", &name, &plugin_name);
let loader =
DynamicPlugin::new(name, DynamicPluginSource::ByName((libloader, plugin_name)));
let loader = DynamicPlugin::new(
name,
DynamicPluginSource::ByName((libloader, plugin_name)),
required,
);
self.plugins.push(PluginRecord::new(loader));
Ok(self.plugins.last_mut().unwrap())
}
Expand All @@ -161,11 +167,12 @@ impl<StartArgs: PluginStartArgs + 'static, Instance: PluginInstance + 'static>
&mut self,
name: S,
paths: &[P],
required: bool,
) -> ZResult<&mut dyn DeclaredPlugin<StartArgs, Instance>> {
let name = name.into();
let paths = paths.iter().map(|p| p.as_ref().into()).collect();
tracing::debug!("Declared dynamic plugin {} by paths {:?}", &name, &paths);
let loader = DynamicPlugin::new(name, DynamicPluginSource::ByPaths(paths));
let loader = DynamicPlugin::new(name, DynamicPluginSource::ByPaths(paths), required);
self.plugins.push(PluginRecord::new(loader));
Ok(self.plugins.last_mut().unwrap())
}
Expand Down
7 changes: 6 additions & 1 deletion plugins/zenoh-plugin-trait/src/manager/dynamic_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,16 +106,18 @@ impl<StartArgs: PluginStartArgs, Instance: PluginInstance>

pub struct DynamicPlugin<StartArgs, Instance> {
name: String,
required: bool,
report: PluginReport,
source: DynamicPluginSource,
starter: Option<DynamicPluginStarter<StartArgs, Instance>>,
instance: Option<Instance>,
}

impl<StartArgs, Instance> DynamicPlugin<StartArgs, Instance> {
pub fn new(name: String, source: DynamicPluginSource) -> Self {
pub fn new(name: String, source: DynamicPluginSource, required: bool) -> Self {
Self {
name,
required,
report: PluginReport::new(),
source,
starter: None,
Expand Down Expand Up @@ -202,6 +204,9 @@ impl<StartArgs: PluginStartArgs, Instance: PluginInstance> LoadedPlugin<StartArg
fn as_status(&self) -> &dyn PluginStatus {
self
}
fn required(&self) -> bool {
self.required
}
fn start(&mut self, args: &StartArgs) -> ZResult<&mut dyn StartedPlugin<StartArgs, Instance>> {
let starter = self
.starter
Expand Down
7 changes: 6 additions & 1 deletion plugins/zenoh-plugin-trait/src/manager/static_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,18 @@ where
P: Plugin<StartArgs = StartArgs, Instance = Instance>,
{
instance: Option<Instance>,
required: bool,
phantom: PhantomData<P>,
}

impl<StartArgs, Instance: PluginInstance, P> StaticPlugin<StartArgs, Instance, P>
where
P: Plugin<StartArgs = StartArgs, Instance = Instance>,
{
pub fn new() -> Self {
pub fn new(required: bool) -> Self {
Self {
instance: None,
required,
phantom: PhantomData,
}
}
Expand Down Expand Up @@ -92,6 +94,9 @@ where
fn as_status(&self) -> &dyn PluginStatus {
self
}
fn required(&self) -> bool {
self.required
}
fn start(&mut self, args: &StartArgs) -> ZResult<&mut dyn StartedPlugin<StartArgs, Instance>> {
if self.instance.is_none() {
tracing::debug!("Plugin `{}` started", self.name());
Expand Down
2 changes: 1 addition & 1 deletion plugins/zenoh-plugin-trait/src/plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ pub trait PluginControl {

pub trait PluginStartArgs: StructVersion {}

pub trait PluginInstance: StructVersion + PluginControl + Send {}
pub trait PluginInstance: StructVersion + PluginControl + Send + Sync {}

/// Base plugin trait. The loaded plugin
pub trait Plugin: Sized + 'static {
Expand Down
2 changes: 1 addition & 1 deletion zenoh/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ maintenance = { status = "actively-developed" }
auth_pubkey = ["zenoh-transport/auth_pubkey"]
auth_usrpwd = ["zenoh-transport/auth_usrpwd"]
complete_n = ["zenoh-codec/complete_n"]
plugins = []
shared-memory = [
"zenoh-shm",
"zenoh-protocol/shared-memory",
Expand Down Expand Up @@ -70,7 +71,6 @@ ahash = { workspace = true }
async-trait = { workspace = true }
base64 = { workspace = true }
const_format = { workspace = true }

event-listener = { workspace = true }
flume = { workspace = true }
form_urlencoded = { workspace = true }
Expand Down
5 changes: 5 additions & 0 deletions zenoh/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ pub use zenoh_result::ZResult as Result;

const GIT_VERSION: &str = git_version!(prefix = "v", cargo_prefix = "v");

lazy_static::lazy_static!(
static ref LONG_VERSION: String = format!("{} built with {}", GIT_VERSION, env!("RUSTC_VERSION"));
);

pub const FEATURES: &str = concat_enabled_features!(
prefix = "zenoh",
features = [
Expand Down Expand Up @@ -137,6 +141,7 @@ pub mod handlers;
pub mod info;
#[cfg(feature = "unstable")]
pub mod liveliness;
#[cfg(all(feature = "unstable", feature = "plugins"))]
pub mod plugins;
pub mod prelude;
pub mod publication;
Expand Down
Loading

0 comments on commit cbeffcf

Please sign in to comment.