Skip to content

Commit b194e29

Browse files
committed
factors: Add VariablesFactor runtime config
Signed-off-by: Lann Martin <[email protected]>
1 parent 4b4e7a5 commit b194e29

File tree

13 files changed

+255
-176
lines changed

13 files changed

+255
-176
lines changed

Cargo.lock

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

crates/expressions/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ use std::{borrow::Cow, collections::HashMap, fmt::Debug};
55

66
use spin_locked_app::Variable;
77

8+
pub use async_trait;
9+
810
pub use provider::Provider;
911
use template::Part;
1012
pub use template::Template;
@@ -251,6 +253,14 @@ impl<'a> Key<'a> {
251253
}
252254
}
253255

256+
impl<'a> TryFrom<&'a str> for Key<'a> {
257+
type Error = Error;
258+
259+
fn try_from(value: &'a str) -> std::prelude::v1::Result<Self, Self::Error> {
260+
Self::new(value)
261+
}
262+
}
263+
254264
impl<'a> AsRef<str> for Key<'a> {
255265
fn as_ref(&self) -> &str {
256266
self.0

crates/factor-outbound-networking/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,12 @@ impl Factor for OutboundNetworkingFactor {
9191
}
9292
}
9393

94-
#[derive(Default)]
9594
pub struct AppState {
9695
component_allowed_hosts: HashMap<String, Arc<[String]>>,
9796
}
9897

9998
type SharedFutureResult<T> = Shared<BoxFuture<'static, Arc<anyhow::Result<T>>>>;
10099

101-
#[derive(Default)]
102100
pub struct InstanceBuilder {
103101
allowed_hosts_future: Option<SharedFutureResult<AllowedHostsConfig>>,
104102
}

crates/factor-variables/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ authors = { workspace = true }
55
edition = { workspace = true }
66

77
[dependencies]
8+
serde = { version = "1.0", features = ["rc"] }
89
spin-expressions = { path = "../expressions" }
910
spin-factors = { path = "../factors" }
1011
spin-world = { path = "../world" }
12+
toml = "0.8"
1113

1214
[lints]
1315
workspace = true

crates/factor-variables/src/lib.rs

Lines changed: 72 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,44 @@
1-
use std::sync::Arc;
1+
mod provider_type;
22

3+
use std::{collections::HashMap, sync::Arc};
4+
5+
use provider_type::{provider_maker, ProviderMaker};
6+
use serde::Deserialize;
37
use spin_expressions::ProviderResolver;
48
use spin_factors::{
5-
anyhow, ConfigureAppContext, Factor, FactorInstanceBuilder, InitContext, InstanceBuilders,
6-
PrepareContext, RuntimeFactors,
9+
anyhow::{self, bail, Context},
10+
ConfigureAppContext, Factor, FactorRuntimeConfig, InitContext, InstanceBuilders,
11+
PrepareContext, RuntimeFactors, SelfInstanceBuilder,
712
};
813
use spin_world::{async_trait, v1::config as v1_config, v2::variables};
914

10-
pub struct VariablesFactor;
15+
pub use provider_type::{StaticVariables, VariablesProviderType};
16+
17+
#[derive(Default)]
18+
pub struct VariablesFactor {
19+
provider_types: HashMap<&'static str, ProviderMaker>,
20+
}
21+
22+
impl VariablesFactor {
23+
pub fn add_provider_type<T: VariablesProviderType>(
24+
&mut self,
25+
provider_type: T,
26+
) -> anyhow::Result<()> {
27+
if self
28+
.provider_types
29+
.insert(T::TYPE, provider_maker(provider_type))
30+
.is_some()
31+
{
32+
bail!("duplicate provider type {:?}", T::TYPE);
33+
}
34+
Ok(())
35+
}
36+
}
1137

1238
impl Factor for VariablesFactor {
13-
type RuntimeConfig = ();
39+
type RuntimeConfig = RuntimeConfig;
1440
type AppState = AppState;
15-
type InstanceBuilder = InstanceBuilder;
41+
type InstanceBuilder = InstanceState;
1642

1743
fn init<Factors: RuntimeFactors>(
1844
&mut self,
@@ -25,18 +51,30 @@ impl Factor for VariablesFactor {
2551

2652
fn configure_app<T: RuntimeFactors>(
2753
&self,
28-
ctx: ConfigureAppContext<T, Self>,
54+
mut ctx: ConfigureAppContext<T, Self>,
2955
) -> anyhow::Result<Self::AppState> {
3056
let app = ctx.app();
3157
let mut resolver =
3258
ProviderResolver::new(app.variables().map(|(key, val)| (key.clone(), val.clone())))?;
59+
3360
for component in app.components() {
3461
resolver.add_component_variables(
3562
component.id(),
3663
component.config().map(|(k, v)| (k.into(), v.into())),
3764
)?;
3865
}
39-
// TODO: add providers from runtime config
66+
67+
if let Some(runtime_config) = ctx.take_runtime_config() {
68+
for ProviderConfig { type_, config } in runtime_config.provider_configs {
69+
let provider_maker = self
70+
.provider_types
71+
.get(type_.as_str())
72+
.with_context(|| format!("unknown variables provider type {type_}"))?;
73+
let provider = provider_maker(config)?;
74+
resolver.add_provider(provider);
75+
}
76+
}
77+
4078
Ok(AppState {
4179
resolver: Arc::new(resolver),
4280
})
@@ -45,47 +83,51 @@ impl Factor for VariablesFactor {
4583
fn prepare<T: RuntimeFactors>(
4684
ctx: PrepareContext<Self>,
4785
_builders: &mut InstanceBuilders<T>,
48-
) -> anyhow::Result<InstanceBuilder> {
86+
) -> anyhow::Result<InstanceState> {
4987
let component_id = ctx.app_component().id().to_string();
5088
let resolver = ctx.app_state().resolver.clone();
51-
Ok(InstanceBuilder {
52-
state: InstanceState {
53-
component_id,
54-
resolver,
55-
},
89+
Ok(InstanceState {
90+
component_id,
91+
resolver,
5692
})
5793
}
5894
}
5995

60-
#[derive(Default)]
61-
pub struct AppState {
62-
resolver: Arc<ProviderResolver>,
96+
#[derive(Deserialize)]
97+
#[serde(transparent)]
98+
pub struct RuntimeConfig {
99+
provider_configs: Vec<ProviderConfig>,
63100
}
64101

65-
pub struct InstanceBuilder {
66-
state: InstanceState,
102+
impl FactorRuntimeConfig for RuntimeConfig {
103+
const KEY: &'static str = "variable_provider";
67104
}
68105

69-
impl InstanceBuilder {
70-
pub fn resolver(&self) -> &Arc<ProviderResolver> {
71-
&self.state.resolver
72-
}
106+
#[derive(Deserialize)]
107+
struct ProviderConfig {
108+
#[serde(rename = "type")]
109+
type_: String,
110+
#[serde(flatten)]
111+
config: toml::Table,
73112
}
74113

75-
impl FactorInstanceBuilder for InstanceBuilder {
76-
type InstanceState = InstanceState;
77-
78-
fn build(self) -> anyhow::Result<Self::InstanceState> {
79-
Ok(self.state)
80-
}
114+
pub struct AppState {
115+
resolver: Arc<ProviderResolver>,
81116
}
82117

83-
#[derive(Default)]
84118
pub struct InstanceState {
85119
component_id: String,
86120
resolver: Arc<ProviderResolver>,
87121
}
88122

123+
impl InstanceState {
124+
pub fn resolver(&self) -> &Arc<ProviderResolver> {
125+
&self.resolver
126+
}
127+
}
128+
129+
impl SelfInstanceBuilder for InstanceState {}
130+
89131
#[async_trait]
90132
impl variables::Host for InstanceState {
91133
async fn get(&mut self, key: String) -> Result<String, variables::Error> {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use std::{collections::HashMap, sync::Arc};
2+
3+
use serde::{de::DeserializeOwned, Deserialize};
4+
use spin_expressions::{async_trait::async_trait, Key, Provider};
5+
use spin_factors::anyhow;
6+
7+
pub trait VariablesProviderType: 'static {
8+
const TYPE: &'static str;
9+
10+
type RuntimeConfig: DeserializeOwned;
11+
type Provider: Provider;
12+
13+
fn make_provider(&self, runtime_config: Self::RuntimeConfig) -> anyhow::Result<Self::Provider>;
14+
}
15+
16+
pub(crate) type ProviderMaker = Box<dyn Fn(toml::Table) -> anyhow::Result<Box<dyn Provider>>>;
17+
18+
pub(crate) fn provider_maker<T: VariablesProviderType>(provider_type: T) -> ProviderMaker {
19+
Box::new(move |table| {
20+
let runtime_config: T::RuntimeConfig = table.try_into()?;
21+
let provider = provider_type.make_provider(runtime_config)?;
22+
Ok(Box::new(provider))
23+
})
24+
}
25+
26+
pub struct StaticVariables;
27+
28+
impl VariablesProviderType for StaticVariables {
29+
const TYPE: &'static str = "static";
30+
31+
type RuntimeConfig = StaticVariablesProvider;
32+
type Provider = StaticVariablesProvider;
33+
34+
fn make_provider(&self, runtime_config: Self::RuntimeConfig) -> anyhow::Result<Self::Provider> {
35+
Ok(runtime_config)
36+
}
37+
}
38+
39+
#[derive(Debug, Deserialize)]
40+
pub struct StaticVariablesProvider {
41+
values: Arc<HashMap<String, String>>,
42+
}
43+
44+
#[async_trait]
45+
impl Provider for StaticVariablesProvider {
46+
async fn get(&self, key: &Key) -> anyhow::Result<Option<String>> {
47+
Ok(self.values.get(key.as_str()).cloned())
48+
}
49+
}

crates/factors-derive/src/lib.rs

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ fn expand_factors(input: &DeriveInput) -> syn::Result<TokenStream> {
104104
)?
105105
);
106106
)*
107+
runtime_config_tracker.validate_all_keys_used()?;
107108
Ok(#ConfiguredApp::new(app, app_state))
108109
}
109110

@@ -142,32 +143,29 @@ fn expand_factors(input: &DeriveInput) -> syn::Result<TokenStream> {
142143
type InstanceBuilders = #builders_name;
143144
type InstanceState = #state_name;
144145

145-
unsafe fn instance_builder_offset<T: #Factor>() -> Option<usize> {
146-
let type_id = #TypeId::of::<T>();
146+
fn app_state<F: #Factor>(app_state: &Self::AppState) -> Option<&F::AppState> {
147+
let type_id = #TypeId::of::<F>();
147148
#(
148149
if type_id == #TypeId::of::<#factor_types>() {
149-
return Some(std::mem::offset_of!(Self::InstanceBuilders, #factor_names));
150+
unsafe {
151+
return Some(::std::mem::transmute(&app_state.#factor_names));
152+
}
150153
}
151154
)*
152155
None
153156
}
154157

155-
unsafe fn instance_state_offset<T: #Factor>() -> Option<usize> {
156-
let type_id = #TypeId::of::<T>();
158+
fn instance_builder_mut<F: #Factor>(
159+
builders: &mut Self::InstanceBuilders,
160+
) -> Option<Option<&mut F::InstanceBuilder>> {
161+
let type_id = #TypeId::of::<F>();
157162
#(
158163
if type_id == #TypeId::of::<#factor_types>() {
159-
return Some(std::mem::offset_of!(Self::InstanceState, #factor_names));
160-
}
161-
)*
162-
None
163-
164-
}
165-
166-
fn app_state<T: #Factor>(app_state: &Self::AppState) -> Option<&T::AppState> {
167-
let type_id = #TypeId::of::<T>();
168-
#(
169-
if type_id == #TypeId::of::<#factor_types>() {
170-
return Some(unsafe { std::mem::transmute(&app_state.#factor_names) });
164+
return Some(
165+
builders.#factor_names.as_mut().map(|builder| {
166+
unsafe { ::std::mem::transmute(builder) }
167+
})
168+
);
171169
}
172170
)*
173171
None

crates/factors/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ spin-factors-derive = { path = "../factors-derive", features = ["expander"] }
1919
spin-factor-outbound-networking = { path = "../factor-outbound-networking" }
2020
spin-factor-variables = { path = "../factor-variables" }
2121
spin-factor-wasi = { path = "../factor-wasi" }
22+
tokio = { version = "1", features = ["macros", "rt"] }
23+
toml = "0.8"
2224

2325
[lints]
2426
workspace = true

crates/factors/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub use spin_factors_derive::RuntimeFactors;
1010

1111
pub use crate::{
1212
factor::{ConfigureAppContext, ConfiguredApp, Factor, InitContext},
13-
prepare::{FactorInstanceBuilder, InstanceBuilders, PrepareContext},
13+
prepare::{FactorInstanceBuilder, InstanceBuilders, PrepareContext, SelfInstanceBuilder},
1414
runtime_config::{FactorRuntimeConfig, RuntimeConfigSource},
1515
runtime_factors::RuntimeFactors,
1616
};

0 commit comments

Comments
 (0)