Add plugin stubs and refactor to use QS (#33)

* Refactor to use qs in plugins

* fmt

* Add empty bindings for plugins
This commit is contained in:
Firstyear 2019-04-01 11:06:34 +10:00 committed by GitHub
parent dffe5ca23a
commit b065b76130
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 233 additions and 166 deletions

View file

@ -52,7 +52,6 @@ macro_rules! audit_segment {
}};
}
macro_rules! try_audit {
($audit:ident, $result:expr, $logFormat:expr, $errorType:expr) => {
match $result {
@ -61,7 +60,7 @@ macro_rules! try_audit {
audit_log!($audit, $logFormat, e);
return Err($errorType);
}
}
}
};
}

View file

@ -391,7 +391,12 @@ impl BackendWriteTransaction {
.conn
.backup(rusqlite::DatabaseName::Main, dstPath, None);
try_audit!(audit, result, "Error in sqlite {:?}", OperationError::SQLiteError);
try_audit!(
audit,
result,
"Error in sqlite {:?}",
OperationError::SQLiteError
);
Ok(())
}

View file

@ -8,6 +8,7 @@ use error::OperationError;
use event::CreateEvent;
use filter::Filter;
use schema::SchemaWriteTransaction;
use server::{QueryServerReadTransaction, QueryServerWriteTransaction};
// TO FINISH
/*
@ -30,11 +31,10 @@ impl Plugin for Base {
// the schema of the running instance
fn pre_create(
be: &BackendWriteTransaction,
au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
_ce: &CreateEvent,
_schema: &SchemaWriteTransaction,
) -> Result<(), OperationError> {
// For each candidate
for entry in cand.iter_mut() {
@ -90,14 +90,14 @@ impl Plugin for Base {
// Make it a string, so we can filter.
let str_uuid = format!("{}", c_uuid);
let mut au_be = AuditScope::new("be_exist");
let mut au_qs = AuditScope::new("qs_exist");
// We need to clone to the filter because it owns the content
let filt = Filter::Eq(name_uuid.clone(), str_uuid.clone());
let r = be.exists(&mut au_be, &filt);
let r = qs.internal_exists(&mut au_qs, filt);
au.append_scope(au_be);
au.append_scope(au_qs);
// end the scope for the be operation.
match r {
@ -108,7 +108,7 @@ impl Plugin for Base {
}
}
Err(e) => {
audit_log!(au, "Backend error occured checking Base existance. {:?}", e);
audit_log!(au, "Error occured checking Base existance. {:?}", e);
return Err(OperationError::Plugin);
}
}
@ -130,12 +130,14 @@ impl Plugin for Base {
mod tests {
use super::super::Plugin;
use super::Base;
use std::sync::Arc;
use audit::AuditScope;
use be::{Backend, BackendWriteTransaction};
use entry::{Entry, EntryInvalid, EntryNew, EntryValid};
use be::Backend;
use entry::{Entry, EntryInvalid, EntryNew};
use event::CreateEvent;
use schema::{Schema, SchemaWriteTransaction};
use schema::Schema;
use server::{QueryServer, QueryServerWriteTransaction};
macro_rules! run_pre_create_test {
(
@ -149,29 +151,34 @@ mod tests {
audit_segment!(au, || {
// Create an in memory BE
let be = Backend::new(&mut au, "").unwrap();
let be_txn = be.write();
// TODO: Preload entries here!
let schema_outer = Schema::new(&mut au).unwrap();
{
let mut schema = schema_outer.write();
schema.bootstrap_core(&mut au).unwrap();
schema.commit().unwrap();
}
let qs = QueryServer::new(be, Arc::new(schema_outer));
if !$preload_entries.is_empty() {
assert!(be_txn.create(&mut au, &$preload_entries).is_ok());
};
let qs_write = qs.write();
qs_write.internal_create(&mut au, $preload_entries);
assert!(qs_write.commit(&mut au).is_ok());
}
let ce = CreateEvent::from_vec($create_entries.clone());
let schema_be = Schema::new(&mut au).unwrap();
let mut schema = schema_be.write();
schema.bootstrap_core(&mut au).unwrap();
let mut au_test = AuditScope::new("pre_create_test");
audit_segment!(au_test, || $test_fn(
&be_txn,
&mut au_test,
&mut $create_entries,
&ce,
&schema,
));
schema.commit().unwrap();
be_txn.commit().unwrap();
{
let qs_write = qs.write();
audit_segment!(au_test, || $test_fn(
&mut au_test,
&qs_write,
&mut $create_entries,
&ce,
));
assert!(qs_write.commit(&mut au).is_ok());
}
au.append_scope(au_test);
});
@ -183,19 +190,18 @@ mod tests {
// Check empty create
#[test]
fn test_pre_create_empty() {
let preload: Vec<Entry<EntryValid, EntryNew>> = Vec::new();
let preload: Vec<Entry<EntryInvalid, EntryNew>> = Vec::new();
let mut create: Vec<Entry<EntryInvalid, EntryNew>> = Vec::new();
run_pre_create_test!(
preload,
create,
false,
false,
|be: &BackendWriteTransaction,
au: &mut AuditScope,
|au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
ce: &CreateEvent,
schema: &SchemaWriteTransaction| {
let r = Base::pre_create(be, au, cand, ce, schema);
ce: &CreateEvent| {
let r = Base::pre_create(au, qs, cand, ce);
assert!(r.is_ok());
// Nothing should have changed.
@ -207,7 +213,7 @@ mod tests {
// check create where no uuid
#[test]
fn test_pre_create_no_uuid() {
let preload: Vec<Entry<EntryValid, EntryNew>> = Vec::new();
let preload: Vec<Entry<EntryInvalid, EntryNew>> = Vec::new();
let e: Entry<EntryInvalid, EntryNew> = serde_json::from_str(
r#"{
@ -230,12 +236,11 @@ mod tests {
create,
false,
false,
|be: &BackendWriteTransaction,
au: &mut AuditScope,
|au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
ce: &CreateEvent,
schema: &SchemaWriteTransaction| {
let r = Base::pre_create(be, au, cand, ce, schema);
ce: &CreateEvent| {
let r = Base::pre_create(au, qs, cand, ce);
assert!(r.is_ok());
// Assert that the entry contains the attr "uuid" now.
let ue = cand.first().unwrap();
@ -247,7 +252,7 @@ mod tests {
// check unparseable uuid
#[test]
fn test_pre_create_uuid_invalid() {
let preload: Vec<Entry<EntryValid, EntryNew>> = Vec::new();
let preload: Vec<Entry<EntryInvalid, EntryNew>> = Vec::new();
let e: Entry<EntryInvalid, EntryNew> = serde_json::from_str(
r#"{
@ -271,12 +276,11 @@ mod tests {
create,
false,
false,
|be: &BackendWriteTransaction,
au: &mut AuditScope,
|au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
ce: &CreateEvent,
schema: &SchemaWriteTransaction| {
let r = Base::pre_create(be, au, cand, ce, schema);
ce: &CreateEvent| {
let r = Base::pre_create(au, qs, cand, ce);
assert!(r.is_err());
}
);
@ -285,7 +289,7 @@ mod tests {
// check entry where uuid is empty list
#[test]
fn test_pre_create_uuid_empty() {
let preload: Vec<Entry<EntryValid, EntryNew>> = Vec::new();
let preload: Vec<Entry<EntryInvalid, EntryNew>> = Vec::new();
let e: Entry<EntryInvalid, EntryNew> = serde_json::from_str(
r#"{
@ -309,12 +313,11 @@ mod tests {
create,
false,
false,
|be: &BackendWriteTransaction,
au: &mut AuditScope,
|au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
ce: &CreateEvent,
schema: &SchemaWriteTransaction| {
let r = Base::pre_create(be, au, cand, ce, schema);
ce: &CreateEvent| {
let r = Base::pre_create(au, qs, cand, ce);
assert!(r.is_err());
}
);
@ -323,7 +326,7 @@ mod tests {
// check create where provided uuid is valid. It should be unchanged.
#[test]
fn test_pre_create_uuid_valid() {
let preload: Vec<Entry<EntryValid, EntryNew>> = Vec::new();
let preload: Vec<Entry<EntryInvalid, EntryNew>> = Vec::new();
let e: Entry<EntryInvalid, EntryNew> = serde_json::from_str(
r#"{
@ -347,12 +350,11 @@ mod tests {
create,
false,
false,
|be: &BackendWriteTransaction,
au: &mut AuditScope,
|au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
ce: &CreateEvent,
schema: &SchemaWriteTransaction| {
let r = Base::pre_create(be, au, cand, ce, schema);
ce: &CreateEvent| {
let r = Base::pre_create(au, qs, cand, ce);
assert!(r.is_ok());
let ue = cand.first().unwrap();
assert!(ue.attribute_equality("uuid", "79724141-3603-4060-b6bb-35c72772611d"));
@ -362,7 +364,7 @@ mod tests {
#[test]
fn test_pre_create_uuid_valid_multi() {
let preload: Vec<Entry<EntryValid, EntryNew>> = Vec::new();
let preload: Vec<Entry<EntryInvalid, EntryNew>> = Vec::new();
let e: Entry<EntryInvalid, EntryNew> = serde_json::from_str(
r#"{
@ -386,12 +388,11 @@ mod tests {
create,
false,
false,
|be: &BackendWriteTransaction,
au: &mut AuditScope,
|au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
ce: &CreateEvent,
schema: &SchemaWriteTransaction| {
let r = Base::pre_create(be, au, cand, ce, schema);
ce: &CreateEvent| {
let r = Base::pre_create(au, qs, cand, ce);
assert!(r.is_err());
}
);
@ -416,19 +417,18 @@ mod tests {
.unwrap();
let mut create = vec![e.clone()];
let preload = vec![unsafe { e.to_valid_new() }];
let preload = vec![e];
run_pre_create_test!(
preload,
create,
false,
false,
|be: &BackendWriteTransaction,
au: &mut AuditScope,
|au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
ce: &CreateEvent,
schema: &SchemaWriteTransaction| {
let r = Base::pre_create(be, au, cand, ce, schema);
ce: &CreateEvent| {
let r = Base::pre_create(au, qs, cand, ce);
assert!(r.is_err());
}
);

View file

@ -1,29 +1,36 @@
use audit::AuditScope;
use be::BackendWriteTransaction;
use entry::{Entry, EntryInvalid, EntryNew};
use entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntryValid};
use error::OperationError;
use event::CreateEvent;
use schema::SchemaWriteTransaction;
use event::{CreateEvent, DeleteEvent, ModifyEvent, SearchEvent};
use server::QueryServerWriteTransaction;
mod base;
mod failure;
mod protected;
mod recycle;
mod refint;
trait Plugin {
fn id() -> &'static str;
fn pre_create(
// TODO: I think this is wrong, it should be a query server
_be: &BackendWriteTransaction,
// TODO: I think this is wrong, it should be a query server.
// Narators voice: He was wrong ... it must be a query server.
_au: &mut AuditScope,
_qs: &QueryServerWriteTransaction,
_cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
_ce: &CreateEvent,
_schema: &SchemaWriteTransaction,
) -> Result<(), OperationError> {
Ok(())
}
fn post_create() -> Result<(), OperationError> {
fn post_create(
_au: &mut AuditScope,
_qs: &QueryServerWriteTransaction,
// List of what we commited that was validate?
_cand: &Vec<Entry<EntryValid, EntryNew>>,
_ce: &CreateEvent,
) -> Result<(), OperationError> {
Ok(())
}
@ -31,7 +38,12 @@ trait Plugin {
Ok(())
}
fn post_modify() -> Result<(), OperationError> {
fn post_modify(
_au: &mut AuditScope,
_qs: &QueryServerWriteTransaction,
_cand: &Vec<Entry<EntryValid, EntryCommitted>>,
_ce: &ModifyEvent,
) -> Result<(), OperationError> {
Ok(())
}
@ -39,7 +51,12 @@ trait Plugin {
Ok(())
}
fn post_delete() -> Result<(), OperationError> {
fn post_delete(
_au: &mut AuditScope,
_qs: &QueryServerWriteTransaction,
_cand: &Vec<Entry<EntryValid, EntryCommitted>>,
_ce: &DeleteEvent,
) -> Result<(), OperationError> {
Ok(())
}
@ -54,22 +71,22 @@ trait Plugin {
pub struct Plugins {}
// TODO: Should this be a function instead, to allow inlining and better debug?
macro_rules! run_pre_create_plugin {
(
$be_txn:ident,
$au:ident,
$qs:ident,
$cand:ident,
$ce:ident,
$schema:ident,
$target_plugin:ty
) => {{
let mut audit_scope = AuditScope::new(<($target_plugin)>::id());
let r = audit_segment!(audit_scope, || <($target_plugin)>::pre_create(
$be_txn,
&mut audit_scope,
$qs,
$cand,
$ce,
$schema
));
$au.append_scope(audit_scope);
r
@ -78,20 +95,74 @@ macro_rules! run_pre_create_plugin {
impl Plugins {
pub fn run_pre_create(
be_txn: &BackendWriteTransaction,
au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
ce: &CreateEvent,
schema: &SchemaWriteTransaction,
) -> Result<(), OperationError> {
audit_segment!(au, || {
// map chain?
let base_res = run_pre_create_plugin!(be_txn, au, cand, ce, schema, base::Base);
let res = run_pre_create_plugin!(au, qs, cand, ce, base::Base).and_then(|_| {
run_pre_create_plugin!(au, qs, cand, ce, refint::ReferentialIntegrity)
});
// TODO, actually return the right thing ...
base_res
res
})
}
pub fn run_post_create(
au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &Vec<Entry<EntryValid, EntryNew>>,
ce: &CreateEvent,
) -> Result<(), OperationError> {
Ok(())
}
pub fn run_pre_modify(
au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
me: &ModifyEvent,
) -> Result<(), OperationError> {
Ok(())
}
pub fn run_post_modify(
au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &Vec<Entry<EntryValid, EntryCommitted>>,
me: &ModifyEvent,
) -> Result<(), OperationError> {
Ok(())
}
pub fn run_pre_delete(
au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
de: &DeleteEvent,
) -> Result<(), OperationError> {
Ok(())
}
pub fn run_post_delete(
au: &mut AuditScope,
qs: &QueryServerWriteTransaction,
cand: &Vec<Entry<EntryValid, EntryCommitted>>,
de: &DeleteEvent,
) -> Result<(), OperationError> {
Ok(())
}
pub fn run_pre_search(au: &mut AuditScope) -> Result<(), OperationError> {
Ok(())
}
pub fn run_post_search(au: &mut AuditScope) -> Result<(), OperationError> {
Ok(())
}
}
// We should define the order that plugins should run

View file

@ -22,10 +22,6 @@ use schema::{
// This is the core of the server. It implements all
// the search and modify actions, applies access controls
// and get's everything ready to push back to the fe code
// This is it's own actor, so we can have a write addr and a read addr,
// and it allows serialisation that way rather than relying on
// the backend
pub trait QueryServerReadTransaction {
type BackendTransactionType: BackendReadTransaction;
fn get_be_txn(&self) -> &Self::BackendTransactionType;
@ -56,7 +52,17 @@ pub trait QueryServerReadTransaction {
// TODO: Assert access control allows the filter and requested attrs.
// TODO: Pre-search plugins
let mut audit_plugin_pre = AuditScope::new("plugin_pre_search");
let plug_pre_res = Plugins::run_pre_search(&mut audit_plugin_pre);
au.append_scope(audit_plugin_pre);
match plug_pre_res {
Ok(_) => {}
Err(e) => {
audit_log!(au, "Search operation failed (plugin), {:?}", e);
return Err(e);
}
}
let mut audit_be = AuditScope::new("backend_search");
let res = self
@ -66,7 +72,21 @@ pub trait QueryServerReadTransaction {
.map_err(|_| OperationError::Backend);
au.append_scope(audit_be);
// TODO: Post-search plugins
if res.is_err() {
return res;
}
let mut audit_plugin_post = AuditScope::new("plugin_post_search");
let plug_post_res = Plugins::run_post_search(&mut audit_plugin_post);
au.append_scope(audit_plugin_post);
match plug_post_res {
Ok(_) => {}
Err(e) => {
audit_log!(au, "Search operation failed (plugin), {:?}", e);
return Err(e);
}
}
// TODO: We'll add ACI here. I think ACI should transform from
// internal -> proto entries since we have to anyway ...
@ -413,13 +433,8 @@ impl<'a> QueryServerWriteTransaction<'a> {
// I have no intent to make these dynamic or configurable.
let mut audit_plugin_pre = AuditScope::new("plugin_pre_create");
let plug_pre_res = Plugins::run_pre_create(
&self.be_txn,
&mut audit_plugin_pre,
&mut candidates,
ce,
&self.schema,
);
let plug_pre_res =
Plugins::run_pre_create(&mut audit_plugin_pre, &self, &mut candidates, ce);
au.append_scope(audit_plugin_pre);
if plug_pre_res.is_err() {
@ -459,6 +474,15 @@ impl<'a> QueryServerWriteTransaction<'a> {
}
// Run any post plugins
let mut audit_plugin_post = AuditScope::new("plugin_post_create");
let plug_post_res = Plugins::run_post_create(&mut audit_plugin_post, &self, &norm_cand, ce);
au.append_scope(audit_plugin_post);
if plug_post_res.is_err() {
audit_log!(au, "Create operation failed (plugin), {:?}", plug_post_res);
return plug_post_res;
}
// Commit the txn
// let commit, commit!
// be_txn.commit();
@ -504,7 +528,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
Err(e) => return Err(OperationError::SchemaViolation(e)),
};
let candidates: Vec<Entry<EntryInvalid, EntryCommitted>> = pre_candidates
let mut candidates: Vec<Entry<EntryInvalid, EntryCommitted>> = pre_candidates
.into_iter()
.map(|er| {
// TODO: Deal with this properly william
@ -515,42 +539,16 @@ impl<'a> QueryServerWriteTransaction<'a> {
audit_log!(au, "delete: candidates -> {:?}", candidates);
// Pre delete plugs
let mut audit_plugin_pre = AuditScope::new("plugin_pre_delete");
let plug_pre_res =
Plugins::run_pre_delete(&mut audit_plugin_pre, &self, &mut candidates, de);
au.append_scope(audit_plugin_pre);
// FIXME: This normalisation COPIES everything, which may be
// slow.
/*
let (norm_cand, invalid_cand): (
Vec<Result<Entry<EntryValid, EntryCommitted>, _>>,
Vec<Result<_, SchemaError>>,
) = candidates
.into_iter()
.map(|e| e.validate(&self.schema))
.partition(|e| e.is_ok());
for err in invalid_cand.iter() {
audit_log!(au, "Schema Violation: {:?}", err);
if plug_pre_res.is_err() {
audit_log!(au, "Delete operation failed (plugin), {:?}", plug_pre_res);
return plug_pre_res;
}
// TODO: Make this better
for err in invalid_cand.iter() {
return Err(OperationError::SchemaViolation(err.unwrap_err()));
}
let del_cand: Vec<Entry<EntryValid, EntryCommitted>> = norm_cand
.into_iter()
.map(|e| match e {
Ok(v) => {
audit_log!(au, "delete: intent candidate {:?}", v);
v
}
Err(_) => panic!("Invalid data set state!!!"),
})
.collect();
*/
let res: Result<Vec<Entry<EntryValid, EntryCommitted>>, SchemaError> = candidates
.into_iter()
.map(|e| e.validate(&self.schema))
@ -581,6 +579,14 @@ impl<'a> QueryServerWriteTransaction<'a> {
}
// Post delete plugs
let mut audit_plugin_post = AuditScope::new("plugin_post_delete");
let plug_post_res = Plugins::run_post_delete(&mut audit_plugin_post, &self, &del_cand, de);
au.append_scope(audit_plugin_post);
if plug_post_res.is_err() {
audit_log!(au, "Delete operation failed (plugin), {:?}", plug_post_res);
return plug_post_res;
}
// Send result
audit_log!(au, "Delete operation success");
@ -728,7 +734,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
// Clone a set of writeables.
// Apply the modlist -> Remember, we have a set of origs
// and the new modified ents.
let candidates: Vec<Entry<EntryInvalid, EntryCommitted>> = pre_candidates
let mut candidates: Vec<Entry<EntryInvalid, EntryCommitted>> = pre_candidates
.into_iter()
.map(|er| {
// TODO: Deal with this properly william
@ -739,38 +745,16 @@ impl<'a> QueryServerWriteTransaction<'a> {
audit_log!(au, "modify: candidates -> {:?}", candidates);
// Pre mod plugins
let mut audit_plugin_pre = AuditScope::new("plugin_pre_modify");
let plug_pre_res =
Plugins::run_pre_modify(&mut audit_plugin_pre, &self, &mut candidates, me);
au.append_scope(audit_plugin_pre);
// Normalise all the data now it's validated.
// FIXME: This normalisation COPIES everything, which may be
// slow.
/*
let (norm_cand, invalid_cand): (
Vec<Result<Entry<EntryValid, EntryCommitted>, _>>,
Vec<Result<_, SchemaError>>,
) = candidates
.into_iter()
.map(|e| e.validate(&self.schema))
.partition(|e| e.is_ok());
for err in invalid_cand.iter() {
audit_log!(au, "Schema Violation: {:?}", err);
if plug_pre_res.is_err() {
audit_log!(au, "Modify operation failed (plugin), {:?}", plug_pre_res);
return plug_pre_res;
}
// TODO: Make this better
for err in invalid_cand.iter() {
return Err(OperationError::SchemaViolation(err.unwrap_err()));
}
let norm_cand: Vec<Entry<EntryValid, EntryCommitted>> = norm_cand
.into_iter()
.map(|e| match e {
Ok(v) => v,
Err(_) => panic!("Invalid data set state!!!"),
})
.collect();
*/
let res: Result<Vec<Entry<EntryValid, EntryCommitted>>, SchemaError> = candidates
.into_iter()
.map(|e| e.validate(&self.schema))
@ -803,6 +787,14 @@ impl<'a> QueryServerWriteTransaction<'a> {
}
// Post Plugins
let mut audit_plugin_post = AuditScope::new("plugin_post_modify");
let plug_post_res = Plugins::run_post_modify(&mut audit_plugin_post, &self, &norm_cand, me);
au.append_scope(audit_plugin_post);
if plug_post_res.is_err() {
audit_log!(au, "Modify operation failed (plugin), {:?}", plug_post_res);
return plug_post_res;
}
// return
audit_log!(au, "Modify operation success");