From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 13FB061735 for ; Wed, 21 Oct 2020 11:42:04 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 5CE471695A for ; Wed, 21 Oct 2020 11:41:33 +0200 (CEST) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id C489516853 for ; Wed, 21 Oct 2020 11:41:29 +0200 (CEST) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 8C48845E88 for ; Wed, 21 Oct 2020 11:41:29 +0200 (CEST) From: Stefan Reiter To: pbs-devel@lists.proxmox.com Date: Wed, 21 Oct 2020 11:41:15 +0200 Message-Id: <20201021094116.32501-7-s.reiter@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201021094116.32501-1-s.reiter@proxmox.com> References: <20201021094116.32501-1-s.reiter@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.031 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [apt.rs] Subject: [pbs-devel] [PATCH proxmox-backup 6/7] fix #2934: list to-be-installed packages in updates X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 21 Oct 2020 09:42:04 -0000 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 --- 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 bool>(filter: F) - -> Vec { +enum PackagePreSelect { + OnlyInstalled, + OnlyNew, + All, +} + +fn list_installed_apt_packages bool>( + filter: F, + only_versions_for: Option<&str>, +) -> Vec { 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 bool>(filter: F) } fn query_detailed_info<'a, F, V>( + pre_select: PackagePreSelect, filter: F, view: V, + depends: Option<&mut HashSet>, ) -> Option 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: ¤t_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 { - 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