viceroy_lib/
config.rs

1//! Fastly-specific configuration utilities.
2
3use {
4    self::{
5        acl::AclConfig, backends::BackendsConfig, dictionaries::DictionariesConfig,
6        object_store::ObjectStoreConfig, secret_store::SecretStoreConfig,
7    },
8    crate::error::FastlyConfigError,
9    serde_derive::Deserialize,
10    std::{collections::HashMap, convert::TryInto, fs, path::Path, str::FromStr, sync::Arc},
11    toml::value::Table,
12};
13
14/// Unit tests for the [`FastlyConfig`] and [`TestingConfig`] types.
15#[cfg(test)]
16mod unit_tests;
17
18/// Fastly limits
19mod limits;
20
21/// Types and deserializers for dictionaries configuration settings.
22mod dictionaries;
23
24pub use self::dictionaries::{Dictionary, LoadedDictionary};
25
26pub type Dictionaries = HashMap<String, Dictionary>;
27
28/// Types and deserializers for acl configuration settings.
29mod acl;
30pub use crate::acl::Acls;
31
32/// Types and deserializers for backend configuration settings.
33mod backends;
34
35pub use self::backends::{Backend, ClientCertError, ClientCertInfo};
36
37pub type Backends = HashMap<String, Arc<Backend>>;
38
39/// Types and deserializers for device detection configuration settings.
40mod device_detection;
41
42pub use self::device_detection::DeviceDetection;
43
44/// Types and deserializers for geolocation configuration settings.
45mod geolocation;
46
47pub use self::geolocation::Geolocation;
48
49/// Types and deserializers for object store configuration settings.
50mod object_store;
51
52pub use crate::object_store::ObjectStores;
53
54/// Types and deserializers for secret store configuration settings.
55mod secret_store;
56pub use crate::secret_store::{SecretStore, SecretStores};
57
58pub use crate::shielding_site::ShieldingSites;
59
60/// Fastly-specific configuration information.
61///
62/// This `struct` represents the fields and values in a Compute package's `fastly.toml`.
63#[derive(Debug, Clone)]
64pub struct FastlyConfig {
65    name: String,
66    description: String,
67    authors: Vec<String>,
68    language: String,
69    local_server: LocalServerConfig,
70}
71
72impl FastlyConfig {
73    /// Get a reference to the package name.
74    pub fn name(&self) -> &str {
75        self.name.as_str()
76    }
77
78    /// Get a reference to the package description.
79    pub fn description(&self) -> &str {
80        self.description.as_str()
81    }
82
83    /// Get a reference to the package authors.
84    pub fn authors(&self) -> &[String] {
85        self.authors.as_ref()
86    }
87
88    /// Get a reference to the package language.
89    pub fn language(&self) -> &str {
90        self.language.as_str()
91    }
92
93    /// Get the acl configuration.
94    pub fn acls(&self) -> &Acls {
95        &self.local_server.acls.0
96    }
97
98    /// Get the backend configuration.
99    pub fn backends(&self) -> &Backends {
100        &self.local_server.backends.0
101    }
102
103    /// Get the device detection configuration.
104    pub fn device_detection(&self) -> &DeviceDetection {
105        &self.local_server.device_detection
106    }
107
108    /// Get the geolocation configuration.
109    pub fn geolocation(&self) -> &Geolocation {
110        &self.local_server.geolocation
111    }
112
113    /// Get the dictionaries configuration.
114    pub fn dictionaries(&self) -> &Dictionaries {
115        &self.local_server.dictionaries.0
116    }
117
118    /// Get the object store configuration.
119    pub fn object_stores(&self) -> &ObjectStores {
120        &self.local_server.object_stores.0
121    }
122
123    /// Get the secret store configuration.
124    pub fn secret_stores(&self) -> &SecretStores {
125        &self.local_server.secret_stores.0
126    }
127    /// Get the shielding site configuration.
128    pub fn shielding_sites(&self) -> &ShieldingSites {
129        &self.local_server.shielding_sites
130    }
131
132    /// Parse a `fastly.toml` file into a `FastlyConfig`.
133    pub fn from_file(path: impl AsRef<Path>) -> Result<Self, FastlyConfigError> {
134        fs::read_to_string(path.as_ref())
135            .map_err(|err| FastlyConfigError::IoError {
136                path: path.as_ref().display().to_string(),
137                err,
138            })
139            .and_then(Self::from_str)
140    }
141
142    /// Parse a string containing TOML data into a `FastlyConfig`.
143    fn from_str(toml: impl AsRef<str>) -> Result<Self, FastlyConfigError> {
144        toml::from_str::<'_, TomlFastlyConfig>(toml.as_ref())
145            .map_err(Into::into)
146            .and_then(TryInto::try_into)
147    }
148}
149
150impl FromStr for FastlyConfig {
151    type Err = FastlyConfigError;
152    fn from_str(s: &str) -> Result<Self, Self::Err> {
153        Self::from_str(s)
154    }
155}
156
157/// Internal deserializer used to read data from a `fastly.toml` file.
158///
159/// Once a TOML file has been read using [`toml::from_str`][from-str], this can be converted into
160/// a [`FastlyConfig`][conf].
161///
162/// [conf]: struct.FastlyConfig.html
163/// [fromt-str]: https://docs.rs/toml/latest/toml/de/fn.from_str.html
164#[derive(Deserialize)]
165struct TomlFastlyConfig {
166    local_server: Option<RawLocalServerConfig>,
167    // AJT 2020.03.10: the following fields are marked as optional because, for the time being,
168    // we are not expecting to actually use the fastly.toml manifest, but instead use a separate
169    // TOML file for backend configuration.
170    //
171    // See https://github.com/fastly/Viceroy/issues/109 for additional context.
172    name: Option<String>,
173    description: Option<String>,
174    authors: Option<Vec<String>>,
175    language: Option<String>,
176}
177
178impl TryInto<FastlyConfig> for TomlFastlyConfig {
179    type Error = FastlyConfigError;
180    fn try_into(self) -> Result<FastlyConfig, Self::Error> {
181        let Self {
182            name,
183            description,
184            authors,
185            language,
186            local_server,
187        } = self;
188        let local_server = local_server
189            .map(TryInto::try_into)
190            .transpose()?
191            .unwrap_or_default();
192        Ok(FastlyConfig {
193            name: name.unwrap_or_default(),
194            description: description.unwrap_or_default(),
195            authors: authors.unwrap_or_default(),
196            language: language.unwrap_or_default(),
197            local_server,
198        })
199    }
200}
201
202/// Configuration settings used for tests.
203///
204/// This represents all of the `fastly.toml` fields whose keys begin with `testing`. Currently this
205/// section of the manifest is only used for providing backend definitions, but additional fields
206/// may be added in the future.
207#[derive(Clone, Debug, Default)]
208pub struct LocalServerConfig {
209    acls: AclConfig,
210    backends: BackendsConfig,
211    device_detection: DeviceDetection,
212    geolocation: Geolocation,
213    dictionaries: DictionariesConfig,
214    object_stores: ObjectStoreConfig,
215    secret_stores: SecretStoreConfig,
216    shielding_sites: ShieldingSites,
217}
218
219/// Enum of available (experimental) wasi modules
220#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
221pub enum ExperimentalModule {
222    WasiNn,
223}
224
225/// Internal deserializer used to read the `[testing]` section of a `fastly.toml` file.
226///
227/// Once a TOML file has been read using [`toml::from_str`], this can be converted into
228/// a [`LocalServerConfig`] with [`TryInto::try_into`].
229#[derive(Deserialize)]
230struct RawLocalServerConfig {
231    acls: Option<Table>,
232    backends: Option<Table>,
233    device_detection: Option<Table>,
234    geolocation: Option<Table>,
235    #[serde(alias = "config_stores")]
236    dictionaries: Option<Table>,
237    #[serde(alias = "object_store", alias = "kv_stores")]
238    object_stores: Option<Table>,
239    secret_stores: Option<Table>,
240    shielding_sites: Option<Table>,
241}
242
243impl TryInto<LocalServerConfig> for RawLocalServerConfig {
244    type Error = FastlyConfigError;
245    fn try_into(self) -> Result<LocalServerConfig, Self::Error> {
246        let Self {
247            acls,
248            backends,
249            device_detection,
250            geolocation,
251            dictionaries,
252            object_stores,
253            secret_stores,
254            shielding_sites,
255        } = self;
256        let acls = if let Some(acls) = acls {
257            acls.try_into()?
258        } else {
259            AclConfig::default()
260        };
261        let backends = if let Some(backends) = backends {
262            backends.try_into()?
263        } else {
264            BackendsConfig::default()
265        };
266        let device_detection = if let Some(device_detection) = device_detection {
267            device_detection.try_into()?
268        } else {
269            DeviceDetection::default()
270        };
271        let geolocation = if let Some(geolocation) = geolocation {
272            geolocation.try_into()?
273        } else {
274            Geolocation::default()
275        };
276        let dictionaries = if let Some(dictionaries) = dictionaries {
277            dictionaries.try_into()?
278        } else {
279            DictionariesConfig::default()
280        };
281        let object_stores = if let Some(object_store) = object_stores {
282            object_store.try_into()?
283        } else {
284            ObjectStoreConfig::default()
285        };
286        let secret_stores = if let Some(secret_store) = secret_stores {
287            secret_store.try_into()?
288        } else {
289            SecretStoreConfig::default()
290        };
291        let shielding_sites = if let Some(shielding_sites) = shielding_sites {
292            shielding_sites.try_into()?
293        } else {
294            ShieldingSites::default()
295        };
296
297        Ok(LocalServerConfig {
298            acls,
299            backends,
300            device_detection,
301            geolocation,
302            dictionaries,
303            object_stores,
304            secret_stores,
305            shielding_sites,
306        })
307    }
308}
309
310#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum, Hash)]
311pub enum UnknownImportBehavior {
312    /// Unknown imports are rejected at link time (default behavior)
313    #[default]
314    LinkError,
315    /// Unknown imports trap when called
316    Trap,
317}