api: implement apt pkg cache

based on the idea of PVE

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Thomas Lamprecht 2020-10-31 21:02:25 +01:00
parent b282557563
commit 33508b1237
2 changed files with 80 additions and 6 deletions

View File

@ -26,17 +26,26 @@ use crate::api2::types::{Authid, APTUpdateInfo, NODE_SCHEMA, UPID_SCHEMA};
type: APTUpdateInfo
},
},
protected: true,
access: {
permission: &Permission::Privilege(&[], PRIV_SYS_AUDIT, false),
},
)]
/// List available APT updates
fn apt_update_available(_param: Value) -> Result<Value, Error> {
let all_upgradeable = apt::list_installed_apt_packages(|data| {
data.candidate_version == data.active_version &&
data.installed_version != Some(data.candidate_version)
}, None);
Ok(json!(all_upgradeable))
match apt::pkg_cache_expired() {
Ok(false) => {
if let Ok(Some(cache)) = apt::read_pkg_state() {
return Ok(json!(cache.package_status));
}
},
_ => (),
}
let cache = apt::update_cache()?;
return Ok(json!(cache.package_status));
}
fn do_apt_update(worker: &WorkerTask, quiet: bool) -> Result<(), Error> {

View File

@ -1,12 +1,77 @@
use std::collections::HashSet;
use anyhow::{Error, bail};
use anyhow::{Error, bail, format_err};
use apt_pkg_native::Cache;
use proxmox::const_regex;
use proxmox::tools::fs::{file_read_optional_string, replace_file, CreateOptions};
use crate::api2::types::APTUpdateInfo;
const APT_PKG_STATE_FN: &str = "/var/lib/proxmox-backup/pkg-state.json";
#[derive(Debug, serde::Serialize, serde::Deserialize)]
/// Some information we cache about the package (update) state
pub struct PkgState {
/// 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())
.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(|s| Some(s))
.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 {
package_status: all_upgradeable,
},
};
write_pkg_cache(&cache)?;
Ok(cache)
}
const_regex! {
VERSION_EPOCH_REGEX = r"^\d+:";
FILENAME_EXTRACT_REGEX = r"^.*/.*?_(.*)_Packages$";