nixfleet_control_plane/polling/
signed_fetch.rs1use std::path::Path;
4use std::time::Duration;
5
6use anyhow::{Context, Result};
7use chrono::{DateTime, Utc};
8use nixfleet_proto::TrustedPubkey;
9
10pub fn build_client() -> reqwest::Client {
12 reqwest::Client::builder()
13 .use_rustls_tls()
14 .timeout(Duration::from_secs(15))
15 .build()
16 .expect("build signed-fetch client (rustls + 15s timeout)")
17}
18
19pub fn read_trust_config(path: &Path) -> Result<nixfleet_proto::TrustConfig> {
21 let raw = std::fs::read_to_string(path)
22 .with_context(|| format!("read trust file {}", path.display()))?;
23 serde_json::from_str(&raw).context("parse trust file")
24}
25
26pub fn read_trust_roots(
30 path: &Path,
31 now: DateTime<Utc>,
32) -> Result<(Vec<TrustedPubkey>, Option<DateTime<Utc>>)> {
33 let trust = read_trust_config(path)?;
34 Ok((
35 trust.ci_release_key.active_keys_at(now),
36 trust.ci_release_key.reject_before,
37 ))
38}
39
40pub fn read_token(path: Option<&Path>) -> Result<Option<String>> {
42 match path {
43 Some(p) => Ok(Some(
44 std::fs::read_to_string(p)
45 .with_context(|| format!("read token file {}", p.display()))?
46 .trim()
47 .to_string(),
48 )),
49 None => Ok(None),
50 }
51}
52
53pub async fn fetch_signed_pair(
55 client: &reqwest::Client,
56 artifact_url: &str,
57 signature_url: &str,
58 token: Option<&str>,
59) -> Result<(Vec<u8>, Vec<u8>)> {
60 let artifact = fetch_url(client, artifact_url, token).await?;
61 let signature = fetch_url(client, signature_url, token).await?;
62 Ok((artifact, signature))
63}
64
65async fn fetch_url(client: &reqwest::Client, url: &str, token: Option<&str>) -> Result<Vec<u8>> {
66 let mut req = client.get(url);
67 if let Some(t) = token {
68 req = req.header("Authorization", format!("Bearer {t}"));
69 }
70 let resp = req.send().await.with_context(|| format!("GET {url}"))?;
71 if !resp.status().is_success() {
72 let status = resp.status();
73 let body = resp.text().await.unwrap_or_default();
74 anyhow::bail!("{url}: {status}: {body}");
75 }
76 let bytes = resp
77 .bytes()
78 .await
79 .with_context(|| format!("read body {url}"))?;
80 Ok(bytes.to_vec())
81}