fix(agent): remove system archive tool dependency
This commit is contained in:
parent
054702e223
commit
2980e32c9d
|
|
@ -1891,6 +1891,7 @@ dependencies = [
|
|||
"deadpool-postgres",
|
||||
"duckdb",
|
||||
"encoding_rs",
|
||||
"flate2",
|
||||
"font-kit",
|
||||
"futures",
|
||||
"iana-time-zone",
|
||||
|
|
@ -1920,6 +1921,7 @@ dependencies = [
|
|||
"sqlparser",
|
||||
"ssfmt",
|
||||
"sysinfo",
|
||||
"tar",
|
||||
"tempfile",
|
||||
"tiberius",
|
||||
"tokio",
|
||||
|
|
|
|||
|
|
@ -95,5 +95,7 @@ async-trait = "0.1"
|
|||
bytes = "1"
|
||||
font-kit = { version = "0.14.3", optional = true }
|
||||
zip = { version = "4", default-features = false, features = ["deflate"] }
|
||||
flate2 = "1"
|
||||
tar = "0.4"
|
||||
sysinfo = { version = "0.32", features = ["system"] }
|
||||
tempfile = "3"
|
||||
|
|
|
|||
|
|
@ -1724,17 +1724,102 @@ fn db_type_for_jar_offline_entry(registry: &AgentRegistry, name: &str) -> Option
|
|||
}
|
||||
|
||||
fn extract_tar_gz(archive: &Path, dest: &Path) -> Result<(), String> {
|
||||
std::fs::create_dir_all(dest).map_err(|e| e.to_string())?;
|
||||
let status = crate::process::new_std_command("tar")
|
||||
.args(["xzf", &archive.to_string_lossy(), "-C", &dest.to_string_lossy(), "--strip-components=1"])
|
||||
.status()
|
||||
.map_err(|e| format!("Failed to extract archive: {e}"))?;
|
||||
if !status.success() {
|
||||
return Err("Failed to extract JRE archive".to_string());
|
||||
let parent = dest.parent().ok_or_else(|| format!("Invalid JRE destination: {}", dest.display()))?;
|
||||
std::fs::create_dir_all(parent).map_err(|e| format!("Failed to create JRE directory: {e}"))?;
|
||||
|
||||
let staging = tempfile::Builder::new()
|
||||
.prefix(".jre-extract-")
|
||||
.tempdir_in(parent)
|
||||
.map_err(|e| format!("Failed to create JRE extraction directory: {e}"))?;
|
||||
let file = std::fs::File::open(archive).map_err(|e| format!("Failed to open JRE archive: {e}"))?;
|
||||
let decoder = flate2::read::GzDecoder::new(file);
|
||||
tar::Archive::new(decoder).unpack(staging.path()).map_err(|e| format!("Failed to extract JRE archive: {e}"))?;
|
||||
|
||||
let mut roots = std::fs::read_dir(staging.path())
|
||||
.map_err(|e| format!("Failed to inspect extracted JRE archive: {e}"))?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(|e| format!("Failed to inspect extracted JRE archive: {e}"))?;
|
||||
if roots.len() != 1 {
|
||||
return Err("Invalid JRE archive: expected a single top-level directory".to_string());
|
||||
}
|
||||
|
||||
let root = roots.pop().expect("root count checked above");
|
||||
if !root.file_type().map_err(|e| format!("Failed to inspect extracted JRE archive: {e}"))?.is_dir() {
|
||||
return Err("Invalid JRE archive: expected a top-level directory".to_string());
|
||||
}
|
||||
|
||||
std::fs::create_dir_all(dest).map_err(|e| format!("Failed to create JRE directory: {e}"))?;
|
||||
for entry in std::fs::read_dir(root.path()).map_err(|e| format!("Failed to inspect extracted JRE archive: {e}"))? {
|
||||
let entry = entry.map_err(|e| format!("Failed to inspect extracted JRE archive: {e}"))?;
|
||||
std::fs::rename(entry.path(), dest.join(entry.file_name()))
|
||||
.map_err(|e| format!("Failed to install extracted JRE: {e}"))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod jre_archive_tests {
|
||||
use super::*;
|
||||
use std::io::Cursor;
|
||||
|
||||
fn append_file(
|
||||
builder: &mut tar::Builder<flate2::write::GzEncoder<std::fs::File>>,
|
||||
path: &str,
|
||||
data: &[u8],
|
||||
mode: u32,
|
||||
) {
|
||||
let mut header = tar::Header::new_gnu();
|
||||
header.set_size(data.len() as u64);
|
||||
header.set_mode(mode);
|
||||
header.set_cksum();
|
||||
builder.append_data(&mut header, path, Cursor::new(data)).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_jre_archive_without_system_tools_and_strips_top_level_directory() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let archive_path = temp.path().join("jre.tar.gz");
|
||||
let encoder = flate2::write::GzEncoder::new(
|
||||
std::fs::File::create(&archive_path).unwrap(),
|
||||
flate2::Compression::default(),
|
||||
);
|
||||
let mut builder = tar::Builder::new(encoder);
|
||||
append_file(&mut builder, "jdk-21/bin/java", b"java", 0o755);
|
||||
append_file(&mut builder, "jdk-21/conf/release", b"JAVA_VERSION=21", 0o644);
|
||||
builder.into_inner().unwrap().finish().unwrap();
|
||||
|
||||
let dest = temp.path().join("managed-jre");
|
||||
extract_tar_gz(&archive_path, &dest).unwrap();
|
||||
|
||||
assert_eq!(std::fs::read(dest.join("bin/java")).unwrap(), b"java");
|
||||
assert_eq!(std::fs::read(dest.join("conf/release")).unwrap(), b"JAVA_VERSION=21");
|
||||
assert!(!dest.join("jdk-21").exists());
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
assert_eq!(std::fs::metadata(dest.join("bin/java")).unwrap().permissions().mode() & 0o777, 0o755);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_jre_archive_without_a_single_top_level_directory() {
|
||||
let temp = tempfile::tempdir().unwrap();
|
||||
let archive_path = temp.path().join("jre.tar.gz");
|
||||
let encoder = flate2::write::GzEncoder::new(
|
||||
std::fs::File::create(&archive_path).unwrap(),
|
||||
flate2::Compression::default(),
|
||||
);
|
||||
let mut builder = tar::Builder::new(encoder);
|
||||
append_file(&mut builder, "jdk-21/bin/java", b"java", 0o755);
|
||||
append_file(&mut builder, "unexpected/readme.txt", b"unexpected", 0o644);
|
||||
builder.into_inner().unwrap().finish().unwrap();
|
||||
|
||||
let error = extract_tar_gz(&archive_path, &temp.path().join("managed-jre")).unwrap_err();
|
||||
assert!(error.contains("single top-level directory"), "unexpected error: {error}");
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn import_agent_driver(am: &AgentManager, db_type: &str, source_path: &Path) -> Result<(), String> {
|
||||
// Manual imports replace the same artifact paths as downloads. Reuse the
|
||||
// install operation and per-driver locks so an import cannot race an
|
||||
|
|
@ -2143,13 +2228,10 @@ mod agent_registry_install_tests {
|
|||
let java_path = payload.join(relative_java_path);
|
||||
std::fs::create_dir_all(java_path.parent().unwrap()).unwrap();
|
||||
std::fs::write(java_path, b"java").unwrap();
|
||||
let archive = archive_root.join("runtime.tar.gz");
|
||||
let status = crate::process::new_std_command("tar")
|
||||
.args(["czf", &archive.to_string_lossy(), "-C", &archive_root.to_string_lossy(), "payload"])
|
||||
.status()
|
||||
.unwrap();
|
||||
assert!(status.success());
|
||||
std::fs::read(archive).unwrap()
|
||||
let encoder = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::default());
|
||||
let mut builder = tar::Builder::new(encoder);
|
||||
builder.append_dir_all("payload", &payload).unwrap();
|
||||
builder.into_inner().unwrap().finish().unwrap()
|
||||
}
|
||||
|
||||
fn write_cached_jre_download(am: &AgentManager, jre_key: &str, version: &str, url: &str, archive: &[u8]) {
|
||||
|
|
|
|||
|
|
@ -963,22 +963,14 @@ fn test_jre_archive_bytes() -> Vec<u8> {
|
|||
let root = test_path("jre-archive");
|
||||
let runtime_root = root.join("dbx-jre");
|
||||
let bin_dir = runtime_root.join("bin");
|
||||
let archive = root.join("jre.tar.gz");
|
||||
std::fs::create_dir_all(&bin_dir).unwrap();
|
||||
std::fs::write(bin_dir.join("java"), b"java").unwrap();
|
||||
std::fs::write(bin_dir.join("java.exe"), b"java").unwrap();
|
||||
|
||||
let status = std::process::Command::new("tar")
|
||||
.arg("czf")
|
||||
.arg(&archive)
|
||||
.arg("-C")
|
||||
.arg(&root)
|
||||
.arg("dbx-jre")
|
||||
.status()
|
||||
.unwrap();
|
||||
assert!(status.success());
|
||||
|
||||
let bytes = std::fs::read(&archive).unwrap();
|
||||
let encoder = flate2::write::GzEncoder::new(Vec::new(), flate2::Compression::default());
|
||||
let mut builder = tar::Builder::new(encoder);
|
||||
builder.append_dir_all("dbx-jre", &runtime_root).unwrap();
|
||||
let bytes = builder.into_inner().unwrap().finish().unwrap();
|
||||
std::fs::remove_dir_all(root).ok();
|
||||
bytes
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue