public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Stefan Reiter <s.reiter@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH proxmox-backup 6/7] fix #2934: list to-be-installed packages in updates
Date: Wed, 21 Oct 2020 11:41:15 +0200	[thread overview]
Message-ID: <20201021094116.32501-7-s.reiter@proxmox.com> (raw)
In-Reply-To: <20201021094116.32501-1-s.reiter@proxmox.com>

As always, libapt is mocking us with complexity, but we can get the
approximate result we want by retrieving dependencies of all
to-be-updated packages and then seeing if they are missing.

If they are, we assume they will be installed.

For this, query_detailed_info is extended to allow reading details for
non-installed packages, and this is also exposed in
list_installed_apt_packages via 'all_versions_for'. This is necessary so
we can retrieve changelogs for such packages.

Note that we cannot retrieve all that information all the time, as
querying details for packages that aren't installed takes a rather long
time.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
---
 src/api2/node/apt.rs | 116 +++++++++++++++++++++++++++++++++++++------
 1 file changed, 101 insertions(+), 15 deletions(-)

diff --git a/src/api2/node/apt.rs b/src/api2/node/apt.rs
index d8a2e824..20a738c0 100644
--- a/src/api2/node/apt.rs
+++ b/src/api2/node/apt.rs
@@ -1,3 +1,5 @@
+use std::collections::HashSet;
+
 use apt_pkg_native::Cache;
 use anyhow::{Error, bail};
 use serde_json::{json, Value};
@@ -71,34 +73,83 @@ fn get_changelog_url(
 
 struct FilterData<'a> {
     // this is version info returned by APT
-    installed_version: &'a str,
+    installed_version: Option<&'a str>,
     candidate_version: &'a str,
 
     // this is the version info the filter is supposed to check
     active_version: &'a str,
 }
 
-fn list_installed_apt_packages<F: Fn(FilterData) -> bool>(filter: F)
-    -> Vec<APTUpdateInfo> {
+enum PackagePreSelect {
+    OnlyInstalled,
+    OnlyNew,
+    All,
+}
+
+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 = cache.iter();
+    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 = query_detailed_info(&filter, 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;
             }
         }
@@ -108,8 +159,10 @@ fn list_installed_apt_packages<F: Fn(FilterData) -> bool>(filter: F)
 }
 
 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,
@@ -118,11 +171,25 @@ where
     let current_version = view.current_version();
     let candidate_version = view.candidate_version();
 
-    let (current_version, candidate_version) = match (current_version, candidate_version) {
-        (Some(cur), Some(can)) => (cur, can), // package installed and there is an update
-        (Some(cur), None) => (cur.clone(), cur), // package installed and up-to-date
-        (None, Some(_)) => return None, // package could be installed
-        (None, None) => return None, // broken
+    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'
@@ -139,7 +206,7 @@ where
         let mut long_desc = "".to_owned();
 
         let fd = FilterData {
-            installed_version: &current_version,
+            installed_version: current_version.as_deref(),
             candidate_version: &candidate_version,
             active_version: &version,
         };
@@ -192,6 +259,22 @@ where
                 }
             }
 
+            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,
@@ -200,7 +283,10 @@ where
                 change_log_url,
                 origin: origin_res,
                 version: candidate_version.clone(),
-                old_version: current_version.clone(),
+                old_version: match current_version {
+                    Some(vers) => vers,
+                    None => "".to_owned()
+                },
                 priority: priority_res,
                 section: section_res,
             });
@@ -229,10 +315,10 @@ where
 )]
 /// List available APT updates
 fn apt_update_available(_param: Value) -> Result<Value, Error> {
-    let all_upgradeable = list_installed_apt_packages(|data|
+    let all_upgradeable = list_installed_apt_packages(|data| {
         data.candidate_version == data.active_version &&
-        data.installed_version != data.candidate_version
-    );
+        data.installed_version != Some(data.candidate_version)
+    }, None);
     Ok(json!(all_upgradeable))
 }
 
-- 
2.20.1





  parent reply	other threads:[~2020-10-21  9:42 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-10-21  9:41 [pbs-devel] [PATCH 0/7] apt: add changelog API and fix #2934 (list new packages) Stefan Reiter
2020-10-21  9:41 ` [pbs-devel] [PATCH proxmox-backup 1/7] apt: allow filter to select different package version Stefan Reiter
2020-10-21  9:41 ` [pbs-devel] [PATCH proxmox-backup 2/7] add tools::http for generic HTTP GET and move HttpsConnector there Stefan Reiter
2020-10-21 18:31   ` [pbs-devel] applied: " Thomas Lamprecht
2020-10-21  9:41 ` [pbs-devel] [PATCH proxmox-backup 3/7] apt: use 'apt-get changelog --print-uris' in get_changelog_url Stefan Reiter
2020-10-21  9:41 ` [pbs-devel] [PATCH proxmox-backup 4/7] bump apt-pkg-native dependency to 0.3.2 Stefan Reiter
2020-10-21  9:41 ` [pbs-devel] [PATCH proxmox-backup 5/7] apt: refactor package detail reading into function Stefan Reiter
2020-10-21  9:41 ` Stefan Reiter [this message]
2020-10-21  9:41 ` [pbs-devel] [PATCH proxmox-backup 7/7] apt: add /changelog API call similar to PVE Stefan Reiter
2020-10-22 15:20 ` [pbs-devel] applied-series: [PATCH 0/7] apt: add changelog API and fix #2934 (list new packages) Fabian Grünbichler

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201021094116.32501-7-s.reiter@proxmox.com \
    --to=s.reiter@proxmox.com \
    --cc=pbs-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal