Skip to content

Commit 495df25

Browse files
Load integration app config at runtime
1 parent 93de780 commit 495df25

11 files changed

Lines changed: 136 additions & 8 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/trusted-server-adapter-cloudflare/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ edgezero-core = { workspace = true }
2525
error-stack = { workspace = true }
2626
futures = { workspace = true }
2727
log = { workspace = true }
28+
serde_json = { workspace = true }
2829
trusted-server-core = { path = "../trusted-server-core" }
2930
trusted-server-js = { path = "../trusted-server-js" }
3031
worker = { version = "0.8", default-features = false, features = ["http"], optional = true }

crates/trusted-server-adapter-cloudflare/src/app.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use edgezero_core::router::RouterService;
1010
use error_stack::Report;
1111
use trusted_server_core::auction::endpoints::handle_auction;
1212
use trusted_server_core::auction::{AuctionOrchestrator, build_orchestrator};
13+
#[cfg(target_arch = "wasm32")]
14+
use trusted_server_core::config_payload::settings_from_config_blob;
1315
use trusted_server_core::ec::EcContext;
1416
use trusted_server_core::error::{IntoHttpResponse as _, TrustedServerError};
1517
use trusted_server_core::integrations::{IntegrationRegistry, ProxyDispatchInput};
@@ -34,6 +36,14 @@ use crate::platform::build_runtime_services;
3436
// AppState
3537
// ---------------------------------------------------------------------------
3638

39+
#[cfg(target_arch = "wasm32")]
40+
static CLOUDFLARE_CONFIG_JSON: std::sync::OnceLock<String> = std::sync::OnceLock::new();
41+
42+
#[cfg(target_arch = "wasm32")]
43+
pub fn set_cloudflare_config_json(value: String) {
44+
let _ = CLOUDFLARE_CONFIG_JSON.set(value);
45+
}
46+
3747
/// Application state built once at startup and shared across all requests.
3848
pub struct AppState {
3949
settings: Arc<Settings>,
@@ -48,10 +58,37 @@ pub struct AppState {
4858
/// Returns an error when settings, the auction orchestrator, or the integration
4959
/// registry fail to initialise.
5060
fn build_state() -> Result<Arc<AppState>, Report<TrustedServerError>> {
61+
#[cfg(target_arch = "wasm32")]
62+
if let Some(settings) = settings_from_cloudflare_config_json()? {
63+
return build_state_with_settings(settings);
64+
}
65+
5166
let settings = Settings::from_toml(include_str!("../../../trusted-server.example.toml"))?;
5267
build_state_with_settings(settings)
5368
}
5469

70+
#[cfg(target_arch = "wasm32")]
71+
fn settings_from_cloudflare_config_json() -> Result<Option<Settings>, Report<TrustedServerError>> {
72+
let Some(raw_config) = CLOUDFLARE_CONFIG_JSON.get() else {
73+
return Ok(None);
74+
};
75+
let value: serde_json::Value = serde_json::from_str(raw_config).map_err(|error| {
76+
Report::new(TrustedServerError::Configuration {
77+
message: "invalid Cloudflare TRUSTED_SERVER_CONFIG JSON".to_string(),
78+
})
79+
.attach(format!("failed to parse TRUSTED_SERVER_CONFIG: {error}"))
80+
})?;
81+
let envelope = value
82+
.get("app_config")
83+
.and_then(serde_json::Value::as_str)
84+
.ok_or_else(|| {
85+
Report::new(TrustedServerError::Configuration {
86+
message: "Cloudflare TRUSTED_SERVER_CONFIG missing app_config".to_string(),
87+
})
88+
})?;
89+
settings_from_config_blob(envelope).map(Some)
90+
}
91+
5592
/// Build the application state from explicit settings.
5693
///
5794
/// # Errors

crates/trusted-server-adapter-cloudflare/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ use worker::{Context, Env, Request, Response, Result, event};
1818
#[cfg(target_arch = "wasm32")]
1919
#[event(fetch)]
2020
pub async fn main(req: Request, env: Env, ctx: Context) -> Result<Response> {
21+
if let Ok(config) = env.var("TRUSTED_SERVER_CONFIG") {
22+
app::set_cloudflare_config_json(config.to_string());
23+
}
24+
2125
match edgezero_adapter_cloudflare::run_app::<app::TrustedServerApp>(req, env, ctx).await {
2226
Ok(resp) => Ok(resp),
2327
Err(e) => {

crates/trusted-server-integration-tests/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/trusted-server-integration-tests/src/bin/generate-viceroy-config.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ fn inject_generated_config_stores(
171171

172172
fn generated_config_store_blocks(envelope_json: &str, edgezero_enabled: bool) -> String {
173173
let edgezero_enabled_value = if edgezero_enabled { "true" } else { "false" };
174+
let edgezero_rollout_pct = if edgezero_enabled { "100" } else { "0" };
174175
format!(
175176
r#" # Generated by generate-viceroy-config. Do not edit generated output.
176177
[local_server.config_stores.app_config]
@@ -182,7 +183,8 @@ fn generated_config_store_blocks(envelope_json: &str, edgezero_enabled: bool) ->
182183
[local_server.config_stores.trusted_server_config]
183184
format = "inline-toml"
184185
[local_server.config_stores.trusted_server_config.contents]
185-
edgezero_enabled = "{edgezero_enabled_value}""#
186+
edgezero_enabled = "{edgezero_enabled_value}"
187+
edgezero_rollout_pct = "{edgezero_rollout_pct}""#
186188
)
187189
}
188190

@@ -241,6 +243,10 @@ mod tests {
241243
generated.contains("edgezero_enabled = \"true\""),
242244
"should include enabled rollout flag"
243245
);
246+
assert!(
247+
generated.contains("edgezero_rollout_pct = \"100\""),
248+
"should include full rollout for enabled EdgeZero config"
249+
);
244250
assert!(
245251
generated.contains("[local_server.config_stores.jwks_store]"),
246252
"should preserve following template content"
@@ -257,6 +263,10 @@ mod tests {
257263
generated.contains("edgezero_enabled = \"false\""),
258264
"should include disabled rollout flag"
259265
);
266+
assert!(
267+
generated.contains("edgezero_rollout_pct = \"0\""),
268+
"should include zero rollout for legacy config"
269+
);
260270
}
261271

262272
#[test]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use error_stack::Report;
2+
use edgezero_core::blob_envelope::BlobEnvelope;
3+
use trusted_server_core::config::validate_settings_for_deploy;
4+
use trusted_server_core::settings::Settings;
5+
6+
use crate::common::runtime::{TestError, TestResult};
7+
8+
const GENERATED_AT: &str = "2026-06-23T00:00:00Z";
9+
const APP_CONFIG: &str = include_str!("../../fixtures/configs/trusted-server.integration.toml");
10+
11+
pub fn integration_app_config_envelope(origin_port: u16) -> TestResult<String> {
12+
let origin_url = format!("http://127.0.0.1:{origin_port}");
13+
let mut settings = Settings::from_toml(APP_CONFIG).map_err(|report| {
14+
Report::new(TestError::ConfigGeneration)
15+
.attach(format!("invalid Trusted Server integration config: {report:?}"))
16+
})?;
17+
settings.publisher.origin_url = origin_url;
18+
validate_settings_for_deploy(&settings).map_err(|report| {
19+
Report::new(TestError::ConfigGeneration)
20+
.attach(format!("invalid generated integration config: {report:?}"))
21+
})?;
22+
23+
let data = serde_json::to_value(&settings).map_err(|error| {
24+
Report::new(TestError::ConfigGeneration)
25+
.attach(format!("failed to serialize integration settings: {error}"))
26+
})?;
27+
let envelope = BlobEnvelope::new(data, GENERATED_AT.to_string());
28+
serde_json::to_string(&envelope).map_err(|error| {
29+
Report::new(TestError::ConfigGeneration)
30+
.attach(format!("failed to serialize integration app-config envelope: {error}"))
31+
})
32+
}
33+
34+
pub fn cloudflare_config_json(origin_port: u16) -> TestResult<String> {
35+
let envelope = integration_app_config_envelope(origin_port)?;
36+
serde_json::to_string(&serde_json::json!({ "app_config": envelope })).map_err(|error| {
37+
Report::new(TestError::ConfigGeneration)
38+
.attach(format!("failed to serialize Cloudflare config binding: {error}"))
39+
})
40+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod assertions;
2+
pub mod config;
23
pub mod ec;
34
pub mod runtime;

crates/trusted-server-integration-tests/tests/common/runtime.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,10 @@ pub enum TestError {
5252
#[display("JSON field assertion failed: {field}")]
5353
JsonFieldMismatch { field: String },
5454

55-
// Resource errors
55+
// Resource/config errors
56+
#[display("Failed to generate runtime configuration")]
57+
ConfigGeneration,
58+
5659
#[display("No available port found")]
5760
NoPortAvailable,
5861
}

crates/trusted-server-integration-tests/tests/environments/axum.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
use crate::common::config::integration_app_config_envelope;
12
use crate::common::runtime::{
2-
RuntimeEnvironment, RuntimeProcess, RuntimeProcessHandle, TestError, TestResult,
3+
RuntimeEnvironment, RuntimeProcess, RuntimeProcessHandle, TestError, TestResult, origin_port,
34
};
45
use error_stack::ResultExt as _;
56
use std::io::{BufRead as _, BufReader};
@@ -31,8 +32,11 @@ impl RuntimeEnvironment for AxumDevServer {
3132
let binary = self.binary_path();
3233
let port = super::find_available_port().unwrap_or(AXUM_DEFAULT_PORT);
3334

35+
let app_config = integration_app_config_envelope(origin_port())?;
36+
3437
let mut child = Command::new(&binary)
3538
.env("PORT", port.to_string())
39+
.env("TRUSTED_SERVER_CONFIG_APP_CONFIG_APP_CONFIG", app_config)
3640
.stdout(Stdio::null())
3741
.stderr(Stdio::piped())
3842
.spawn()

0 commit comments

Comments
 (0)