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(&mut self, ctx: &mut impl spin_factors::InitContext<Self>) -> 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
54        Ok(AppState {
55            semaphore: build_connection_semaphore(
56                ctx.app_state::<OutboundNetworkingFactor>().ok(),
57                "redis",
58                config.max_connections,
59                config.wait_timeout,
60            ),
61        })
62    }
63
64    fn prepare<T: RuntimeFactors>(
65        &self,
66        mut ctx: PrepareContext<T, Self>,
67    ) -> anyhow::Result<Self::InstanceBuilder> {
68        let otel = OtelFactorState::from_prepare_context(&mut ctx)?;
69        let outbound_networking = ctx.instance_builder::<OutboundNetworkingFactor>()?;
70
71        Ok(InstanceState {
72            allowed_host_checker: AllowedHostChecker::new(outbound_networking.allowed_hosts()),
73            blocked_networks: outbound_networking.blocked_networks(),
74            connections: spin_resource_table::Table::new(1024),
75            semaphore: ctx.app_state().semaphore.clone(),
76            otel,
77        })
78    }
79}
80
81impl SelfInstanceBuilder for InstanceState {}
82
83pub struct RedisFactorData(OutboundRedisFactor);
84
85impl spin_core::wasmtime::component::HasData for RedisFactorData {
86    type Data<'a> = &'a mut InstanceState;
87}