Skip to content

Commit c058a88

Browse files
committed
Adds new dynamism metadata for Providers
New logic only allows no default variable before runtime if Provider it is of a Dynamic type Signed-off-by: Aminu Oluwaseun Joshua <[email protected]>
1 parent d1eb9ac commit c058a88

File tree

14 files changed

+109
-6
lines changed

14 files changed

+109
-6
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/expressions/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ edition = { workspace = true }
88
anyhow = { workspace = true }
99
async-trait = { workspace = true }
1010
futures = { workspace = true }
11+
serde = { workspace = true }
1112
spin-locked-app = { path = "../locked-app" }
1213
thiserror = { workspace = true }
1314

crates/expressions/src/lib.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ pub use provider::Provider;
1111
use template::Part;
1212
pub use template::Template;
1313

14+
use crate::provider::ProviderVariableKind;
15+
1416
/// A [`ProviderResolver`] that can be shared.
1517
pub type SharedPreparedResolver =
1618
std::sync::Arc<std::sync::OnceLock<std::sync::Arc<PreparedResolver>>>;
@@ -90,6 +92,30 @@ impl ProviderResolver {
9092
Ok(PreparedResolver { variables })
9193
}
9294

95+
/// Prepares the resolver by attempting to resolve all variables, printing warnings for any
96+
/// that cannot be resolved.
97+
pub async fn pre_runtime_prepare(&self) -> Result<()> {
98+
for name in self.internal.variables.keys() {
99+
self.check_variable_existence(name).await?;
100+
}
101+
Ok(())
102+
}
103+
104+
async fn check_variable_existence(&self, key: &str) -> Result<()> {
105+
for provider in &self.providers {
106+
if provider.kind() == &ProviderVariableKind::Dynamic {
107+
continue;
108+
}
109+
110+
match provider.get(&Key(key)).await {
111+
Ok(Some(_)) => return Ok(()),
112+
Err(_) | Ok(None) => return self.internal.resolve_variable(key).map(|_| ()),
113+
}
114+
}
115+
116+
Ok(())
117+
}
118+
93119
async fn resolve_variable(&self, key: &str) -> Result<String> {
94120
for provider in &self.providers {
95121
if let Some(value) = provider.get(&Key(key)).await.map_err(Error::Provider)? {
@@ -310,6 +336,7 @@ mod tests {
310336
use async_trait::async_trait;
311337

312338
use super::*;
339+
use crate::provider::ProviderVariableKind;
313340

314341
#[derive(Debug)]
315342
struct TestProvider;
@@ -323,6 +350,10 @@ mod tests {
323350
_ => Ok(None),
324351
}
325352
}
353+
354+
fn kind(&self) -> &ProviderVariableKind {
355+
&ProviderVariableKind::Static
356+
}
326357
}
327358

328359
async fn test_resolve(template: &str) -> Result<String> {

crates/expressions/src/provider.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::fmt::Debug;
22

33
use async_trait::async_trait;
4+
use serde::Deserialize;
45

56
use crate::Key;
67

@@ -9,4 +10,15 @@ use crate::Key;
910
pub trait Provider: Debug + Send + Sync {
1011
/// Returns the value at the given config path, if it exists.
1112
async fn get(&self, key: &Key) -> anyhow::Result<Option<String>>;
13+
fn kind(&self) -> &ProviderVariableKind;
14+
}
15+
16+
/// The dynamism of a Provider.
17+
#[derive(Clone, Debug, Default, Deserialize, PartialEq)]
18+
pub enum ProviderVariableKind {
19+
/// Variable must be declared on start
20+
Static,
21+
/// Variable can be made available at runtime
22+
#[default]
23+
Dynamic,
1224
}

crates/factor-variables/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ impl AppState {
8585
let template = Template::new(expr)?;
8686
self.expression_resolver.resolve_template(&template).await
8787
}
88+
89+
pub fn expression_resolver(&self) -> &Arc<ExpressionResolver> {
90+
&self.expression_resolver
91+
}
8892
}
8993

9094
pub struct InstanceState {

crates/factor-variables/tests/factor_test.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use spin_expressions::{Key, Provider};
1+
use spin_expressions::{provider::ProviderVariableKind, Key, Provider};
22
use spin_factor_variables::{runtime_config::RuntimeConfig, VariablesFactor};
33
use spin_factors::{anyhow, RuntimeFactors};
44
use spin_factors_test::{toml, TestEnvironment};
@@ -46,4 +46,8 @@ impl Provider for MockProvider {
4646
_ => Ok(None),
4747
}
4848
}
49+
50+
fn kind(&self) -> &ProviderVariableKind {
51+
&ProviderVariableKind::Static
52+
}
4953
}

crates/runtime-factors/src/build.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use spin_runtime_config::ResolvedRuntimeConfig;
88
use spin_trigger::cli::{
99
FactorsConfig, InitialKvSetterHook, KeyValueDefaultStoreSummaryHook, MaxInstanceMemoryHook,
1010
RuntimeFactorsBuilder, SqlStatementExecutorHook, SqliteDefaultStoreSummaryHook,
11-
StdioLoggingExecutorHooks,
11+
StdioLoggingExecutorHooks, VariablesPreparationExecutorHook,
1212
};
1313
use spin_variables_static::StaticVariablesProvider;
1414

@@ -72,6 +72,7 @@ impl RuntimeFactorsBuilder for FactorsBuilder {
7272
executor.add_hooks(InitialKvSetterHook::new(args.key_values.clone()));
7373
executor.add_hooks(SqliteDefaultStoreSummaryHook);
7474
executor.add_hooks(KeyValueDefaultStoreSummaryHook);
75+
executor.add_hooks(VariablesPreparationExecutorHook);
7576

7677
let max_instance_memory = args
7778
.max_instance_memory

crates/trigger/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ spin-compose = { path = "../compose" }
2828
spin-core = { path = "../core" }
2929
spin-factor-key-value = { path = "../factor-key-value" }
3030
spin-factor-sqlite = { path = "../factor-sqlite" }
31+
spin-factor-variables = { path = "../factor-variables" }
3132
spin-factor-wasi = { path = "../factor-wasi" }
3233
spin-factors = { path = "../factors" }
3334
spin-factors-executor = { path = "../factors-executor" }

crates/trigger/src/cli.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod max_instance_memory;
44
mod sqlite_statements;
55
mod stdio;
66
mod summary;
7+
mod variable;
78

89
use std::path::PathBuf;
910
use std::{future::Future, sync::Arc};
@@ -25,6 +26,7 @@ pub use sqlite_statements::SqlStatementExecutorHook;
2526
use stdio::FollowComponents;
2627
pub use stdio::StdioLoggingExecutorHooks;
2728
pub use summary::{KeyValueDefaultStoreSummaryHook, SqliteDefaultStoreSummaryHook};
29+
pub use variable::VariablesPreparationExecutorHook;
2830

2931
pub const APP_LOG_DIR: &str = "APP_LOG_DIR";
3032
pub const SPIN_TRUNCATE_LOGS: &str = "SPIN_TRUNCATE_LOGS";

crates/trigger/src/cli/variable.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use spin_factor_variables::VariablesFactor;
2+
use spin_factors::RuntimeFactors;
3+
use spin_factors_executor::ExecutorHooks;
4+
5+
/// An executor hook that prepares the variables factor before runtime execution.
6+
pub struct VariablesPreparationExecutorHook;
7+
8+
#[spin_core::async_trait]
9+
impl<F: RuntimeFactors, U> ExecutorHooks<F, U> for VariablesPreparationExecutorHook {
10+
async fn configure_app(
11+
&self,
12+
configured_app: &spin_factors::ConfiguredApp<F>,
13+
) -> anyhow::Result<()> {
14+
let variables_factor = configured_app.app_state::<VariablesFactor>()?;
15+
16+
let expression_resolver = variables_factor.expression_resolver();
17+
expression_resolver.pre_runtime_prepare().await?;
18+
19+
Ok(())
20+
}
21+
}

0 commit comments

Comments
 (0)