1use std::path::Path;
6
7use s2_common::{
8 config::{BasinConfig, OptionalStreamConfig},
9 resources::ProvisionMode,
10};
11use tracing::info;
12
13use crate::backend::Backend;
14
15pub fn load(path: &Path) -> eyre::Result<s2_resource_spec::Resources> {
16 let contents = std::fs::read_to_string(path)
17 .map_err(|e| eyre::eyre!("failed to read init file {:?}: {}", path, e))?;
18 let spec: s2_resource_spec::Resources = serde_json::from_str(&contents)
19 .map_err(|e| eyre::eyre!("failed to parse init file {:?}: {}", path, e))?;
20 Ok(spec)
21}
22
23pub async fn apply(backend: &Backend, spec: s2_resource_spec::Resources) -> eyre::Result<()> {
24 s2_resource_spec::validate(&spec).map_err(|e| eyre::eyre!(e))?;
25
26 for basin_spec in spec.basins {
27 let config = basin_spec.config.map(BasinConfig::from).unwrap_or_default();
28
29 backend
30 .provision_basin(basin_spec.name.clone(), config, ProvisionMode::Ensure)
31 .await
32 .map_err(|e| {
33 eyre::eyre!(
34 "failed to apply basin {:?}: {}",
35 basin_spec.name.as_ref(),
36 e
37 )
38 })?;
39
40 info!(basin = basin_spec.name.as_ref(), "basin applied");
41
42 for stream_spec in basin_spec.streams {
43 let config = stream_spec
44 .config
45 .map(OptionalStreamConfig::from)
46 .unwrap_or_default();
47
48 backend
49 .provision_stream(
50 basin_spec.name.clone(),
51 stream_spec.name.clone(),
52 config,
53 ProvisionMode::Ensure,
54 )
55 .await
56 .map_err(|e| {
57 eyre::eyre!(
58 "failed to apply stream {:?}/{:?}: {}",
59 basin_spec.name.as_ref(),
60 stream_spec.name.as_ref(),
61 e
62 )
63 })?;
64
65 info!(
66 basin = basin_spec.name.as_ref(),
67 stream = stream_spec.name.as_ref(),
68 "stream applied"
69 );
70 }
71 }
72 Ok(())
73}