fc-common: add unsupported_timeout for queue runner

Hydra maxUnsupportedTime compatibility. DUration is optional, defaults
to `None` and is configurable through `fc.toml` under
`queue_runner.unsupported_timeout` key. E.g., "1h".

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: I299722e2a3865c873e212a7615b97b806a6a6964
This commit is contained in:
raf 2026-02-28 22:06:53 +03:00
commit b43a11756a
Signed by: NotAShelf
GPG key ID: 29D95B64378DB4BF
4 changed files with 95 additions and 8 deletions

17
Cargo.lock generated
View file

@ -812,6 +812,7 @@ dependencies = [
"config",
"git2",
"hex",
"humantime-serde",
"lettre",
"libc",
"regex",
@ -1288,6 +1289,22 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "humantime"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
[[package]]
name = "humantime-serde"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c"
dependencies = [
"humantime",
"serde",
]
[[package]]
name = "hyper"
version = "1.8.1"

View file

@ -23,7 +23,6 @@ fc-evaluator = { path = "./crates/evaluator" }
fc-queue-runner = { path = "./crates/queue-runner" }
fc-server = { path = "./crates/server" }
anyhow = "1.0.101"
argon2 = "0.5.3"
askama = "0.15.4"
@ -38,6 +37,7 @@ futures = "0.3.31"
git2 = "0.20.4"
hex = "0.4.3"
hmac = "0.12.1"
humantime-serde = "1.1.1"
lettre = { version = "0.11.19", default-features = false, features = [
"tokio1-rustls-tls",
"smtp-transport",

View file

@ -14,6 +14,7 @@ clap.workspace = true
config.workspace = true
git2.workspace = true
hex.workspace = true
humantime-serde.workspace = true
lettre.workspace = true
libc.workspace = true
regex.workspace = true

View file

@ -1,6 +1,7 @@
//! Configuration management for FC CI
use std::path::PathBuf;
use std::time::Duration;
use config as config_crate;
use serde::{Deserialize, Serialize};
@ -90,6 +91,12 @@ pub struct QueueRunnerConfig {
/// TTL in seconds for failed paths cache entries (default 24h).
#[serde(default = "default_failed_paths_ttl")]
pub failed_paths_ttl: u64,
/// Timeout after which builds for unsupported systems are aborted.
/// None or 0 = disabled (Hydra maxUnsupportedTime compatibility).
#[serde(default)]
#[serde(with = "humantime_serde")]
pub unsupported_timeout: Option<Duration>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
@ -546,13 +553,14 @@ impl Default for EvaluatorConfig {
impl Default for QueueRunnerConfig {
fn default() -> Self {
Self {
workers: 4,
poll_interval: 5,
build_timeout: 3600,
work_dir: PathBuf::from("/tmp/fc-queue-runner"),
strict_errors: false,
failed_paths_cache: true,
failed_paths_ttl: 86400,
workers: 4,
poll_interval: 5,
build_timeout: 3600,
work_dir: PathBuf::from("/tmp/fc-queue-runner"),
strict_errors: false,
failed_paths_cache: true,
failed_paths_ttl: 86400,
unsupported_timeout: None,
}
}
}
@ -853,4 +861,65 @@ mod tests {
env::remove_var("FC_SERVER__PORT");
}
}
#[test]
fn test_unsupported_timeout_config() {
let mut config = Config::default();
config.queue_runner.unsupported_timeout = Some(Duration::from_secs(3600));
// Serialize and deserialize to verify serde works
let toml_str = toml::to_string(&config).unwrap();
let parsed: Config = toml::from_str(&toml_str).unwrap();
assert_eq!(
parsed.queue_runner.unsupported_timeout,
Some(Duration::from_secs(3600))
);
}
#[test]
fn test_unsupported_timeout_default() {
let config = Config::default();
assert_eq!(config.queue_runner.unsupported_timeout, None);
}
#[test]
fn test_unsupported_timeout_various_formats() {
// Test 30 minutes
let mut config = Config::default();
config.queue_runner.unsupported_timeout = Some(Duration::from_secs(1800));
let toml_str = toml::to_string(&config).unwrap();
let parsed: Config = toml::from_str(&toml_str).unwrap();
assert_eq!(
parsed.queue_runner.unsupported_timeout,
Some(Duration::from_secs(1800))
);
// Test disabled (0s)
let mut config = Config::default();
config.queue_runner.unsupported_timeout = Some(Duration::from_secs(0));
let toml_str = toml::to_string(&config).unwrap();
let parsed: Config = toml::from_str(&toml_str).unwrap();
assert_eq!(
parsed.queue_runner.unsupported_timeout,
Some(Duration::from_secs(0))
);
}
#[test]
fn test_humantime_serde_parsing() {
// Test that we can directly parse a QueueRunnerConfig with humantime format
let toml = r#"
workers = 4
poll_interval = 5
build_timeout = 3600
work_dir = "/tmp/fc"
unsupported_timeout = "2h 30m"
"#;
let qr_config: QueueRunnerConfig = toml::from_str(toml).unwrap();
assert_eq!(
qr_config.unsupported_timeout,
Some(Duration::from_secs(9000)) // 2.5 hours
);
}
}