|
@@ -17,13 +17,16 @@ pub async fn init_host_keys(
|
|
|
let updates = collect_updates(config, cluster_filter).await?;
|
|
let updates = collect_updates(config, cluster_filter).await?;
|
|
|
|
|
|
|
|
if updates.is_empty() {
|
|
if updates.is_empty() {
|
|
|
- println!("(no nodes matched)");
|
|
|
|
|
|
|
+ println!("{}", fluent_i18n::t!("host-key-no-match"));
|
|
|
return Ok(());
|
|
return Ok(());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- println!("Discovered host keys:");
|
|
|
|
|
- for (name, fp) in &updates {
|
|
|
|
|
- println!(" {} -> {}", name, fp);
|
|
|
|
|
|
|
+ println!("{}", fluent_i18n::t!("host-key-header"));
|
|
|
|
|
+ for (name, ip, fp) in &updates {
|
|
|
|
|
+ println!(
|
|
|
|
|
+ " {}",
|
|
|
|
|
+ fluent_i18n::t!("host-key-line", { "name" => name, "ip" => ip, "key" => fp })
|
|
|
|
|
+ );
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
let content = fs::read_to_string(config_path)
|
|
let content = fs::read_to_string(config_path)
|
|
@@ -44,7 +47,7 @@ pub async fn init_host_keys(
|
|
|
async fn collect_updates(
|
|
async fn collect_updates(
|
|
|
config: &Config,
|
|
config: &Config,
|
|
|
cluster_filter: Option<&str>,
|
|
cluster_filter: Option<&str>,
|
|
|
-) -> Result<Vec<(String, String)>> {
|
|
|
|
|
|
|
+) -> Result<Vec<(String, String, String)>> {
|
|
|
let mut out = Vec::new();
|
|
let mut out = Vec::new();
|
|
|
for cluster in &config.clusters {
|
|
for cluster in &config.clusters {
|
|
|
if let Some(name) = cluster_filter
|
|
if let Some(name) = cluster_filter
|
|
@@ -54,8 +57,11 @@ async fn collect_updates(
|
|
|
}
|
|
}
|
|
|
for node in &cluster.nodes {
|
|
for node in &cluster.nodes {
|
|
|
match fetch_host_key(config, node).await {
|
|
match fetch_host_key(config, node).await {
|
|
|
- Ok(fp) => out.push((node.name.clone(), fp)),
|
|
|
|
|
- Err(e) => eprintln!("warning: failed to fetch key for {}: {}", node.name, e),
|
|
|
|
|
|
|
+ Ok(fp) => out.push((node.name.clone(), node.ssh_ip.clone(), fp)),
|
|
|
|
|
+ Err(e) => eprintln!(
|
|
|
|
|
+ "{}",
|
|
|
|
|
+ fluent_i18n::t!("host-key-error", { "name" => &node.name, "ip" => &node.ssh_ip, "error" => &e.to_string() })
|
|
|
|
|
+ ),
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -65,9 +71,9 @@ async fn collect_updates(
|
|
|
/// Walk `content` line by line and rewrite any node definition whose
|
|
/// Walk `content` line by line and rewrite any node definition whose
|
|
|
/// `name = "..."` matches an entry in `updates`, setting or updating the
|
|
/// `name = "..."` matches an entry in `updates`, setting or updating the
|
|
|
/// `host_key` field.
|
|
/// `host_key` field.
|
|
|
-fn update_host_keys_text(content: &str, updates: &[(String, String)]) -> String {
|
|
|
|
|
|
|
+fn update_host_keys_text(content: &str, updates: &[(String, String, String)]) -> String {
|
|
|
let mut lines: Vec<String> = content.lines().map(String::from).collect();
|
|
let mut lines: Vec<String> = content.lines().map(String::from).collect();
|
|
|
- for (name, fp) in updates {
|
|
|
|
|
|
|
+ for (name, _ip, fp) in updates {
|
|
|
let marker = format!("name = \"{}\"", name);
|
|
let marker = format!("name = \"{}\"", name);
|
|
|
for line in &mut lines {
|
|
for line in &mut lines {
|
|
|
if is_node_line(line) && line.contains(&marker) {
|
|
if is_node_line(line) && line.contains(&marker) {
|
|
@@ -165,7 +171,11 @@ nodes = [
|
|
|
{ name = \"b\", rank = 1 },
|
|
{ name = \"b\", rank = 1 },
|
|
|
]
|
|
]
|
|
|
";
|
|
";
|
|
|
- let updates = vec![("a".to_string(), "SHA256:newA".to_string())];
|
|
|
|
|
|
|
+ let updates = vec![(
|
|
|
|
|
+ "a".to_string(),
|
|
|
|
|
+ "10.0.0.1".to_string(),
|
|
|
|
|
+ "SHA256:newA".to_string(),
|
|
|
|
|
+ )];
|
|
|
let result = update_host_keys_text(content, &updates);
|
|
let result = update_host_keys_text(content, &updates);
|
|
|
// a was updated to include host_key
|
|
// a was updated to include host_key
|
|
|
assert!(result.contains("name = \"a\", rank = 0, host_key = \"SHA256:newA\""));
|
|
assert!(result.contains("name = \"a\", rank = 0, host_key = \"SHA256:newA\""));
|