release(v1.7.31-alpha): idempotent IndeedHub install + auto-merge default mirrors/registries + 3rd OVH update mirror
- Backend: install.rs registry reachability probe now strips the `host[:port]/namespace` suffix before appending `/v2/` (the Docker V2 API lives at the host root, not under the namespace) and accepts HTTP 405 in addition to 200/401 as "registry daemon alive". This fixes false "unreachable" reports on the Test button for Gitea and other registries that protect their /v2/ endpoint. - Backend: stacks.rs install_indeedhub_stack now force-removes any leftover indeedhub-* containers and indeedhub-net before creating the stack. A partial install (or the old first-boot stub racing the installer) used to leave containers around that blocked re-install with "name already in use". Re-running the App Store install now self-heals. - Backend: registry.rs load_registries auto-merges any default registry URLs missing from the saved config (appended with priority max+10+i, persisted). Lets new default mirrors (e.g. Server 3 OVH) roll out to existing nodes without manual config edits. Explicit removals still stick — URLs absent from disk AND absent from defaults stay gone. - Backend: update.rs adds DEFAULT_TERTIARY_MIRROR_URL at http://146.59.87.168:3000/ (Server 3 OVH) to default_mirrors, with the same auto-merge-on-load behavior as registries. Test updated for 3-mirror default (.160, tx1138, .168). - Scripts: dropped the first-boot IndeedHub stub (~38 lines in first-boot-containers.sh §8b). It predated the proper stack installer, raced it, and was the main source of the name-conflict mess the stacks.rs cleanup above now also guards against. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
18f0929614
commit
682b93f2d6
@@ -68,6 +68,10 @@ const DEFAULT_UPDATE_MANIFEST_URL: &str =
|
||||
/// is slow or unreachable.
|
||||
const DEFAULT_SECONDARY_MIRROR_URL: &str =
|
||||
"http://23.182.128.160:3000/lfg2025/archy/raw/branch/main/releases/manifest.json";
|
||||
/// Tertiary mirror on a separate OVH VPS — independent network path so
|
||||
/// a single-provider outage doesn't knock out all three mirrors.
|
||||
const DEFAULT_TERTIARY_MIRROR_URL: &str =
|
||||
"http://146.59.87.168:3000/lfg2025/archy/raw/branch/main/releases/manifest.json";
|
||||
const UPDATE_STATE_FILE: &str = "update_state.json";
|
||||
const UPDATE_MIRRORS_FILE: &str = "update-mirrors.json";
|
||||
|
||||
@@ -97,13 +101,24 @@ fn default_mirrors() -> Vec<UpdateMirror> {
|
||||
url: DEFAULT_UPDATE_MANIFEST_URL.to_string(),
|
||||
label: "Server 2 (tx1138)".to_string(),
|
||||
},
|
||||
UpdateMirror {
|
||||
url: DEFAULT_TERTIARY_MIRROR_URL.to_string(),
|
||||
label: "Server 3 (OVH)".to_string(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
/// Load the operator-configured mirror list. Returns defaults if the
|
||||
/// file doesn't exist yet, so a node OTA'd from a pre-mirrors release
|
||||
/// starts with both Server 1 and Server 2 available without any manual
|
||||
/// config.
|
||||
/// starts with the current default mirrors available without any
|
||||
/// manual config.
|
||||
///
|
||||
/// Migration: any default mirror URL that isn't already in the saved
|
||||
/// list gets appended at the end. This lets us add new default mirrors
|
||||
/// (e.g. a new Server 3) and have them appear on existing nodes after
|
||||
/// an update, without requiring manual config edits. Explicit removals
|
||||
/// stick — once an operator removes a URL it stays gone unless it's
|
||||
/// later re-added to defaults.
|
||||
pub async fn load_mirrors(data_dir: &Path) -> Result<Vec<UpdateMirror>> {
|
||||
let path = mirrors_path(data_dir);
|
||||
if !path.exists() {
|
||||
@@ -112,13 +127,27 @@ pub async fn load_mirrors(data_dir: &Path) -> Result<Vec<UpdateMirror>> {
|
||||
let bytes = fs::read(&path)
|
||||
.await
|
||||
.with_context(|| format!("read {}", path.display()))?;
|
||||
let list: Vec<UpdateMirror> =
|
||||
let mut list: Vec<UpdateMirror> =
|
||||
serde_json::from_slice(&bytes).with_context(|| format!("parse {}", path.display()))?;
|
||||
if list.is_empty() {
|
||||
Ok(default_mirrors())
|
||||
} else {
|
||||
Ok(list)
|
||||
return Ok(default_mirrors());
|
||||
}
|
||||
|
||||
// Merge in any default URLs the saved config is missing.
|
||||
let known: std::collections::HashSet<String> =
|
||||
list.iter().map(|m| m.url.clone()).collect();
|
||||
let defaults = default_mirrors();
|
||||
let mut added = false;
|
||||
for def in &defaults {
|
||||
if !known.contains(&def.url) {
|
||||
list.push(def.clone());
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
if added {
|
||||
let _ = save_mirrors(data_dir, &list).await;
|
||||
}
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
pub async fn save_mirrors(data_dir: &Path, mirrors: &[UpdateMirror]) -> Result<()> {
|
||||
@@ -1187,9 +1216,10 @@ mod tests {
|
||||
async fn test_load_mirrors_returns_defaults_when_absent() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let list = load_mirrors(dir.path()).await.unwrap();
|
||||
assert_eq!(list.len(), 2);
|
||||
assert!(list[0].url.contains("git.tx1138.com"));
|
||||
assert!(list[1].url.contains("23.182.128.160"));
|
||||
assert_eq!(list.len(), 3);
|
||||
assert!(list[0].url.contains("23.182.128.160"));
|
||||
assert!(list[1].url.contains("git.tx1138.com"));
|
||||
assert!(list[2].url.contains("146.59.87.168"));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
Reference in New Issue
Block a user