mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-30 20:14:49 +00:00
Compare commits
3 Commits
feat/gener
...
fix/migrat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9eb26db050 | ||
|
|
6cefc27c5f | ||
|
|
2b676808a9 |
24
.github/workflows/startos-iso.yaml
vendored
24
.github/workflows/startos-iso.yaml
vendored
@@ -89,9 +89,9 @@ jobs:
|
|||||||
"riscv64": "ubuntu-latest"
|
"riscv64": "ubuntu-latest"
|
||||||
}')[matrix.arch],
|
}')[matrix.arch],
|
||||||
fromJson('{
|
fromJson('{
|
||||||
"x86_64": "ubuntu-24.04-32-cores",
|
"x86_64": "amd64-fast",
|
||||||
"aarch64": "ubuntu-24.04-arm-32-cores",
|
"aarch64": "aarch64-fast",
|
||||||
"riscv64": "ubuntu-24.04-32-cores"
|
"riscv64": "amd64-fast"
|
||||||
}')[matrix.arch]
|
}')[matrix.arch]
|
||||||
)
|
)
|
||||||
)[github.event.inputs.runner == 'fast']
|
)[github.event.inputs.runner == 'fast']
|
||||||
@@ -153,15 +153,15 @@ jobs:
|
|||||||
"riscv64-nonfree": "ubuntu-24.04-arm",
|
"riscv64-nonfree": "ubuntu-24.04-arm",
|
||||||
}')[matrix.platform],
|
}')[matrix.platform],
|
||||||
fromJson('{
|
fromJson('{
|
||||||
"x86_64": "ubuntu-24.04-8-cores",
|
"x86_64": "amd64-fast",
|
||||||
"x86_64-nonfree": "ubuntu-24.04-8-cores",
|
"x86_64-nonfree": "amd64-fast",
|
||||||
"x86_64-nvidia": "ubuntu-24.04-8-cores",
|
"x86_64-nvidia": "amd64-fast",
|
||||||
"aarch64": "ubuntu-24.04-arm-8-cores",
|
"aarch64": "aarch64-fast",
|
||||||
"aarch64-nonfree": "ubuntu-24.04-arm-8-cores",
|
"aarch64-nonfree": "aarch64-fast",
|
||||||
"aarch64-nvidia": "ubuntu-24.04-arm-8-cores",
|
"aarch64-nvidia": "aarch64-fast",
|
||||||
"raspberrypi": "ubuntu-24.04-arm-8-cores",
|
"raspberrypi": "aarch64-fast",
|
||||||
"riscv64": "ubuntu-24.04-8-cores",
|
"riscv64": "amd64-fast",
|
||||||
"riscv64-nonfree": "ubuntu-24.04-8-cores",
|
"riscv64-nonfree": "amd64-fast",
|
||||||
}')[matrix.platform]
|
}')[matrix.platform]
|
||||||
)
|
)
|
||||||
)[github.event.inputs.runner == 'fast']
|
)[github.event.inputs.runner == 'fast']
|
||||||
|
|||||||
@@ -344,17 +344,12 @@ pub async fn mount_fs<P: AsRef<Path>>(
|
|||||||
.arg(&blockdev_path)
|
.arg(&blockdev_path)
|
||||||
.invoke(ErrorKind::DiskManagement)
|
.invoke(ErrorKind::DiskManagement)
|
||||||
.await?;
|
.await?;
|
||||||
// Delete ext2_saved subvolume and defragment after conversion
|
// Defragment after conversion for optimal performance
|
||||||
let tmp_mount = datadir.as_ref().join(format!("{name}.convert-tmp"));
|
let tmp_mount = datadir.as_ref().join(format!("{name}.convert-tmp"));
|
||||||
tokio::fs::create_dir_all(&tmp_mount).await?;
|
tokio::fs::create_dir_all(&tmp_mount).await?;
|
||||||
BlockDev::new(&blockdev_path)
|
BlockDev::new(&blockdev_path)
|
||||||
.mount(&tmp_mount, ReadWrite)
|
.mount(&tmp_mount, ReadWrite)
|
||||||
.await?;
|
.await?;
|
||||||
Command::new("btrfs")
|
|
||||||
.args(["subvolume", "delete"])
|
|
||||||
.arg(tmp_mount.join("ext2_saved"))
|
|
||||||
.invoke(ErrorKind::DiskManagement)
|
|
||||||
.await?;
|
|
||||||
Command::new("btrfs")
|
Command::new("btrfs")
|
||||||
.args(["filesystem", "defragment", "-r"])
|
.args(["filesystem", "defragment", "-r"])
|
||||||
.arg(&tmp_mount)
|
.arg(&tmp_mount)
|
||||||
|
|||||||
@@ -40,6 +40,102 @@ lazy_static::lazy_static! {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Detect the LC_COLLATE / LC_CTYPE the cluster was created with and generate
|
||||||
|
/// those locales if they are missing from the running system. Older installs
|
||||||
|
/// may have been initialized with a locale (e.g. en_GB.UTF-8) that the current
|
||||||
|
/// image does not ship. Without it PostgreSQL starts but refuses
|
||||||
|
/// connections, breaking the migration.
|
||||||
|
async fn ensure_cluster_locale(pg_version: u32) -> Result<(), Error> {
|
||||||
|
let cluster_dir = format!("/var/lib/postgresql/{pg_version}/main");
|
||||||
|
let pg_controldata = format!("/usr/lib/postgresql/{pg_version}/bin/pg_controldata");
|
||||||
|
|
||||||
|
let output = Command::new(&pg_controldata)
|
||||||
|
.arg(&cluster_dir)
|
||||||
|
.kill_on_drop(true)
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::piped())
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.with_kind(crate::ErrorKind::Database)?;
|
||||||
|
if !output.status.success() {
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
tracing::warn!("pg_controldata failed, skipping locale check: {stderr}");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||||
|
let mut locales_needed = Vec::new();
|
||||||
|
for line in stdout.lines() {
|
||||||
|
let locale = if let Some(rest) = line.strip_prefix("LC_COLLATE:") {
|
||||||
|
rest.trim()
|
||||||
|
} else if let Some(rest) = line.strip_prefix("LC_CTYPE:") {
|
||||||
|
rest.trim()
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if !locale.is_empty() && locale != "C" && locale != "POSIX" {
|
||||||
|
locales_needed.push(locale.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
locales_needed.sort();
|
||||||
|
locales_needed.dedup();
|
||||||
|
|
||||||
|
if locales_needed.is_empty() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check which locales are already available.
|
||||||
|
let available = Command::new("locale")
|
||||||
|
.arg("-a")
|
||||||
|
.kill_on_drop(true)
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::null())
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.map(|o| String::from_utf8_lossy(&o.stdout).to_string())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let mut need_gen = false;
|
||||||
|
for locale in &locales_needed {
|
||||||
|
// locale -a normalizes e.g. "en_GB.UTF-8" → "en_GB.utf8"
|
||||||
|
let normalized = locale.replace("-", "").to_lowercase();
|
||||||
|
if available.lines().any(|l| l.replace("-", "").to_lowercase() == normalized) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Debian's locale-gen ignores positional args — the locale must be
|
||||||
|
// uncommented in /etc/locale.gen or appended to it.
|
||||||
|
tracing::info!("Enabling missing locale for PostgreSQL cluster: {locale}");
|
||||||
|
let locale_gen_path = Path::new("/etc/locale.gen");
|
||||||
|
let contents = tokio::fs::read_to_string(locale_gen_path)
|
||||||
|
.await
|
||||||
|
.unwrap_or_default();
|
||||||
|
// Try to uncomment an existing entry first, otherwise append.
|
||||||
|
let entry = format!("{locale} UTF-8");
|
||||||
|
let commented = format!("# {entry}");
|
||||||
|
if contents.contains(&commented) {
|
||||||
|
let updated = contents.replace(&commented, &entry);
|
||||||
|
tokio::fs::write(locale_gen_path, updated).await?;
|
||||||
|
} else if !contents.contains(&entry) {
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
let mut f = tokio::fs::OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(locale_gen_path)
|
||||||
|
.await?;
|
||||||
|
f.write_all(format!("\n{entry}\n").as_bytes()).await?;
|
||||||
|
}
|
||||||
|
need_gen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if need_gen {
|
||||||
|
Command::new("locale-gen")
|
||||||
|
.invoke(crate::ErrorKind::Database)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn init_postgres(datadir: impl AsRef<Path>) -> Result<PgPool, Error> {
|
async fn init_postgres(datadir: impl AsRef<Path>) -> Result<PgPool, Error> {
|
||||||
let db_dir = datadir.as_ref().join("main/postgresql");
|
let db_dir = datadir.as_ref().join("main/postgresql");
|
||||||
@@ -91,6 +187,12 @@ async fn init_postgres(datadir: impl AsRef<Path>) -> Result<PgPool, Error> {
|
|||||||
|
|
||||||
crate::disk::mount::util::bind(&db_dir, "/var/lib/postgresql", false).await?;
|
crate::disk::mount::util::bind(&db_dir, "/var/lib/postgresql", false).await?;
|
||||||
|
|
||||||
|
// The cluster may have been created with a locale not present on the
|
||||||
|
// current image (e.g. en_GB.UTF-8 on a server that predates the trixie
|
||||||
|
// image). Detect and generate it before starting PostgreSQL, otherwise
|
||||||
|
// PG will start but refuse connections.
|
||||||
|
ensure_cluster_locale(pg_version).await?;
|
||||||
|
|
||||||
Command::new("systemctl")
|
Command::new("systemctl")
|
||||||
.arg("start")
|
.arg("start")
|
||||||
.arg(format!("postgresql@{pg_version}-main.service"))
|
.arg(format!("postgresql@{pg_version}-main.service"))
|
||||||
|
|||||||
Reference in New Issue
Block a user