-
Notifications
You must be signed in to change notification settings - Fork 623
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This prototoype contains a plugin scheduler, which automatically resolves dependencies and enables execution of plugins either sequential or parallel
- Loading branch information
1 parent
868623f
commit c2ffb43
Showing
8 changed files
with
880 additions
and
142 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
[package] | ||
name = "plugin-scheduler" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
dep-graph = { path = "../dep-graph" } | ||
generic-array = "1.0" | ||
rayon = "1.8" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
# Plugin Scheduler | ||
|
||
## Plugin | ||
|
||
A plugin is a simple struct containing an identifier, a list of identifier representing the dependencies and a Category or Phase, in which the plugin should be launched. | ||
|
||
## Scheduler | ||
|
||
The Scheduler uses a Collection of Plugins and a list of requested plugin identifiers to create an execution order of the requested plugins. It automatically resolves dependencies and detects errors. Plugins or dependencies containing some kind of error are not added to the scheduler, but are collected as errors. | ||
|
||
|
||
## Usage | ||
|
||
Here is a simple example creating a scheduler and executing plugins in parallel: | ||
|
||
```rust | ||
use std::{marker::PhantomData, slice::Iter, thread, time}; | ||
|
||
use generic_array::typenum::U2; | ||
use plugin_scheduler::{ | ||
plugin::{Phase, Plugin, PluginCollection}, | ||
scheduler::PluginScheduler, | ||
}; | ||
|
||
#[derive(PartialEq, Clone, Debug)] | ||
enum TestCategory { | ||
Phase1, | ||
Phase2, | ||
} | ||
|
||
impl Phase for TestCategory { | ||
type LEN = U2; | ||
|
||
fn get(&self) -> usize { | ||
match self { | ||
Self::Phase1 => 0, | ||
Self::Phase2 => 1, | ||
} | ||
} | ||
} | ||
|
||
impl TestCategory { | ||
pub fn iterator() -> Iter<'static, TestCategory> { | ||
static CATEGORIES: [TestCategory; 2] = [TestCategory::Phase1, TestCategory::Phase2]; | ||
CATEGORIES.iter() | ||
} | ||
} | ||
|
||
#[derive(Clone)] | ||
struct TestPlugin<C> | ||
where | ||
C: Phase + Clone, | ||
{ | ||
category: C, | ||
dependencies: Vec<String>, | ||
name: String, | ||
} | ||
|
||
impl<C> Plugin<C> for TestPlugin<C> | ||
where | ||
C: Phase + Clone, | ||
{ | ||
fn get_category(&self) -> C { | ||
self.category.clone() | ||
} | ||
|
||
fn get_dependencies(&self) -> Vec<String> { | ||
self.dependencies.clone() | ||
} | ||
|
||
fn get_id(&self) -> String { | ||
self.name.clone() | ||
} | ||
} | ||
|
||
struct TestPluginCollection<P, C> | ||
where | ||
P: Plugin<C>, | ||
C: Phase, | ||
{ | ||
plugins: Vec<P>, | ||
phantom: PhantomData<C>, | ||
} | ||
|
||
impl<P, C> TestPluginCollection<P, C> | ||
where | ||
P: Plugin<C>, | ||
C: Phase, | ||
{ | ||
fn new() -> Self { | ||
TestPluginCollection { | ||
plugins: Default::default(), | ||
phantom: Default::default(), | ||
} | ||
} | ||
|
||
fn add(&mut self, plugin: P) { | ||
self.plugins.push(plugin); | ||
} | ||
} | ||
|
||
impl<P, C> PluginCollection<P, C> for TestPluginCollection<P, C> | ||
where | ||
P: Plugin<C> + Clone, | ||
C: Phase, | ||
{ | ||
fn get_plugin(&self, id: &str) -> Option<P> { | ||
for plugin in &self.plugins { | ||
if id == plugin.get_id() { | ||
return Some(plugin.to_owned()); | ||
} | ||
} | ||
None | ||
} | ||
} | ||
|
||
fn main() { | ||
let mut collection = TestPluginCollection::new(); | ||
|
||
collection.add(TestPlugin { | ||
category: TestCategory::Phase2, | ||
dependencies: vec!["1".to_string(), "2".to_string()], | ||
name: "0".to_string(), | ||
}); | ||
|
||
collection.add(TestPlugin { | ||
category: TestCategory::Phase2, | ||
dependencies: vec!["2".to_string()], | ||
name: "1".to_string(), | ||
}); | ||
|
||
collection.add(TestPlugin { | ||
category: TestCategory::Phase1, | ||
dependencies: vec![], | ||
name: "2".to_string(), | ||
}); | ||
|
||
let scheduler = PluginScheduler::create(&collection, vec!["0".to_string()]); | ||
|
||
for phase in TestCategory::iterator() { | ||
scheduler.execute_parallel(phase.to_owned(), |script| todo!()); | ||
} | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// SPDX-FileCopyrightText: 2023 Greenbone AG | ||
// | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
|
||
use std::fmt::Display; | ||
|
||
use crate::plugin::Phase; | ||
|
||
#[derive(Clone, Debug)] | ||
/// Errors, that occur during creation of the PluginScheduler | ||
pub enum SchedulerError<C> | ||
where | ||
C: Phase + Clone, | ||
{ | ||
/// A dependency cycle within the dependency chain of a plugin. | ||
DependencyCycle(Vec<String>), | ||
|
||
/// A plugin is missing in the PluginCollection. | ||
PluginNotFound(Vec<String>, String), | ||
|
||
/// An error in the plugin execution error. Plugins corresponds to Categories. These categories | ||
/// are ran in a specific order, like category 1 runs before category 2. When a plugin of | ||
/// category 1 has a dependency to a plugin of category 2, it is impossible to run the | ||
/// dependency plugin before its dependant. | ||
DependencyOrder(Vec<String>, (String, C), (String, C)), | ||
} | ||
|
||
impl<C> Display for SchedulerError<C> | ||
where | ||
C: Phase + Display + Clone, | ||
{ | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
match self { | ||
Self::DependencyCycle(deps) => { | ||
write!(f, "dependency cycle in ({})", deps.join(",")) | ||
} | ||
Self::PluginNotFound(deps, not_found) => { | ||
if deps.is_empty() { | ||
write!(f, "NVT not found") | ||
} else { | ||
write!(f, "dependency {not_found} not found ({})", deps.join(",")) | ||
} | ||
} | ||
Self::DependencyOrder(deps, dependant, dependency) => { | ||
write!( | ||
f, | ||
"dependency {} of category {} would run after dependant {} of category {} ({})", | ||
dependency.0, | ||
dependency.1, | ||
dependant.0, | ||
dependant.1, | ||
deps.join(",") | ||
) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// SPDX-FileCopyrightText: 2023 Greenbone AG | ||
// | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
|
||
pub mod error; | ||
pub mod plugin; | ||
pub mod scheduler; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// SPDX-FileCopyrightText: 2023 Greenbone AG | ||
// | ||
// SPDX-License-Identifier: GPL-2.0-or-later | ||
|
||
use generic_array::ArrayLength; | ||
|
||
/// The phase is used to differentiate execution phases of Plugins. Different phases might use | ||
/// different setups for execution. | ||
pub trait Phase { | ||
/// Number of Phases | ||
type LEN: ArrayLength; | ||
/// Get the value of a phase for indexing | ||
fn get(&self) -> usize; | ||
} | ||
|
||
/// This Trait defines a Plugin used for the Plugin Scheduler. | ||
pub trait Plugin<C> | ||
where | ||
C: Phase, | ||
{ | ||
/// Returns an identifier of a Plugin | ||
fn get_id(&self) -> String; | ||
/// Returns a list of identifiers corresponding to the plugins dependencies | ||
fn get_dependencies(&self) -> Vec<String>; | ||
/// Return the category of a Plugin | ||
fn get_category(&self) -> C; | ||
} | ||
|
||
/// A PluginCollection is a collection of plugins, to look them up | ||
pub trait PluginCollection<P, C> | ||
where | ||
P: Plugin<C>, | ||
C: Phase, | ||
{ | ||
/// Search for a Plugin by an identifier | ||
fn get_plugin(&self, id: &str) -> Option<P>; | ||
} |
Oops, something went wrong.