Skip to main content

spin_factor_outbound_redis/
lib.rs

1mod allowed_hosts;
2mod host;
3pub mod runtime_config;
4
5use host::InstanceState;
6use runtime_config::RuntimeConfig;
7use spin_factor_otel::OtelFactorState;
8use spin_factor_outbound_networking::{
9    ConnectionSemaphore, OutboundNetworkingFactor, build_connection_semaphore,
10};
11use spin_factors::{
12    ConfigureAppContext, Factor, FactorData, PrepareContext, RuntimeFactors, SelfInstanceBuilder,
13    anyhow,
14};
15use spin_world::spin::redis::redis as v3;
16
17use crate::allowed_hosts::AllowedHostChecker;
18
19/// The [`Factor`] for `fermyon:spin/outbound-redis`.
20#[derive(Default)]
21pub struct OutboundRedisFactor {
22    _priv: (),
23}
24
25impl OutboundRedisFactor {
26    pub fn new() -> Self {
27        Self::default()
28    }
29}
30
31pub struct AppState {
32    /// Semaphore to limit concurrent outbound Redis connections.
33    pub semaphore: ConnectionSemaphore,
34}
35
36impl Factor for OutboundRedisFactor {
37    type RuntimeConfig = RuntimeConfig;
38    type AppState = AppState;
39    type InstanceBuilder = InstanceState;
40
41    fn init<T: spin_factors::InitContext<Self>>(&mut self, ctx: &mut T) -> anyhow::Result<()> {
42        ctx.link_bindings(spin_world::v1::redis::add_to_linker::<_, FactorData<Self>>)?;
43        ctx.link_bindings(spin_world::v2::redis::add_to_linker::<_, FactorData<Self>>)?;
44        ctx.link_bindings(v3::add_to_linker::<_, RedisFactorData>)?;
45        Ok(())
46    }
47
48    fn configure_app<T: RuntimeFactors>(
49        &self,
50        mut ctx: ConfigureAppContext<T, Self>,
51    ) -> anyhow::Result<Self::AppState> {
52        let config = ctx.take_runtime_config().unwrap_or_default();
53        let networking = ctx.app_state::<OutboundNetworkingFactor>().ok();
54
55        Ok(AppState {
56            semaphore: build_connection_semaphore(
57                networking,
58                "redis",
59                config.max_connections,
60                config.wait_timeout,
61            ),
62        })
63    }
64
65    fn prepare<T: RuntimeFactors>(
66        &self,
67        mut ctx: PrepareContext<T, Self>,
68    ) -> anyhow::Result<Self::InstanceBuilder> {
69        let otel = OtelFactorState::from_prepare_context(&mut ctx)?;
70        let outbound_networking = ctx.instance_builder::<OutboundNetworkingFactor>()?;
71
72        Ok(InstanceState {
73            allowed_host_checker: AllowedHostChecker::new(outbound_networking.allowed_hosts()),
74            blocked_networks: outbound_networking.blocked_networks(),
75            connections: spin_resource_table::Table::new(1024),
76            semaphore: ctx.app_state().semaphore.clone(),
77            otel,
78        })
79    }
80}
81
82impl SelfInstanceBuilder for InstanceState {}
83
84pub struct RedisFactorData(OutboundRedisFactor);
85
86impl spin_core::wasmtime::component::HasData for RedisFactorData {
87    type Data<'a> = &'a mut InstanceState;
88}