huskies: merge 1173 story Identity-aware fleet checks: cryptographic node identity in upgrade and health probes
This commit is contained in:
@@ -40,6 +40,14 @@ pub struct ProjectEntry {
|
||||
/// commands can route to the correct directory without re-deriving it.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub host_path: Option<String>,
|
||||
/// Ed25519 node ID (hex pubkey) this sled is expected to answer as.
|
||||
///
|
||||
/// Captured automatically from the first successful `/identity` probe
|
||||
/// (story 1173) when absent, then checked on every subsequent upgrade and
|
||||
/// health probe so a container swapped out from under the gateway is
|
||||
/// detected even when its `/health` endpoint still reports ok.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub expected_node_id: Option<String>,
|
||||
}
|
||||
|
||||
impl ProjectEntry {
|
||||
@@ -52,6 +60,7 @@ impl ProjectEntry {
|
||||
auth_token: None,
|
||||
ssh_port: None,
|
||||
host_path: None,
|
||||
expected_node_id: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,6 +232,7 @@ auth_token = "secret"
|
||||
auth_token: Some("secret".into()),
|
||||
ssh_port: None,
|
||||
host_path: None,
|
||||
expected_node_id: None,
|
||||
},
|
||||
);
|
||||
let config = GatewayConfig {
|
||||
@@ -258,6 +268,7 @@ auth_token = "secret"
|
||||
auth_token: Some("tok".into()),
|
||||
ssh_port: None,
|
||||
host_path: None,
|
||||
expected_node_id: None,
|
||||
},
|
||||
);
|
||||
assert_eq!(validate_project_exists(&projects, "ws").unwrap(), "");
|
||||
@@ -278,6 +289,7 @@ auth_token = "secret"
|
||||
auth_token: Some("tok".into()),
|
||||
ssh_port: None,
|
||||
host_path: None,
|
||||
expected_node_id: None,
|
||||
};
|
||||
assert!(!e.has_url());
|
||||
}
|
||||
@@ -321,6 +333,7 @@ auth_token = "secret"
|
||||
auth_token: Some("mysecret".into()),
|
||||
ssh_port: None,
|
||||
host_path: None,
|
||||
expected_node_id: None,
|
||||
};
|
||||
let mut projects = BTreeMap::new();
|
||||
projects.insert("myproj".into(), entry);
|
||||
@@ -347,6 +360,7 @@ auth_token = "secret"
|
||||
auth_token: None,
|
||||
ssh_port: Some(2201),
|
||||
host_path: None,
|
||||
expected_node_id: None,
|
||||
};
|
||||
let mut projects = BTreeMap::new();
|
||||
projects.insert("myproj".into(), entry);
|
||||
@@ -364,6 +378,43 @@ auth_token = "secret"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn expected_node_id_roundtrips_and_is_omitted_when_none() {
|
||||
let with_id = ProjectEntry {
|
||||
url: Some("http://127.0.0.1:3101".into()),
|
||||
auth_token: None,
|
||||
ssh_port: None,
|
||||
host_path: None,
|
||||
expected_node_id: Some("ab".repeat(32)),
|
||||
};
|
||||
let mut projects = BTreeMap::new();
|
||||
projects.insert("p".into(), with_id);
|
||||
let config = GatewayConfig {
|
||||
projects,
|
||||
sled_tokens: BTreeMap::new(),
|
||||
};
|
||||
let toml_str = toml::to_string_pretty(&config).unwrap();
|
||||
assert!(toml_str.contains("expected_node_id"));
|
||||
let parsed: GatewayConfig = toml::from_str(&toml_str).unwrap();
|
||||
assert_eq!(
|
||||
parsed.projects["p"].expected_node_id.as_deref(),
|
||||
Some("ab".repeat(32).as_str())
|
||||
);
|
||||
|
||||
let without_id = ProjectEntry::with_url("http://127.0.0.1:3101");
|
||||
let mut projects2 = BTreeMap::new();
|
||||
projects2.insert("p".into(), without_id);
|
||||
let config2 = GatewayConfig {
|
||||
projects: projects2,
|
||||
sled_tokens: BTreeMap::new(),
|
||||
};
|
||||
let toml_str2 = toml::to_string_pretty(&config2).unwrap();
|
||||
assert!(
|
||||
!toml_str2.contains("expected_node_id"),
|
||||
"expected_node_id should be omitted when None: {toml_str2}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ssh_port_none_is_omitted_from_toml() {
|
||||
let entry = ProjectEntry::with_url("http://127.0.0.1:3101");
|
||||
|
||||
Reference in New Issue
Block a user