Skip to main content

spin_factor_variables/
host.rs

1use spin_core::wasmtime::component::Accessor;
2use spin_factors::anyhow;
3use spin_telemetry::traces::{self, Blame};
4use spin_world::{
5    spin::variables::variables as v3, v1, v2::variables as v2, wasi::config as wasi_config,
6};
7use tracing::instrument;
8
9use crate::{InstanceState, VariablesFactorData};
10
11impl v3::HostWithStore for VariablesFactorData {
12    #[instrument(name = "spin_variables.get", skip(accessor), fields(otel.kind = "client"))]
13    async fn get<T: Send>(accessor: &Accessor<T, Self>, key: String) -> Result<String, v3::Error> {
14        let (resolver, component_id) = accessor.with(|mut access| {
15            let host = access.get();
16            host.otel.reparent_tracing_span();
17            (host.expression_resolver.clone(), host.component_id.clone())
18        });
19
20        let key = spin_expressions::Key::new(&key).map_err(expressions_to_variables_err_v3)?;
21
22        resolver
23            .resolve(&component_id, key)
24            .await
25            .map_err(expressions_to_variables_err_v3)
26    }
27}
28
29impl v3::Host for InstanceState {
30    fn convert_error(&mut self, err: v3::Error) -> anyhow::Result<v3::Error> {
31        Ok(err)
32    }
33}
34
35impl v2::Host for InstanceState {
36    #[instrument(name = "spin_variables.get", skip(self), fields(otel.kind = "client"))]
37    async fn get(&mut self, key: String) -> Result<String, v2::Error> {
38        self.otel.reparent_tracing_span();
39        let key = spin_expressions::Key::new(&key).map_err(expressions_to_variables_err)?;
40        self.expression_resolver
41            .resolve(&self.component_id, key)
42            .await
43            .map_err(expressions_to_variables_err)
44    }
45
46    fn convert_error(&mut self, error: v2::Error) -> anyhow::Result<v2::Error> {
47        Ok(error)
48    }
49}
50
51impl v1::config::Host for InstanceState {
52    #[instrument(name = "spin_config.get", skip(self), fields(otel.kind = "client"))]
53    async fn get_config(&mut self, key: String) -> Result<String, v1::config::Error> {
54        <Self as v2::Host>::get(self, key)
55            .await
56            .map_err(|err| match err {
57                v2::Error::InvalidName(msg) => v1::config::Error::InvalidKey(msg),
58                v2::Error::Undefined(msg) => v1::config::Error::Provider(msg),
59                other => v1::config::Error::Other(format!("{other}")),
60            })
61    }
62
63    fn convert_error(&mut self, err: v1::config::Error) -> anyhow::Result<v1::config::Error> {
64        Ok(err)
65    }
66}
67
68impl wasi_config::store::Host for InstanceState {
69    #[instrument(name = "wasi_config.get", skip(self), fields(otel.kind = "client"))]
70    async fn get(&mut self, key: String) -> Result<Option<String>, wasi_config::store::Error> {
71        match <Self as v2::Host>::get(self, key).await {
72            Ok(value) => Ok(Some(value)),
73            Err(v2::Error::Undefined(_)) => Ok(None),
74            Err(v2::Error::InvalidName(_)) => Ok(None), // this is the guidance from https://github.com/WebAssembly/wasi-runtime-config/pull/19)
75            Err(v2::Error::Provider(msg)) => Err(wasi_config::store::Error::Upstream(msg)),
76            Err(v2::Error::Other(msg)) => Err(wasi_config::store::Error::Io(msg)),
77        }
78    }
79
80    #[instrument(name = "wasi_config.get_all", skip(self), fields(otel.kind = "client"))]
81    async fn get_all(&mut self) -> Result<Vec<(String, String)>, wasi_config::store::Error> {
82        let all = self
83            .expression_resolver
84            .resolve_all(&self.component_id)
85            .await;
86        all.map_err(|e| {
87            match expressions_to_variables_err(e) {
88                v2::Error::Undefined(msg) => wasi_config::store::Error::Io(msg), // this shouldn't happen but just in case
89                v2::Error::InvalidName(msg) => wasi_config::store::Error::Io(msg), // this shouldn't happen but just in case
90                v2::Error::Provider(msg) => wasi_config::store::Error::Upstream(msg),
91                v2::Error::Other(msg) => wasi_config::store::Error::Io(msg),
92            }
93        })
94    }
95
96    fn convert_error(
97        &mut self,
98        err: wasi_config::store::Error,
99    ) -> anyhow::Result<wasi_config::store::Error> {
100        Ok(err)
101    }
102}
103
104/// Convert a `spin_expressions::Error` to a `v2::Error`, setting the current span's status and fault attribute.
105fn expressions_to_variables_err(err: spin_expressions::Error) -> v2::Error {
106    use spin_expressions::Error;
107    let blame = match err {
108        Error::InvalidName(_) | Error::InvalidTemplate(_) | Error::Undefined(_) => Blame::Guest,
109        Error::Provider(_) => Blame::Host,
110    };
111    traces::mark_as_error(&err, Some(blame));
112    match err {
113        Error::InvalidName(msg) => v2::Error::InvalidName(msg),
114        Error::Undefined(msg) => v2::Error::Undefined(msg),
115        Error::InvalidTemplate(_) => v2::Error::Other(format!("{err}")),
116        Error::Provider(err) => v2::Error::Provider(err.to_string()),
117    }
118}
119
120/// Convert a `spin_expressions::Error` to a `v3::Error`, setting the current span's status and fault attribute.
121fn expressions_to_variables_err_v3(err: spin_expressions::Error) -> v3::Error {
122    use spin_expressions::Error;
123    let blame = match err {
124        Error::InvalidName(_) | Error::InvalidTemplate(_) | Error::Undefined(_) => Blame::Guest,
125        Error::Provider(_) => Blame::Host,
126    };
127    traces::mark_as_error(&err, Some(blame));
128    match err {
129        Error::InvalidName(msg) => v3::Error::InvalidName(msg),
130        Error::Undefined(msg) => v3::Error::Undefined(msg),
131        Error::InvalidTemplate(_) => v3::Error::Other(format!("{err}")),
132        Error::Provider(err) => v3::Error::Provider(err.to_string()),
133    }
134}