all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] applied: [PATCH backup] use new apt/apt-api-types crate
@ 2024-07-09  6:19 Wolfgang Bumiller
  0 siblings, 0 replies; only message in thread
From: Wolfgang Bumiller @ 2024-07-09  6:19 UTC (permalink / raw)
  To: pbs-devel

From: Dietmar Maurer <dietmar@proxmox.com>

---
 Cargo.toml               |   6 +-
 pbs-api-types/Cargo.toml |   1 +
 pbs-api-types/src/lib.rs |  35 +---
 pbs-buildcfg/src/lib.rs  |   2 +
 src/api2/node/apt.rs     | 402 +++++----------------------------------
 src/bin/pbs2to3.rs       |  26 +--
 src/tools/apt.rs         | 293 ----------------------------
 src/tools/mod.rs         |   1 -
 8 files changed, 72 insertions(+), 694 deletions(-)
 delete mode 100644 src/tools/apt.rs

diff --git a/Cargo.toml b/Cargo.toml
index 17aeeb21..8120526a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -53,7 +53,9 @@ path = "src/lib.rs"
 
 [workspace.dependencies]
 # proxmox workspace
-proxmox-apt = "0.10.5"
+proxmox-config-digest = "0.1.0"
+proxmox-apt = { version = "0.11", features = [ "cache" ] }
+proxmox-apt-api-types = "1.0"
 proxmox-async = "0.4"
 proxmox-auth-api = "0.4"
 proxmox-borrow = "1"
@@ -203,9 +205,11 @@ zstd.workspace = true
 
 # proxmox workspace
 proxmox-apt.workspace = true
+proxmox-apt-api-types.workspace = true
 proxmox-async.workspace = true
 proxmox-auth-api = { workspace = true, features = [ "api", "pam-authenticator" ] }
 proxmox-compression.workspace = true
+proxmox-config-digest.workspace = true
 proxmox-http = { workspace = true, features = [ "client-trait", "proxmox-async", "rate-limited-stream" ] } # pbs-client doesn't use these
 proxmox-human-byte.workspace = true
 proxmox-io.workspace = true
diff --git a/pbs-api-types/Cargo.toml b/pbs-api-types/Cargo.toml
index 94ab583b..808ff514 100644
--- a/pbs-api-types/Cargo.toml
+++ b/pbs-api-types/Cargo.toml
@@ -16,6 +16,7 @@ serde.workspace = true
 serde_plain.workspace = true
 
 proxmox-auth-api = { workspace = true, features = [ "api-types" ] }
+proxmox-apt-api-types.workspace = true
 proxmox-human-byte.workspace = true
 proxmox-lang.workspace=true
 proxmox-schema = { workspace = true, features = [ "api-macro" ] }
diff --git a/pbs-api-types/src/lib.rs b/pbs-api-types/src/lib.rs
index a3ad185b..40bcd8f1 100644
--- a/pbs-api-types/src/lib.rs
+++ b/pbs-api-types/src/lib.rs
@@ -52,6 +52,13 @@ pub use proxmox_schema::api_types::{SYSTEMD_DATETIME_FORMAT, TIME_ZONE_SCHEMA};
 
 use proxmox_schema::api_types::{DNS_NAME_STR, IPRE_BRACKET_STR};
 
+// re-export APT API types
+pub use proxmox_apt_api_types::{
+    APTChangeRepositoryOptions, APTGetChangelogOptions, APTRepositoriesResult, APTRepositoryFile,
+    APTRepositoryFileError, APTRepositoryHandle, APTRepositoryInfo, APTStandardRepository,
+    APTUpdateInfo, APTUpdateOptions,
+};
+
 #[rustfmt::skip]
 pub const BACKUP_ID_RE: &str = r"[A-Za-z0-9_][A-Za-z0-9._\-]*";
 
@@ -249,34 +256,6 @@ pub const PASSWORD_HINT_SCHEMA: Schema = StringSchema::new("Password hint.")
     .max_length(64)
     .schema();
 
-#[api()]
-#[derive(Debug, Clone, Serialize, Deserialize)]
-#[serde(rename_all = "PascalCase")]
-/// Describes a package for which an update is available.
-pub struct APTUpdateInfo {
-    /// Package name
-    pub package: String,
-    /// Package title
-    pub title: String,
-    /// Package architecture
-    pub arch: String,
-    /// Human readable package description
-    pub description: String,
-    /// New version to be updated to
-    pub version: String,
-    /// Old version currently installed
-    pub old_version: String,
-    /// Package origin
-    pub origin: String,
-    /// Package priority in human-readable form
-    pub priority: String,
-    /// Package section
-    pub section: String,
-    /// Custom extra field for additional package information
-    #[serde(skip_serializing_if = "Option::is_none")]
-    pub extra_info: Option<String>,
-}
-
 #[api()]
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
 #[serde(rename_all = "lowercase")]
diff --git a/pbs-buildcfg/src/lib.rs b/pbs-buildcfg/src/lib.rs
index 8ce2f7e5..3d087015 100644
--- a/pbs-buildcfg/src/lib.rs
+++ b/pbs-buildcfg/src/lib.rs
@@ -98,6 +98,8 @@ pub const PROXMOX_BACKUP_KERNEL_FN: &str =
 
 pub const PROXMOX_BACKUP_SUBSCRIPTION_FN: &str = configdir!("/subscription");
 
+pub const APT_PKG_STATE_FN: &str = concat!(PROXMOX_BACKUP_STATE_DIR_M!(), "/pkg-state.json");
+
 /// Prepend configuration directory to a file name
 ///
 /// This is a simply way to get the full path for configuration files.
diff --git a/src/api2/node/apt.rs b/src/api2/node/apt.rs
index 0ae353d5..7b604cc0 100644
--- a/src/api2/node/apt.rs
+++ b/src/api2/node/apt.rs
@@ -1,26 +1,21 @@
-use anyhow::{bail, format_err, Error};
-use serde_json::{json, Value};
-use std::os::unix::prelude::OsStrExt;
+use anyhow::{bail, Error};
 
+use proxmox_config_digest::ConfigDigest;
 use proxmox_router::{
     list_subdirs_api_method, Permission, Router, RpcEnvironment, RpcEnvironmentType, SubdirMap,
 };
 use proxmox_schema::api;
 use proxmox_sys::fs::{replace_file, CreateOptions};
 
-use proxmox_apt::repositories::{
-    APTRepositoryFile, APTRepositoryFileError, APTRepositoryHandle, APTRepositoryInfo,
-    APTStandardRepository,
+use proxmox_apt_api_types::{
+    APTChangeRepositoryOptions, APTGetChangelogOptions, APTRepositoriesResult, APTRepositoryHandle,
+    APTUpdateInfo, APTUpdateOptions,
 };
 use proxmox_http::ProxyConfig;
 
-use pbs_api_types::{
-    APTUpdateInfo, NODE_SCHEMA, PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, PROXMOX_CONFIG_DIGEST_SCHEMA,
-    UPID_SCHEMA,
-};
+use pbs_api_types::{NODE_SCHEMA, PRIV_SYS_AUDIT, PRIV_SYS_MODIFY, UPID_SCHEMA};
 
 use crate::config::node;
-use crate::tools::apt;
 use proxmox_rest_server::WorkerTask;
 
 #[api(
@@ -44,16 +39,8 @@ use proxmox_rest_server::WorkerTask;
     },
 )]
 /// List available APT updates
-fn apt_update_available(_param: Value) -> Result<Value, Error> {
-    if let Ok(false) = apt::pkg_cache_expired() {
-        if let Ok(Some(cache)) = apt::read_pkg_state() {
-            return Ok(json!(cache.package_status));
-        }
-    }
-
-    let cache = apt::update_cache()?;
-
-    Ok(json!(cache.package_status))
+pub fn apt_update_available() -> Result<Vec<APTUpdateInfo>, Error> {
+    proxmox_apt::list_available_apt_update(pbs_buildcfg::APT_PKG_STATE_FN)
 }
 
 pub fn update_apt_proxy_config(proxy_config: Option<&ProxyConfig>) -> Result<(), Error> {
@@ -83,45 +70,6 @@ fn read_and_update_proxy_config() -> Result<Option<ProxyConfig>, Error> {
     Ok(proxy_config)
 }
 
-fn do_apt_update(worker: &WorkerTask, quiet: bool) -> Result<(), Error> {
-    if !quiet {
-        worker.log_message("starting apt-get update")
-    }
-
-    read_and_update_proxy_config()?;
-
-    let mut command = std::process::Command::new("apt-get");
-    command.arg("update");
-
-    // apt "errors" quite easily, and run_command is a bit rigid, so handle this inline for now.
-    let output = command
-        .output()
-        .map_err(|err| format_err!("failed to execute {:?} - {}", command, err))?;
-
-    if !quiet {
-        worker.log_message(String::from_utf8(output.stdout)?);
-    }
-
-    // TODO: improve run_command to allow outputting both, stderr and stdout
-    if !output.status.success() {
-        if output.status.code().is_some() {
-            let msg = String::from_utf8(output.stderr)
-                .map(|m| {
-                    if m.is_empty() {
-                        String::from("no error message")
-                    } else {
-                        m
-                    }
-                })
-                .unwrap_or_else(|_| String::from("non utf8 error message (suppressed)"));
-            worker.log_warning(msg);
-        } else {
-            bail!("terminated by signal");
-        }
-    }
-    Ok(())
-}
-
 #[api(
     protected: true,
     input: {
@@ -129,19 +77,10 @@ fn do_apt_update(worker: &WorkerTask, quiet: bool) -> Result<(), Error> {
             node: {
                 schema: NODE_SCHEMA,
             },
-            notify: {
-                type: bool,
-                description: r#"Send notification mail about new package updates available to the
-                    email address configured for 'root@pam')."#,
-                default: false,
-                optional: true,
-            },
-            quiet: {
-                description: "Only produces output suitable for logging, omitting progress indicators.",
-                type: bool,
-                default: false,
-                optional: true,
-            },
+            options: {
+                type: APTUpdateOptions,
+                flatten: true,
+            }
         },
     },
     returns: {
@@ -153,40 +92,22 @@ fn do_apt_update(worker: &WorkerTask, quiet: bool) -> Result<(), Error> {
 )]
 /// Update the APT database
 pub fn apt_update_database(
-    notify: bool,
-    quiet: bool,
+    options: APTUpdateOptions,
     rpcenv: &mut dyn RpcEnvironment,
 ) -> Result<String, Error> {
     let auth_id = rpcenv.get_auth_id().unwrap();
     let to_stdout = rpcenv.env_type() == RpcEnvironmentType::CLI;
 
-    let upid_str = WorkerTask::new_thread("aptupdate", None, auth_id, to_stdout, move |worker| {
-        do_apt_update(&worker, quiet)?;
-
-        let mut cache = apt::update_cache()?;
-
-        if notify {
-            let mut notified = cache.notified.unwrap_or_default();
-            let mut to_notify: Vec<&APTUpdateInfo> = Vec::new();
-
-            for pkg in &cache.package_status {
-                match notified.insert(pkg.package.to_owned(), pkg.version.to_owned()) {
-                    Some(notified_version) => {
-                        if notified_version != pkg.version {
-                            to_notify.push(pkg);
-                        }
-                    }
-                    None => to_notify.push(pkg),
-                }
-            }
-            if !to_notify.is_empty() {
-                to_notify.sort_unstable_by_key(|k| &k.package);
-                crate::server::send_updates_available(&to_notify)?;
-            }
-            cache.notified = Some(notified);
-            apt::write_pkg_cache(&cache)?;
-        }
-
+    let upid_str = WorkerTask::new_thread("aptupdate", None, auth_id, to_stdout, move |_worker| {
+        read_and_update_proxy_config()?;
+        proxmox_apt::update_database(
+            pbs_buildcfg::APT_PKG_STATE_FN,
+            &options,
+            |updates: &[&APTUpdateInfo]| {
+                crate::server::send_updates_available(updates)?;
+                Ok(())
+            },
+        )?;
         Ok(())
     })?;
 
@@ -200,14 +121,9 @@ pub fn apt_update_database(
             node: {
                 schema: NODE_SCHEMA,
             },
-            name: {
-                description: "Package name to get changelog of.",
-                type: String,
-            },
-            version: {
-                description: "Package version to get changelog of. Omit to use candidate version.",
-                type: String,
-                optional: true,
+            options: {
+                type: APTGetChangelogOptions,
+                flatten: true,
             },
         },
     },
@@ -219,17 +135,8 @@ pub fn apt_update_database(
     },
 )]
 /// Retrieve the changelog of the specified package.
-fn apt_get_changelog(name: String, version: Option<String>) -> Result<Value, Error> {
-    let mut command = std::process::Command::new("apt-get");
-    command.arg("changelog");
-    command.arg("-qq"); // don't display download progress
-    if let Some(ver) = version {
-        command.arg(format!("{name}={ver}"));
-    } else {
-        command.arg(name);
-    }
-    let output = proxmox_sys::command::run_command(command, None)?;
-    Ok(json!(output))
+fn apt_get_changelog(options: APTGetChangelogOptions) -> Result<String, Error> {
+    proxmox_apt::get_changelog(&options)
 }
 
 #[api(
@@ -256,10 +163,8 @@ pub fn get_versions() -> Result<Vec<APTUpdateInfo>, Error> {
     const PACKAGES: &[&str] = &[
         "ifupdown2",
         "libjs-extjs",
-        "proxmox-backup",
         "proxmox-backup-docs",
         "proxmox-backup-client",
-        "proxmox-backup-server",
         "proxmox-mail-forward",
         "proxmox-mini-journalreader",
         "proxmox-offline-mirror-helper",
@@ -269,96 +174,16 @@ pub fn get_versions() -> Result<Vec<APTUpdateInfo>, Error> {
         "zfsutils-linux",
     ];
 
-    fn unknown_package(package: String, extra_info: Option<String>) -> APTUpdateInfo {
-        APTUpdateInfo {
-            package,
-            title: "unknown".into(),
-            arch: "unknown".into(),
-            description: "unknown".into(),
-            version: "unknown".into(),
-            old_version: "unknown".into(),
-            origin: "unknown".into(),
-            priority: "unknown".into(),
-            section: "unknown".into(),
-            extra_info,
-        }
-    }
-
-    let is_kernel =
-        |name: &str| name.starts_with("pve-kernel-") || name.starts_with("proxmox-kernel");
-
-    let mut packages: Vec<APTUpdateInfo> = Vec::new();
-    let pbs_packages = apt::list_installed_apt_packages(
-        |filter| {
-            filter.installed_version == Some(filter.active_version)
-                && (is_kernel(filter.package) || PACKAGES.contains(&filter.package))
-        },
-        None,
-    );
-
-    let running_kernel = format!(
-        "running kernel: {}",
-        std::str::from_utf8(nix::sys::utsname::uname()?.release().as_bytes())?.to_owned()
-    );
-    if let Some(proxmox_backup) = pbs_packages
-        .iter()
-        .find(|pkg| pkg.package == "proxmox-backup")
-    {
-        let mut proxmox_backup = proxmox_backup.clone();
-        proxmox_backup.extra_info = Some(running_kernel);
-        packages.push(proxmox_backup);
-    } else {
-        packages.push(unknown_package(
-            "proxmox-backup".into(),
-            Some(running_kernel),
-        ));
-    }
-
     let version = pbs_buildcfg::PROXMOX_PKG_VERSION;
     let release = pbs_buildcfg::PROXMOX_PKG_RELEASE;
-    let daemon_version_info = Some(format!("running version: {}.{}", version, release));
-    if let Some(pkg) = pbs_packages
-        .iter()
-        .find(|pkg| pkg.package == "proxmox-backup-server")
-    {
-        let mut pkg = pkg.clone();
-        pkg.extra_info = daemon_version_info;
-        packages.push(pkg);
-    } else {
-        packages.push(unknown_package(
-            "proxmox-backup".into(),
-            daemon_version_info,
-        ));
-    }
-
-    let mut kernel_pkgs: Vec<APTUpdateInfo> = pbs_packages
-        .iter()
-        .filter(|pkg| is_kernel(&pkg.package))
-        .cloned()
-        .collect();
-    // make sure the cache mutex gets dropped before the next call to list_installed_apt_packages
-    {
-        let cache = apt_pkg_native::Cache::get_singleton();
-        kernel_pkgs.sort_by(|left, right| {
-            cache
-                .compare_versions(&left.old_version, &right.old_version)
-                .reverse()
-        });
-    }
-    packages.append(&mut kernel_pkgs);
+    let running_daemon_version = format!("running version: {version}.{release}");
 
-    // add entry for all packages we're interested in, even if not installed
-    for pkg in PACKAGES.iter() {
-        if pkg == &"proxmox-backup" || pkg == &"proxmox-backup-server" {
-            continue;
-        }
-        match pbs_packages.iter().find(|item| &item.package == pkg) {
-            Some(apt_pkg) => packages.push(apt_pkg.to_owned()),
-            None => packages.push(unknown_package(pkg.to_string(), None)),
-        }
-    }
-
-    Ok(packages)
+    proxmox_apt::get_package_versions(
+        "proxmox-backup",
+        "proxmox-backup-server",
+        &running_daemon_version,
+        &PACKAGES,
+    )
 }
 
 #[api(
@@ -370,61 +195,15 @@ pub fn get_versions() -> Result<Vec<APTUpdateInfo>, Error> {
         },
     },
     returns: {
-        type: Object,
-        description: "Result from parsing the APT repository files in /etc/apt/.",
-        properties: {
-            files: {
-                description: "List of parsed repository files.",
-                type: Array,
-                items: {
-                    type: APTRepositoryFile,
-                },
-            },
-            errors: {
-                description: "List of problematic files.",
-                type: Array,
-                items: {
-                    type: APTRepositoryFileError,
-                },
-            },
-            digest: {
-                schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
-            },
-            infos: {
-                description: "List of additional information/warnings about the repositories.",
-                items: {
-                    type: APTRepositoryInfo,
-                },
-            },
-            "standard-repos": {
-                description: "List of standard repositories and their configuration status.",
-                items: {
-                    type: APTStandardRepository,
-                },
-            },
-        },
+        type: APTRepositoriesResult,
     },
     access: {
         permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false),
     },
 )]
 /// Get APT repository information.
-pub fn get_repositories() -> Result<Value, Error> {
-    let (files, errors, digest) = proxmox_apt::repositories::repositories()?;
-    let digest = hex::encode(digest);
-
-    let suite = proxmox_apt::repositories::get_current_release_codename()?;
-
-    let infos = proxmox_apt::repositories::check_repositories(&files, suite);
-    let standard_repos = proxmox_apt::repositories::standard_repositories(&files, "pbs", suite);
-
-    Ok(json!({
-        "files": files,
-        "errors": errors,
-        "digest": digest,
-        "infos": infos,
-        "standard-repos": standard_repos,
-    }))
+pub fn get_repositories() -> Result<APTRepositoriesResult, Error> {
+    proxmox_apt::list_repositories("pbs")
 }
 
 #[api(
@@ -437,7 +216,7 @@ pub fn get_repositories() -> Result<Value, Error> {
                 type: APTRepositoryHandle,
             },
             digest: {
-                schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
+                type: ConfigDigest,
                 optional: true,
             },
         },
@@ -451,61 +230,11 @@ pub fn get_repositories() -> Result<Value, Error> {
 /// If the repository is already configured, it will be set to enabled.
 ///
 /// The `digest` parameter asserts that the configuration has not been modified.
-pub fn add_repository(handle: APTRepositoryHandle, digest: Option<String>) -> Result<(), Error> {
-    let (mut files, errors, current_digest) = proxmox_apt::repositories::repositories()?;
-
-    let suite = proxmox_apt::repositories::get_current_release_codename()?;
-
-    if let Some(expected_digest) = digest {
-        let current_digest = hex::encode(current_digest);
-        crate::tools::assert_if_modified(&expected_digest, &current_digest)?;
-    }
-
-    // check if it's already configured first
-    for file in files.iter_mut() {
-        for repo in file.repositories.iter_mut() {
-            if repo.is_referenced_repository(handle, "pbs", &suite.to_string()) {
-                if repo.enabled {
-                    return Ok(());
-                }
-
-                repo.set_enabled(true);
-                file.write()?;
-
-                return Ok(());
-            }
-        }
-    }
-
-    let (repo, path) = proxmox_apt::repositories::get_standard_repository(handle, "pbs", suite);
-
-    if let Some(error) = errors.iter().find(|error| error.path == path) {
-        bail!(
-            "unable to parse existing file {} - {}",
-            error.path,
-            error.error,
-        );
-    }
-
-    if let Some(file) = files
-        .iter_mut()
-        .find(|file| file.path.as_ref() == Some(&path))
-    {
-        file.repositories.push(repo);
-
-        file.write()?;
-    } else {
-        let mut file = match APTRepositoryFile::new(&path)? {
-            Some(file) => file,
-            None => bail!("invalid path - {}", path),
-        };
-
-        file.repositories.push(repo);
-
-        file.write()?;
-    }
-
-    Ok(())
+pub fn add_repository(
+    handle: APTRepositoryHandle,
+    digest: Option<ConfigDigest>,
+) -> Result<(), Error> {
+    proxmox_apt::add_repository_handle("pbs", handle, digest)
 }
 
 #[api(
@@ -522,13 +251,12 @@ pub fn add_repository(handle: APTRepositoryHandle, digest: Option<String>) -> Re
                 description: "Index within the file (starting from 0).",
                 type: usize,
             },
-            enabled: {
-                description: "Whether the repository should be enabled or not.",
-                type: bool,
-                optional: true,
+             options: {
+                type: APTChangeRepositoryOptions,
+                flatten: true,
             },
             digest: {
-                schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
+                type: ConfigDigest,
                 optional: true,
             },
         },
@@ -544,38 +272,10 @@ pub fn add_repository(handle: APTRepositoryHandle, digest: Option<String>) -> Re
 pub fn change_repository(
     path: String,
     index: usize,
-    enabled: Option<bool>,
-    digest: Option<String>,
+    options: APTChangeRepositoryOptions,
+    digest: Option<ConfigDigest>,
 ) -> Result<(), Error> {
-    let (mut files, errors, current_digest) = proxmox_apt::repositories::repositories()?;
-
-    if let Some(expected_digest) = digest {
-        let current_digest = hex::encode(current_digest);
-        crate::tools::assert_if_modified(&expected_digest, &current_digest)?;
-    }
-
-    if let Some(error) = errors.iter().find(|error| error.path == path) {
-        bail!("unable to parse file {} - {}", error.path, error.error);
-    }
-
-    if let Some(file) = files
-        .iter_mut()
-        .find(|file| file.path.as_ref() == Some(&path))
-    {
-        if let Some(repo) = file.repositories.get_mut(index) {
-            if let Some(enabled) = enabled {
-                repo.set_enabled(enabled);
-            }
-
-            file.write()?;
-        } else {
-            bail!("invalid index - {}", index);
-        }
-    } else {
-        bail!("invalid path - {}", path);
-    }
-
-    Ok(())
+    proxmox_apt::change_repository(&path, index, &options, digest)
 }
 
 const SUBDIRS: SubdirMap = &[
diff --git a/src/bin/pbs2to3.rs b/src/bin/pbs2to3.rs
index 3b3714f6..1f895abd 100644
--- a/src/bin/pbs2to3.rs
+++ b/src/bin/pbs2to3.rs
@@ -5,7 +5,8 @@ use anyhow::{format_err, Error};
 use regex::Regex;
 use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
 
-use proxmox_apt::repositories::{self, APTRepositoryFile, APTRepositoryPackageType};
+use proxmox_apt::repositories;
+use proxmox_apt_api_types::{APTRepositoryFile, APTRepositoryPackageType};
 use proxmox_backup::api2::node::apt;
 
 const OLD_SUITE: &str = "bullseye";
@@ -50,19 +51,18 @@ impl Checker {
     fn check_upgradable_packages(&mut self) -> Result<(), Error> {
         self.output.log_info("Checking for package updates..")?;
 
-        let result = Self::get_upgradable_packages();
+        let result = apt::apt_update_available();
         match result {
             Err(err) => {
                 self.output.log_warn(format!("{err}"))?;
                 self.output
                     .log_fail("unable to retrieve list of package updates!")?;
             }
-            Ok(cache) => {
-                if cache.package_status.is_empty() {
+            Ok(package_status) => {
+                if package_status.is_empty() {
                     self.output.log_pass("all packages up-to-date")?;
                 } else {
-                    let pkgs = cache
-                        .package_status
+                    let pkgs = package_status
                         .iter()
                         .map(|pkg| pkg.package.clone())
                         .collect::<Vec<String>>()
@@ -452,20 +452,6 @@ impl Checker {
         }
         Ok(())
     }
-
-    fn get_upgradable_packages() -> Result<proxmox_backup::tools::apt::PkgState, Error> {
-        let cache = if let Ok(false) = proxmox_backup::tools::apt::pkg_cache_expired() {
-            if let Ok(Some(cache)) = proxmox_backup::tools::apt::read_pkg_state() {
-                cache
-            } else {
-                proxmox_backup::tools::apt::update_cache()?
-            }
-        } else {
-            proxmox_backup::tools::apt::update_cache()?
-        };
-
-        Ok(cache)
-    }
 }
 
 #[derive(PartialEq)]
diff --git a/src/tools/apt.rs b/src/tools/apt.rs
deleted file mode 100644
index 900843aa..00000000
--- a/src/tools/apt.rs
+++ /dev/null
@@ -1,293 +0,0 @@
-use std::collections::HashMap;
-use std::collections::HashSet;
-
-use anyhow::{bail, format_err, Error};
-use apt_pkg_native::Cache;
-
-use proxmox_schema::const_regex;
-use proxmox_sys::fs::{file_read_optional_string, replace_file, CreateOptions};
-
-use pbs_api_types::APTUpdateInfo;
-use pbs_buildcfg::PROXMOX_BACKUP_STATE_DIR_M;
-
-const APT_PKG_STATE_FN: &str = concat!(PROXMOX_BACKUP_STATE_DIR_M!(), "/pkg-state.json");
-
-#[derive(Debug, serde::Serialize, serde::Deserialize)]
-/// Some information we cache about the package (update) state, like what pending update version
-/// we already notfied an user about
-pub struct PkgState {
-    /// simple map from package name to most recently notified (emailed) version
-    pub notified: Option<HashMap<String, String>>,
-    /// A list of pending updates
-    pub package_status: Vec<APTUpdateInfo>,
-}
-
-pub fn write_pkg_cache(state: &PkgState) -> Result<(), Error> {
-    let serialized_state = serde_json::to_string(state)?;
-
-    replace_file(
-        APT_PKG_STATE_FN,
-        serialized_state.as_bytes(),
-        CreateOptions::new(),
-        false,
-    )
-    .map_err(|err| format_err!("Error writing package cache - {}", err))?;
-    Ok(())
-}
-
-pub fn read_pkg_state() -> Result<Option<PkgState>, Error> {
-    let serialized_state = match file_read_optional_string(APT_PKG_STATE_FN) {
-        Ok(Some(raw)) => raw,
-        Ok(None) => return Ok(None),
-        Err(err) => bail!("could not read cached package state file - {}", err),
-    };
-
-    serde_json::from_str(&serialized_state)
-        .map(Some)
-        .map_err(|err| format_err!("could not parse cached package status - {}", err))
-}
-
-pub fn pkg_cache_expired() -> Result<bool, Error> {
-    if let Ok(pbs_cache) = std::fs::metadata(APT_PKG_STATE_FN) {
-        let apt_pkgcache = std::fs::metadata("/var/cache/apt/pkgcache.bin")?;
-        let dpkg_status = std::fs::metadata("/var/lib/dpkg/status")?;
-
-        let mtime = pbs_cache.modified()?;
-
-        if apt_pkgcache.modified()? <= mtime && dpkg_status.modified()? <= mtime {
-            return Ok(false);
-        }
-    }
-    Ok(true)
-}
-
-pub fn update_cache() -> Result<PkgState, Error> {
-    // update our cache
-    let all_upgradeable = list_installed_apt_packages(
-        |data| {
-            data.candidate_version == data.active_version
-                && data.installed_version != Some(data.candidate_version)
-        },
-        None,
-    );
-
-    let cache = match read_pkg_state() {
-        Ok(Some(mut cache)) => {
-            cache.package_status = all_upgradeable;
-            cache
-        }
-        _ => PkgState {
-            notified: None,
-            package_status: all_upgradeable,
-        },
-    };
-    write_pkg_cache(&cache)?;
-    Ok(cache)
-}
-
-const_regex! {
-    VERSION_EPOCH_REGEX = r"^\d+:";
-    FILENAME_EXTRACT_REGEX = r"^.*/.*?_(.*)_Packages$";
-}
-
-pub struct FilterData<'a> {
-    /// package name
-    pub package: &'a str,
-    /// this is version info returned by APT
-    pub installed_version: Option<&'a str>,
-    pub candidate_version: &'a str,
-
-    /// this is the version info the filter is supposed to check
-    pub active_version: &'a str,
-}
-
-enum PackagePreSelect {
-    OnlyInstalled,
-    OnlyNew,
-    All,
-}
-
-pub fn list_installed_apt_packages<F: Fn(FilterData) -> bool>(
-    filter: F,
-    only_versions_for: Option<&str>,
-) -> Vec<APTUpdateInfo> {
-    let mut ret = Vec::new();
-    let mut depends = HashSet::new();
-
-    // note: this is not an 'apt update', it just re-reads the cache from disk
-    let mut cache = Cache::get_singleton();
-    cache.reload();
-
-    let mut cache_iter = match only_versions_for {
-        Some(name) => cache.find_by_name(name),
-        None => cache.iter(),
-    };
-
-    loop {
-        match cache_iter.next() {
-            Some(view) => {
-                let di = if only_versions_for.is_some() {
-                    query_detailed_info(PackagePreSelect::All, &filter, view, None)
-                } else {
-                    query_detailed_info(
-                        PackagePreSelect::OnlyInstalled,
-                        &filter,
-                        view,
-                        Some(&mut depends),
-                    )
-                };
-                if let Some(info) = di {
-                    ret.push(info);
-                }
-
-                if only_versions_for.is_some() {
-                    break;
-                }
-            }
-            None => {
-                drop(cache_iter);
-                // also loop through missing dependencies, as they would be installed
-                for pkg in depends.iter() {
-                    let mut iter = cache.find_by_name(pkg);
-                    let view = match iter.next() {
-                        Some(view) => view,
-                        None => continue, // package not found, ignore
-                    };
-
-                    let di = query_detailed_info(PackagePreSelect::OnlyNew, &filter, view, None);
-                    if let Some(info) = di {
-                        ret.push(info);
-                    }
-                }
-                break;
-            }
-        }
-    }
-
-    ret
-}
-
-fn query_detailed_info<'a, F, V>(
-    pre_select: PackagePreSelect,
-    filter: F,
-    view: V,
-    depends: Option<&mut HashSet<String>>,
-) -> Option<APTUpdateInfo>
-where
-    F: Fn(FilterData) -> bool,
-    V: std::ops::Deref<Target = apt_pkg_native::sane::PkgView<'a>>,
-{
-    let current_version = view.current_version();
-    let candidate_version = view.candidate_version();
-
-    let (current_version, candidate_version) = match pre_select {
-        PackagePreSelect::OnlyInstalled => match (current_version, candidate_version) {
-            (Some(cur), Some(can)) => (Some(cur), can), // package installed and there is an update
-            (Some(cur), None) => (Some(cur.clone()), cur), // package installed and up-to-date
-            (None, Some(_)) => return None,             // package could be installed
-            (None, None) => return None,                // broken
-        },
-        PackagePreSelect::OnlyNew => match (current_version, candidate_version) {
-            (Some(_), Some(_)) => return None,
-            (Some(_), None) => return None,
-            (None, Some(can)) => (None, can),
-            (None, None) => return None,
-        },
-        PackagePreSelect::All => match (current_version, candidate_version) {
-            (Some(cur), Some(can)) => (Some(cur), can),
-            (Some(cur), None) => (Some(cur.clone()), cur),
-            (None, Some(can)) => (None, can),
-            (None, None) => return None,
-        },
-    };
-
-    // get additional information via nested APT 'iterators'
-    let mut view_iter = view.versions();
-    while let Some(ver) = view_iter.next() {
-        let package = view.name();
-        let version = ver.version();
-        let mut origin_res = "unknown".to_owned();
-        let mut section_res = "unknown".to_owned();
-        let mut priority_res = "unknown".to_owned();
-        let mut short_desc = package.clone();
-        let mut long_desc = "".to_owned();
-
-        let fd = FilterData {
-            package: package.as_str(),
-            installed_version: current_version.as_deref(),
-            candidate_version: &candidate_version,
-            active_version: &version,
-        };
-
-        if filter(fd) {
-            if let Some(section) = ver.section() {
-                section_res = section;
-            }
-
-            if let Some(prio) = ver.priority_type() {
-                priority_res = prio;
-            }
-
-            // assume every package has only one origin file (not
-            // origin, but origin *file*, for some reason those seem to
-            // be different concepts in APT)
-            let mut origin_iter = ver.origin_iter();
-            let origin = origin_iter.next();
-            if let Some(origin) = origin {
-                if let Some(sd) = origin.short_desc() {
-                    short_desc = sd;
-                }
-
-                if let Some(ld) = origin.long_desc() {
-                    long_desc = ld;
-                }
-
-                // the package files appear in priority order, meaning
-                // the one for the candidate version is first - this is fine
-                // however, as the source package should be the same for all
-                // versions anyway
-                let mut pkg_iter = origin.file();
-                let pkg_file = pkg_iter.next();
-                if let Some(pkg_file) = pkg_file {
-                    if let Some(origin_name) = pkg_file.origin() {
-                        origin_res = origin_name;
-                    }
-                }
-            }
-
-            if let Some(depends) = depends {
-                let mut dep_iter = ver.dep_iter();
-                loop {
-                    let dep = match dep_iter.next() {
-                        Some(dep) if dep.dep_type() != "Depends" => continue,
-                        Some(dep) => dep,
-                        None => break,
-                    };
-
-                    let dep_pkg = dep.target_pkg();
-                    let name = dep_pkg.name();
-
-                    depends.insert(name);
-                }
-            }
-
-            return Some(APTUpdateInfo {
-                package,
-                title: short_desc,
-                arch: view.arch(),
-                description: long_desc,
-                origin: origin_res,
-                version: candidate_version.clone(),
-                old_version: match current_version {
-                    Some(vers) => vers,
-                    None => "".to_owned(),
-                },
-                priority: priority_res,
-                section: section_res,
-                extra_info: None,
-            });
-        }
-    }
-
-    None
-}
diff --git a/src/tools/mod.rs b/src/tools/mod.rs
index f8c4f2d5..322894dd 100644
--- a/src/tools/mod.rs
+++ b/src/tools/mod.rs
@@ -6,7 +6,6 @@ use anyhow::{bail, Error};
 
 use proxmox_http::{client::Client, HttpOptions, ProxyConfig};
 
-pub mod apt;
 pub mod config;
 pub mod disks;
 pub mod fs;
-- 
2.39.2



_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2024-07-09  6:19 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-07-09  6:19 [pbs-devel] applied: [PATCH backup] use new apt/apt-api-types crate Wolfgang Bumiller

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal