Skip to content

Commit fe17796

Browse files
committed
Fix subtle runtime config issues.
Signed-off-by: Ryan Levick <[email protected]>
1 parent d5ea75a commit fe17796

File tree

3 files changed

+104
-47
lines changed

3 files changed

+104
-47
lines changed

crates/factor-variables/src/spin_cli/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ use crate::runtime_config::RuntimeConfig;
2020
pub fn runtime_config_from_toml(table: &impl GetTomlValue) -> anyhow::Result<RuntimeConfig> {
2121
// Always include the environment variable provider.
2222
let mut providers = vec![Box::<EnvVariablesProvider>::default() as _];
23-
let Some(array) = table.get("variable_provider") else {
23+
let value = table
24+
.get("variables_provider")
25+
.or_else(|| table.get("config_provider"));
26+
let Some(array) = value else {
2427
return Ok(RuntimeConfig { providers });
2528
};
2629

crates/runtime-config/src/lib.rs

Lines changed: 82 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,23 @@ where
5252
/// Creates a new resolved runtime configuration from an optional runtime config source TOML file.
5353
pub fn from_optional_file(
5454
runtime_config_path: Option<&Path>,
55-
provided_state_dir: Option<&Path>,
56-
provided_log_dir: LogDir,
55+
local_app_dir: Option<PathBuf>,
56+
provided_state_dir: UserProvidedPath,
57+
provided_log_dir: UserProvidedPath,
5758
use_gpu: bool,
5859
) -> anyhow::Result<Self> {
5960
match runtime_config_path {
6061
Some(runtime_config_path) => Self::from_file(
6162
runtime_config_path,
63+
local_app_dir,
6264
provided_state_dir,
6365
provided_log_dir,
6466
use_gpu,
6567
),
6668
None => Self::new(
6769
Default::default(),
6870
None,
71+
local_app_dir,
6972
provided_state_dir,
7073
provided_log_dir,
7174
use_gpu,
@@ -78,8 +81,9 @@ where
7881
/// `provided_state_dir` is the explicitly provided state directory, if any.
7982
pub fn from_file(
8083
runtime_config_path: &Path,
81-
provided_state_dir: Option<&Path>,
82-
provided_log_dir: LogDir,
84+
local_app_dir: Option<PathBuf>,
85+
provided_state_dir: UserProvidedPath,
86+
provided_log_dir: UserProvidedPath,
8387
use_gpu: bool,
8488
) -> anyhow::Result<Self> {
8589
let file = std::fs::read_to_string(runtime_config_path).with_context(|| {
@@ -98,6 +102,7 @@ where
98102
Self::new(
99103
toml,
100104
Some(runtime_config_path),
105+
local_app_dir,
101106
provided_state_dir,
102107
provided_log_dir,
103108
use_gpu,
@@ -108,11 +113,13 @@ where
108113
pub fn new(
109114
toml: toml::Table,
110115
runtime_config_path: Option<&Path>,
111-
provided_state_dir: Option<&Path>,
112-
provided_log_dir: LogDir,
116+
local_app_dir: Option<PathBuf>,
117+
provided_state_dir: UserProvidedPath,
118+
provided_log_dir: UserProvidedPath,
113119
use_gpu: bool,
114120
) -> anyhow::Result<Self> {
115-
let toml_resolver = TomlResolver::new(&toml, provided_state_dir, provided_log_dir);
121+
let toml_resolver =
122+
TomlResolver::new(&toml, local_app_dir, provided_state_dir, provided_log_dir);
116123
let tls_resolver = runtime_config_path.map(SpinTlsRuntimeConfig::new);
117124
let key_value_config_resolver = key_value_config_resolver(toml_resolver.state_dir()?);
118125
let sqlite_config_resolver = sqlite_config_resolver(toml_resolver.state_dir()?)
@@ -178,19 +185,25 @@ where
178185
/// Resolves runtime configuration from a TOML file.
179186
pub struct TomlResolver<'a> {
180187
table: TomlKeyTracker<'a>,
188+
/// The local app directory.
189+
local_app_dir: Option<PathBuf>,
181190
/// Explicitly provided state directory.
182-
state_dir: Option<&'a Path>,
191+
state_dir: UserProvidedPath,
183192
/// Explicitly provided log directory.
184-
log_dir: LogDir,
193+
log_dir: UserProvidedPath,
185194
}
186195

187196
impl<'a> TomlResolver<'a> {
188197
/// Create a new TOML resolver.
189-
///
190-
/// The `state_dir` is the explicitly provided state directory, if any.
191-
pub fn new(table: &'a toml::Table, state_dir: Option<&'a Path>, log_dir: LogDir) -> Self {
198+
pub fn new(
199+
table: &'a toml::Table,
200+
local_app_dir: Option<PathBuf>,
201+
state_dir: UserProvidedPath,
202+
log_dir: UserProvidedPath,
203+
) -> Self {
192204
Self {
193205
table: TomlKeyTracker::new(table),
206+
local_app_dir,
194207
state_dir,
195208
log_dir,
196209
}
@@ -200,28 +213,63 @@ impl<'a> TomlResolver<'a> {
200213
///
201214
/// Errors if the path cannot be converted to an absolute path.
202215
pub fn state_dir(&self) -> std::io::Result<Option<PathBuf>> {
203-
let from_toml = || {
204-
self.table
205-
.get("state_dir")
206-
.and_then(|v| v.as_str())
207-
.filter(|v| !v.is_empty())
208-
.map(Path::new)
209-
};
210-
// Prefer explicitly provided state directory, then take from toml.
211-
self.state_dir
212-
.or_else(from_toml)
213-
.map(std::path::absolute)
214-
.transpose()
216+
let mut state_dir = self.state_dir.clone();
217+
// If the state_dir is not explicitly provided, check the toml.
218+
if matches!(state_dir, UserProvidedPath::Default) {
219+
let from_toml =
220+
self.table
221+
.get("state_dir")
222+
.and_then(|v| v.as_str())
223+
.map(|toml_value| {
224+
if toml_value.is_empty() {
225+
// If the toml value is empty, treat it as unset.
226+
UserProvidedPath::Unset
227+
} else {
228+
// Otherwise, treat the toml value as a provided path.
229+
UserProvidedPath::Provided(PathBuf::from(toml_value))
230+
}
231+
});
232+
// If toml value is not provided, use the original value after all.
233+
state_dir = from_toml.unwrap_or(state_dir);
234+
}
235+
236+
match (state_dir, &self.local_app_dir) {
237+
(UserProvidedPath::Provided(p), _) => Ok(Some(std::path::absolute(p)?)),
238+
(UserProvidedPath::Default, Some(local_app_dir)) => {
239+
Ok(Some(local_app_dir.join(".spin")))
240+
}
241+
(UserProvidedPath::Default | UserProvidedPath::Unset, _) => Ok(None),
242+
}
215243
}
216244

217245
/// Get the configured log directory.
218246
///
219247
/// Errors if the path cannot be converted to an absolute path.
220248
pub fn log_dir(&self) -> std::io::Result<Option<PathBuf>> {
221-
match &self.log_dir {
222-
LogDir::Provided(p) => Ok(Some(std::path::absolute(p)?)),
223-
LogDir::Default => Ok(self.state_dir()?.map(|p| p.join("logs"))),
224-
LogDir::None => Ok(None),
249+
let mut log_dir = self.log_dir.clone();
250+
// If the log_dir is not explicitly provided, check the toml.
251+
if matches!(log_dir, UserProvidedPath::Default) {
252+
let from_toml = self
253+
.table
254+
.get("log_dir")
255+
.and_then(|v| v.as_str())
256+
.map(|toml_value| {
257+
if toml_value.is_empty() {
258+
// If the toml value is empty, treat it as unset.
259+
UserProvidedPath::Unset
260+
} else {
261+
// Otherwise, treat the toml value as a provided path.
262+
UserProvidedPath::Provided(PathBuf::from(toml_value))
263+
}
264+
});
265+
// If toml value is not provided, use the original value after all.
266+
log_dir = from_toml.unwrap_or(log_dir);
267+
}
268+
269+
match log_dir {
270+
UserProvidedPath::Provided(p) => Ok(Some(std::path::absolute(p)?)),
271+
UserProvidedPath::Default => Ok(self.state_dir()?.map(|p| p.join("logs"))),
272+
UserProvidedPath::Unset => Ok(None),
225273
}
226274
}
227275

@@ -397,13 +445,13 @@ fn sqlite_config_resolver(
397445
))
398446
}
399447

400-
/// The log directory for the trigger.
448+
/// A user provided option which be either be provided, default, or explicitly none.
401449
#[derive(Clone, Debug)]
402-
pub enum LogDir {
403-
/// Use the explicitly provided log directory.
450+
pub enum UserProvidedPath {
451+
/// Use the explicitly provided directory.
404452
Provided(PathBuf),
405-
/// Use the default log directory.
453+
/// Use the default.
406454
Default,
407-
/// Do not log.
408-
None,
455+
/// Explicitly unset.
456+
Unset,
409457
}

crates/trigger/src/cli.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use spin_common::ui::quoted_path;
1010
use spin_common::url::parse_file_url;
1111
use spin_common::{arg_parser::parse_kv, sloth};
1212
use spin_factors_executor::{ComponentLoader, FactorsExecutor};
13-
use spin_runtime_config::{LogDir, ResolvedRuntimeConfig};
13+
use spin_runtime_config::{ResolvedRuntimeConfig, UserProvidedPath};
1414

1515
use crate::factors::{TriggerFactors, TriggerFactorsRuntimeConfig};
1616
use crate::stdio::{FollowComponents, StdioLoggingExecutorHooks};
@@ -308,25 +308,31 @@ impl<T: Trigger> TriggerAppBuilder<T> {
308308
};
309309
self.trigger.add_to_linker(core_engine_builder.linker())?;
310310

311-
// Hardcode `use_gpu` to true for now
312-
let use_gpu = true;
311+
let runtime_config_path = options.runtime_config_file;
312+
let local_app_dir = options
313+
.local_app_dir
314+
.map(std::path::absolute)
315+
.transpose()
316+
.context("failed to resolve local app directory path to an absolute path")?;
313317
let state_dir = match options.state_dir {
314318
// Make sure `--state-dir=""` unsets the state dir
315-
Some("") => None,
316-
Some(s) => Some(PathBuf::from(s)),
317-
// Default to `.spin/` in the local app dir
318-
None => options.local_app_dir.map(|d| Path::new(d).join(".spin")),
319+
Some("") => UserProvidedPath::Unset,
320+
Some(s) => UserProvidedPath::Provided(PathBuf::from(s)),
321+
None => UserProvidedPath::Default,
319322
};
320323
let log_dir = match &options.log_dir {
321324
// Make sure `--log-dir=""` unsets the log dir
322-
Some(p) if p.as_os_str().is_empty() => LogDir::None,
323-
Some(p) => LogDir::Provided(p.clone()),
324-
None => LogDir::Default,
325+
Some(p) if p.as_os_str().is_empty() => UserProvidedPath::Unset,
326+
Some(p) => UserProvidedPath::Provided(p.clone()),
327+
None => UserProvidedPath::Default,
325328
};
329+
// Hardcode `use_gpu` to true for now
330+
let use_gpu = true;
326331
let runtime_config =
327332
ResolvedRuntimeConfig::<TriggerFactorsRuntimeConfig>::from_optional_file(
328-
options.runtime_config_file,
329-
state_dir.as_deref(),
333+
runtime_config_path,
334+
local_app_dir,
335+
state_dir,
330336
log_dir,
331337
use_gpu,
332338
)?;

0 commit comments

Comments
 (0)