-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
Merge pull request #396 from flavio/on-demand
feat: introduce `PolicyEvaluatorPre`
- v0.20.0
- v0.19.11
- v0.19.10
- v0.19.9
- v0.19.8
- v0.19.7
- v0.19.6
- v0.19.5
- v0.19.4
- v0.19.3
- v0.19.2
- v0.19.1
- v0.19.0
- v0.18.8
- v0.18.7
- v0.18.6
- v0.18.5
- v0.18.4
- v0.18.3
- v0.18.2
- v0.18.1
- v0.18.0
- v0.17.7
- v0.17.6
- v0.17.5
- v0.17.4
- v0.17.3
- v0.17.2
- v0.17.1
- v0.17.0
- v0.16.4
- v0.16.3
- v0.16.2
- v0.16.1
- v0.16.0
- v0.15.0
- v0.14.2
- v0.14.1
- v0.14.0
- v0.13.3
- v0.13.2
- v0.13.1
- v0.13.0
Showing
21 changed files
with
680 additions
and
600 deletions.
There are no files selected for viewing
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
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
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,71 @@ | ||
use anyhow::Result; | ||
|
||
use crate::evaluation_context::EvaluationContext; | ||
use crate::policy_evaluator::PolicyEvaluator; | ||
use crate::runtimes::{rego, wapc, wasi_cli, Runtime}; | ||
|
||
/// Holds pre-initialized stacks for all the types of policies we run | ||
/// | ||
/// Pre-initialized instances are key to reduce the evaluation time when | ||
/// using on-demand PolicyEvaluator instances; where on-demand means that | ||
/// each validation request has a brand new PolicyEvaluator that is discarded | ||
/// once the evaluation is done. | ||
pub(crate) enum StackPre { | ||
Wapc(crate::runtimes::wapc::StackPre), | ||
Wasi(crate::runtimes::wasi_cli::StackPre), | ||
Rego(crate::runtimes::rego::StackPre), | ||
} | ||
|
||
impl From<wapc::StackPre> for StackPre { | ||
fn from(wapc_stack_pre: wapc::StackPre) -> Self { | ||
StackPre::Wapc(wapc_stack_pre) | ||
} | ||
} | ||
|
||
impl From<wasi_cli::StackPre> for StackPre { | ||
fn from(wasi_stack_pre: wasi_cli::StackPre) -> Self { | ||
StackPre::Wasi(wasi_stack_pre) | ||
} | ||
} | ||
|
||
impl From<rego::StackPre> for StackPre { | ||
fn from(rego_stack_pre: rego::StackPre) -> Self { | ||
StackPre::Rego(rego_stack_pre) | ||
} | ||
} | ||
|
||
/// This struct provides a way to quickly allocate a `PolicyEvaluator` | ||
/// object. | ||
/// | ||
/// See the [`rehydrate`](PolicyEvaluatorPre::rehydrate) method. | ||
pub struct PolicyEvaluatorPre { | ||
pub(crate) stack_pre: StackPre, | ||
} | ||
|
||
impl PolicyEvaluatorPre { | ||
/// Create a `PolicyEvaluator` instance. The creation of the instance is achieved by | ||
/// using wasmtime low level primitives (like `wasmtime::InstancePre`) to make the operation | ||
/// as fast as possible. | ||
/// | ||
/// Warning: the Rego stack cannot make use of these low level primitives, but its | ||
/// instantiation times are negligible. More details inside of the | ||
/// documentation of [`rego::StackPre`](crate::rego::stack_pre::StackPre). | ||
pub fn rehydrate(&self, eval_ctx: &EvaluationContext) -> Result<PolicyEvaluator> { | ||
let runtime = match &self.stack_pre { | ||
StackPre::Wapc(stack_pre) => { | ||
let wapc_stack = wapc::WapcStack::new_from_pre(stack_pre, eval_ctx)?; | ||
Runtime::Wapc(wapc_stack) | ||
} | ||
StackPre::Wasi(stack_pre) => { | ||
let wasi_stack = wasi_cli::Stack::new_from_pre(stack_pre); | ||
Runtime::Cli(wasi_stack) | ||
} | ||
StackPre::Rego(stack_pre) => { | ||
let rego_stack = rego::Stack::new_from_pre(stack_pre)?; | ||
Runtime::Rego(rego_stack) | ||
} | ||
}; | ||
|
||
Ok(PolicyEvaluator::new(runtime)) | ||
} | ||
} |
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
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
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,56 @@ | ||
use anyhow::Result; | ||
|
||
use crate::policy_evaluator::RegoPolicyExecutionMode; | ||
use crate::policy_evaluator_builder::EpochDeadlines; | ||
|
||
/// This struct allows to follow the `StackPre -> Stack` | ||
/// "pattern" also for Rego policies. | ||
/// | ||
/// However, Rego policies cannot make use of `wasmtime::InstancePre` | ||
/// to reduce the instantiation times. That happens because all | ||
/// Rego WebAssembly policies import their Wasm Memory from the host. | ||
/// The Wasm Memory is defined inside of a `wasmtime::Store`, which is | ||
/// something that `wasmtime::InstnacePre` objects do not have (rightfully!). | ||
/// | ||
/// However, Rego Wasm modules are so small thta instantiating them from scratch | ||
/// is already a cheap operation. | ||
#[derive(Clone)] | ||
pub(crate) struct StackPre { | ||
engine: wasmtime::Engine, | ||
module: wasmtime::Module, | ||
epoch_deadlines: Option<EpochDeadlines>, | ||
pub entrypoint_id: i32, | ||
pub policy_execution_mode: RegoPolicyExecutionMode, | ||
} | ||
|
||
impl StackPre { | ||
pub(crate) fn new( | ||
engine: wasmtime::Engine, | ||
module: wasmtime::Module, | ||
epoch_deadlines: Option<EpochDeadlines>, | ||
entrypoint_id: i32, | ||
policy_execution_mode: RegoPolicyExecutionMode, | ||
) -> Self { | ||
Self { | ||
engine, | ||
module, | ||
epoch_deadlines, | ||
entrypoint_id, | ||
policy_execution_mode, | ||
} | ||
} | ||
|
||
/// Create a fresh `burrego::Evaluator` | ||
pub(crate) fn rehydrate(&self) -> Result<burrego::Evaluator> { | ||
let mut builder = burrego::EvaluatorBuilder::default() | ||
.engine(&self.engine) | ||
.module(self.module.clone()) | ||
.host_callbacks(crate::runtimes::rego::new_host_callbacks()); | ||
|
||
if let Some(deadlines) = self.epoch_deadlines { | ||
builder = builder.enable_epoch_interruptions(deadlines.wapc_func); | ||
} | ||
let evaluator = builder.build()?; | ||
Ok(evaluator) | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
mod callback; | ||
mod runtime; | ||
mod stack; | ||
mod stack_pre; | ||
|
||
pub(crate) use runtime::Runtime; | ||
pub(crate) use stack::WapcStack; | ||
pub(crate) use stack_pre::StackPre; |
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
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,36 @@ | ||
use anyhow::Result; | ||
use wasmtime_provider::wasmtime; | ||
|
||
use crate::policy_evaluator_builder::EpochDeadlines; | ||
|
||
/// Reduce allocation time of new `WasmtimeProviderEngine`, see the `rehydrate` method | ||
#[derive(Clone)] | ||
pub(crate) struct StackPre { | ||
engine_provider_pre: wasmtime_provider::WasmtimeEngineProviderPre, | ||
} | ||
|
||
impl StackPre { | ||
pub(crate) fn new( | ||
engine: wasmtime::Engine, | ||
module: wasmtime::Module, | ||
epoch_deadlines: Option<EpochDeadlines>, | ||
) -> Result<Self> { | ||
let mut builder = wasmtime_provider::WasmtimeEngineProviderBuilder::new() | ||
.engine(engine) | ||
.module(module); | ||
if let Some(deadlines) = epoch_deadlines { | ||
builder = builder.enable_epoch_interruptions(deadlines.wapc_init, deadlines.wapc_func); | ||
} | ||
|
||
let engine_provider_pre = builder.build_pre()?; | ||
Ok(Self { | ||
engine_provider_pre, | ||
}) | ||
} | ||
|
||
/// Allocate a new `WasmtimeEngineProvider` instance by using a pre-allocated instance | ||
pub(crate) fn rehydrate(&self) -> Result<wasmtime_provider::WasmtimeEngineProvider> { | ||
let engine = self.engine_provider_pre.rehydrate()?; | ||
Ok(engine) | ||
} | ||
} |
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 |
---|---|---|
@@ -1,6 +1,8 @@ | ||
mod errors; | ||
mod runtime; | ||
mod stack; | ||
mod stack_pre; | ||
|
||
pub(crate) use runtime::Runtime; | ||
pub(crate) use stack::Stack; | ||
pub(crate) use stack_pre::StackPre; |
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 |
---|---|---|
@@ -1,34 +1,108 @@ | ||
use anyhow::Result; | ||
use std::io::Cursor; | ||
use wasi_common::pipe::{ReadPipe, WritePipe}; | ||
use wasi_common::WasiCtx; | ||
use wasmtime::{Engine, InstancePre, Linker, Module}; | ||
use wasmtime_wasi::sync::WasiCtxBuilder; | ||
|
||
use crate::policy_evaluator_builder::EpochDeadlines; | ||
use crate::runtimes::wasi_cli::{errors::WasiRuntimeError, stack_pre::StackPre}; | ||
|
||
const EXIT_SUCCESS: i32 = 0; | ||
|
||
pub(crate) struct Context { | ||
pub(crate) wasi_ctx: WasiCtx, | ||
} | ||
|
||
pub(crate) struct Stack { | ||
pub(crate) engine: Engine, | ||
pub(crate) epoch_deadlines: Option<EpochDeadlines>, | ||
pub(crate) instance_pre: InstancePre<Context>, | ||
stack_pre: StackPre, | ||
} | ||
|
||
pub(crate) struct RunResult { | ||
pub stdout: String, | ||
pub stderr: String, | ||
} | ||
|
||
impl Stack { | ||
pub(crate) fn new( | ||
engine: Engine, | ||
module: Module, | ||
epoch_deadlines: Option<EpochDeadlines>, | ||
) -> Result<Self> { | ||
let mut linker = Linker::<Context>::new(&engine); | ||
wasmtime_wasi::add_to_linker(&mut linker, |c: &mut Context| &mut c.wasi_ctx)?; | ||
|
||
let instance_pre = linker.instantiate_pre(&module)?; | ||
|
||
Ok(Stack { | ||
engine, | ||
instance_pre, | ||
epoch_deadlines, | ||
}) | ||
pub(crate) fn new_from_pre(stack_pre: &StackPre) -> Self { | ||
Self { | ||
stack_pre: stack_pre.to_owned(), | ||
} | ||
} | ||
|
||
/// Run a WASI program with the given input and args | ||
pub(crate) fn run( | ||
&self, | ||
input: &[u8], | ||
args: &[&str], | ||
) -> std::result::Result<RunResult, WasiRuntimeError> { | ||
let stdout_pipe = WritePipe::new_in_memory(); | ||
let stderr_pipe = WritePipe::new_in_memory(); | ||
let stdin_pipe = ReadPipe::new(Cursor::new(input.to_owned())); | ||
|
||
let args: Vec<String> = args.iter().map(|s| s.to_string()).collect(); | ||
|
||
let wasi_ctx = WasiCtxBuilder::new() | ||
.args(&args)? | ||
.stdin(Box::new(stdin_pipe)) | ||
.stdout(Box::new(stdout_pipe.clone())) | ||
.stderr(Box::new(stderr_pipe.clone())) | ||
.build(); | ||
let ctx = Context { wasi_ctx }; | ||
|
||
let mut store = self.stack_pre.build_store(ctx); | ||
let instance = self | ||
.stack_pre | ||
.rehydrate(&mut store) | ||
.map_err(WasiRuntimeError::WasmInstantiate)?; | ||
let start_fn = instance | ||
.get_typed_func::<(), ()>(&mut store, "_start") | ||
.map_err(WasiRuntimeError::WasmMissingStartFn)?; | ||
let evaluation_result = start_fn.call(&mut store, ()); | ||
|
||
// Dropping the store, this is no longer needed, plus it's keeping | ||
// references to the WritePipe(s) that we need exclusive access to. | ||
drop(store); | ||
|
||
let stderr = pipe_to_string("stderr", stderr_pipe)?; | ||
|
||
if let Err(err) = evaluation_result { | ||
if let Some(exit_error) = err.downcast_ref::<wasmtime_wasi::I32Exit>() { | ||
if exit_error.0 == EXIT_SUCCESS { | ||
let stdout = pipe_to_string("stdout", stdout_pipe)?; | ||
return Ok(RunResult { stdout, stderr }); | ||
} else { | ||
return Err(WasiRuntimeError::WasiEvaluation { | ||
code: Some(exit_error.0), | ||
stderr, | ||
error: err, | ||
}); | ||
} | ||
} | ||
return Err(WasiRuntimeError::WasiEvaluation { | ||
code: None, | ||
stderr, | ||
error: err, | ||
}); | ||
} | ||
|
||
let stdout = pipe_to_string("stdout", stdout_pipe)?; | ||
Ok(RunResult { stdout, stderr }) | ||
} | ||
} | ||
|
||
fn pipe_to_string( | ||
name: &str, | ||
pipe: WritePipe<Cursor<Vec<u8>>>, | ||
) -> std::result::Result<String, WasiRuntimeError> { | ||
match pipe.try_into_inner() { | ||
Ok(cursor) => { | ||
let buf = cursor.into_inner(); | ||
String::from_utf8(buf).map_err(|e| WasiRuntimeError::PipeConversion { | ||
name: name.to_string(), | ||
error: format!("Cannot convert buffer to UTF8 string: {e}"), | ||
}) | ||
} | ||
Err(_) => Err(WasiRuntimeError::PipeConversion { | ||
name: name.to_string(), | ||
error: "cannot convert pipe into inner".to_string(), | ||
}), | ||
} | ||
} |
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,50 @@ | ||
use anyhow::Result; | ||
|
||
use crate::policy_evaluator_builder::EpochDeadlines; | ||
use crate::runtimes::wasi_cli::stack::Context; | ||
|
||
/// Reduce the allocation time of a Wasi Stack. This is done by leveraging `wasmtime::InstancePre`. | ||
#[derive(Clone)] | ||
pub(crate) struct StackPre { | ||
engine: wasmtime::Engine, | ||
instance_pre: wasmtime::InstancePre<Context>, | ||
epoch_deadlines: Option<EpochDeadlines>, | ||
} | ||
|
||
impl StackPre { | ||
pub(crate) fn new( | ||
engine: wasmtime::Engine, | ||
module: wasmtime::Module, | ||
epoch_deadlines: Option<EpochDeadlines>, | ||
) -> Result<Self> { | ||
let mut linker = wasmtime::Linker::<Context>::new(&engine); | ||
wasmtime_wasi::add_to_linker(&mut linker, |c: &mut Context| &mut c.wasi_ctx)?; | ||
|
||
let instance_pre = linker.instantiate_pre(&module)?; | ||
Ok(Self { | ||
engine, | ||
instance_pre, | ||
epoch_deadlines, | ||
}) | ||
} | ||
|
||
/// Create a brand new `wasmtime::Store` to be used during an evaluation | ||
pub(crate) fn build_store(&self, ctx: Context) -> wasmtime::Store<Context> { | ||
let mut store = wasmtime::Store::new(&self.engine, ctx); | ||
if let Some(deadline) = self.epoch_deadlines { | ||
store.set_epoch_deadline(deadline.wapc_func); | ||
} | ||
|
||
store | ||
} | ||
|
||
/// Allocate a new `wasmtime::Instance` that is bound to the given `wasmtime::Store`. | ||
/// It's recommended to provide a brand new `wasmtime::Store` created by the | ||
/// `build_store` method | ||
pub(crate) fn rehydrate( | ||
&self, | ||
store: &mut wasmtime::Store<Context>, | ||
) -> Result<wasmtime::Instance> { | ||
self.instance_pre.instantiate(store) | ||
} | ||
} |