huskies: merge 1169 story Gateway pulls signed artifacts from a release channel into its local store
This commit is contained in:
@@ -70,8 +70,34 @@ impl ProjectEntry {
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration for one signed release channel the gateway can `pull` from.
|
||||
///
|
||||
/// All three fields are optional in the TOML shape — a channel can be
|
||||
/// partially configured while it's being set up — but `pull <channel>`
|
||||
/// (story 1169) requires both `base_url` and `pubkey` to be present. There is
|
||||
/// no unsigned-pull mode: a missing pubkey is always a hard error, never a
|
||||
/// fallback to trusting whatever the channel serves.
|
||||
#[derive(Debug, Clone, Default, PartialEq, Deserialize, Serialize)]
|
||||
pub struct ReleaseChannelConfig {
|
||||
/// Base URL the channel is served from (e.g. `https://releases.example.com/stable`).
|
||||
///
|
||||
/// `pull <channel>` fetches `{base_url}/manifest.json` and, once verified,
|
||||
/// `{base_url}/{manifest.artifact}`.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub base_url: Option<String>,
|
||||
/// Pinned Ed25519 public key (hex) the channel's `manifest.json` signature
|
||||
/// must verify against. Generated by `release-tool keygen` and never
|
||||
/// derived automatically — an operator must paste it in explicitly.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub pubkey: Option<String>,
|
||||
/// Optional bearer token sent as `Authorization: Bearer <token>` on both
|
||||
/// the manifest and artifact requests (e.g. for a private release channel).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub bearer_token: Option<String>,
|
||||
}
|
||||
|
||||
/// Top-level `projects.toml` config.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct GatewayConfig {
|
||||
/// Map of project name → container configuration.
|
||||
#[serde(default)]
|
||||
@@ -86,6 +112,12 @@ pub struct GatewayConfig {
|
||||
/// `/api/sled-uplink` using the given secret token as a bearer credential.
|
||||
#[serde(default)]
|
||||
pub sled_tokens: BTreeMap<String, String>,
|
||||
/// Map of channel name → signed release channel configuration (story 1169).
|
||||
///
|
||||
/// Populated by an operator adding `[release_channels.<name>]` sections to
|
||||
/// `projects.toml`. Read by the `pull <channel>` gateway chat command.
|
||||
#[serde(default)]
|
||||
pub release_channels: BTreeMap<String, ReleaseChannelConfig>,
|
||||
}
|
||||
|
||||
/// Validate that a gateway config has at least one project.
|
||||
@@ -206,6 +238,7 @@ auth_token = "secret"
|
||||
let config = GatewayConfig {
|
||||
projects: BTreeMap::new(),
|
||||
sled_tokens: BTreeMap::new(),
|
||||
release_channels: BTreeMap::new(),
|
||||
};
|
||||
assert!(validate_config(&config).is_err());
|
||||
}
|
||||
@@ -218,6 +251,7 @@ auth_token = "secret"
|
||||
let config = GatewayConfig {
|
||||
projects,
|
||||
sled_tokens: BTreeMap::new(),
|
||||
release_channels: BTreeMap::new(),
|
||||
};
|
||||
assert_eq!(validate_config(&config).unwrap(), "alpha");
|
||||
}
|
||||
@@ -238,6 +272,7 @@ auth_token = "secret"
|
||||
let config = GatewayConfig {
|
||||
projects,
|
||||
sled_tokens: BTreeMap::new(),
|
||||
release_channels: BTreeMap::new(),
|
||||
};
|
||||
assert!(validate_config(&config).is_ok());
|
||||
}
|
||||
@@ -340,6 +375,7 @@ auth_token = "secret"
|
||||
let config = GatewayConfig {
|
||||
projects,
|
||||
sled_tokens: BTreeMap::new(),
|
||||
release_channels: BTreeMap::new(),
|
||||
};
|
||||
let toml_str = toml::to_string_pretty(&config).unwrap();
|
||||
let parsed: GatewayConfig = toml::from_str(&toml_str).unwrap();
|
||||
@@ -367,6 +403,7 @@ auth_token = "secret"
|
||||
let config = GatewayConfig {
|
||||
projects,
|
||||
sled_tokens: BTreeMap::new(),
|
||||
release_channels: BTreeMap::new(),
|
||||
};
|
||||
let toml_str = toml::to_string_pretty(&config).unwrap();
|
||||
let parsed: GatewayConfig = toml::from_str(&toml_str).unwrap();
|
||||
@@ -392,6 +429,7 @@ auth_token = "secret"
|
||||
let config = GatewayConfig {
|
||||
projects,
|
||||
sled_tokens: BTreeMap::new(),
|
||||
release_channels: BTreeMap::new(),
|
||||
};
|
||||
let toml_str = toml::to_string_pretty(&config).unwrap();
|
||||
assert!(toml_str.contains("expected_node_id"));
|
||||
@@ -407,6 +445,7 @@ auth_token = "secret"
|
||||
let config2 = GatewayConfig {
|
||||
projects: projects2,
|
||||
sled_tokens: BTreeMap::new(),
|
||||
release_channels: BTreeMap::new(),
|
||||
};
|
||||
let toml_str2 = toml::to_string_pretty(&config2).unwrap();
|
||||
assert!(
|
||||
@@ -423,6 +462,7 @@ auth_token = "secret"
|
||||
let config = GatewayConfig {
|
||||
projects,
|
||||
sled_tokens: BTreeMap::new(),
|
||||
release_channels: BTreeMap::new(),
|
||||
};
|
||||
let toml_str = toml::to_string_pretty(&config).unwrap();
|
||||
assert!(
|
||||
|
||||
Reference in New Issue
Block a user