public inbox for pdm-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: "Lukas Wagner" <l.wagner@proxmox.com>
To: "Proxmox Datacenter Manager development discussion"
	<pdm-devel@lists.proxmox.com>
Cc: "pdm-devel" <pdm-devel-bounces@lists.proxmox.com>
Subject: Re: [pdm-devel] [PATCH proxmox-datacenter-manager 1/4] server: add api for getting available updates/changelogs for remote nodes
Date: Wed, 03 Sep 2025 10:42:10 +0200	[thread overview]
Message-ID: <DCJ15C93262W.2M1Y8V0JV8DEI@proxmox.com> (raw)
In-Reply-To: <20250902151427.425017-8-l.wagner@proxmox.com>

On Tue Sep 2, 2025 at 5:14 PM CEST, Lukas Wagner wrote:
> This adds new APIs for update management:
>
>     GET /pve/remotes/{remote}/nodes/{node}/apt/changelog
>       -> get package changelog
>     GET /pve/remotes/{remote}/nodes/{node}/apt/update
>       -> get list of updatable packages
>     POST /pve/remotes/{remote}/nodes/{node}/apt/update
>       -> refresh APT database
>
> At this time these just pass the call through to PVE with no caching
> involved on the PDM side. This should be fine for this API, but once
> we have an API for 'give me a view of ALL available remote updates',
> we need to introduce a cache that is periodically refreshed.
>
> Signed-off-by: Lukas Wagner <l.wagner@proxmox.com>
> ---
>  server/src/api/pve/apt.rs    | 119 +++++++++++++++++++++++++++++++++++
>  server/src/api/pve/mod.rs    |   3 +-
>  server/src/api/pve/node.rs   |   1 +
>  server/src/lib.rs            |   1 +
>  server/src/remote_updates.rs |  96 ++++++++++++++++++++++++++++
>  5 files changed, 219 insertions(+), 1 deletion(-)
>  create mode 100644 server/src/api/pve/apt.rs
>  create mode 100644 server/src/remote_updates.rs
>
> diff --git a/server/src/api/pve/apt.rs b/server/src/api/pve/apt.rs
> new file mode 100644
> index 00000000..f5027fb8
> --- /dev/null
> +++ b/server/src/api/pve/apt.rs
> @@ -0,0 +1,119 @@
> +use anyhow::Error;
> +
> +use proxmox_apt_api_types::{APTGetChangelogOptions, APTUpdateInfo};
> +use proxmox_router::{list_subdirs_api_method, Permission, Router, SubdirMap};
> +use proxmox_schema::api;
> +use proxmox_schema::api_types::NODE_SCHEMA;
> +
> +use pdm_api_types::{remotes::REMOTE_ID_SCHEMA, RemoteUpid, PRIV_RESOURCE_MODIFY};
> +
> +use crate::{api::remotes::get_remote, remote_updates};
> +
> +#[api(
> +    input: {
> +        properties: {
> +            remote: {
> +                schema: REMOTE_ID_SCHEMA,
> +            },
> +            node: {
> +                schema: NODE_SCHEMA,
> +            },
> +        },
> +    },
> +    returns: {
> +        description: "A list of packages with available updates.",
> +        type: Array,
> +        items: {
> +            type: APTUpdateInfo
> +        },
> +    },
> +    access: {
> +        permission: &Permission::Privilege(&["resource", "{remote}", "node", "{node}", "system"], PRIV_RESOURCE_MODIFY, false),
> +    },
> +)]
> +/// List available APT updates for a remote PVE node.
> +async fn apt_update_available(remote: String, node: String) -> Result<Vec<APTUpdateInfo>, Error> {
> +    let (config, _digest) = pdm_config::remotes::config()?;
> +    let remote = get_remote(&config, &remote)?;
> +
> +    let updates = remote_updates::list_available_updates(remote.clone(), &node).await?;
> +
> +    Ok(updates)
> +}
> +
> +#[api(
> +    input: {
> +        properties: {
> +            remote: {
> +                schema: REMOTE_ID_SCHEMA,
> +            },
> +            node: {
> +                schema: NODE_SCHEMA,
> +            },
> +        },
> +    },
> +    access: {
> +        permission: &Permission::Privilege(&["resource", "{remote}", "node", "{node}", "system"], PRIV_RESOURCE_MODIFY, false),
> +    },
> +)]
> +/// Update the APT database of a remote PVE node.
> +pub async fn apt_update_database(remote: String, node: String) -> Result<RemoteUpid, Error> {
> +    let (config, _digest) = pdm_config::remotes::config()?;
> +    let remote = get_remote(&config, &remote)?;
> +
> +    let upid = remote_updates::update_apt_database(remote, &node).await?;
> +
> +    Ok(upid)
> +}
> +
> +#[api(
> +    input: {
> +        properties: {
> +            remote: {
> +                schema: REMOTE_ID_SCHEMA,
> +            },
> +            node: {
> +                schema: NODE_SCHEMA,
> +            },
> +            options: {
> +                type: APTGetChangelogOptions,
> +                flatten: true,
> +            },
> +        },
> +    },
> +    returns: {
> +        description: "The Package changelog.",
> +        type: String,
> +    },
> +    access: {
> +        permission: &Permission::Privilege(&["resource", "{remote}", "node", "{node}", "system"], PRIV_RESOURCE_MODIFY, false),
> +    },
> +)]
> +/// Retrieve the changelog of the specified package for a remote PVE node.
> +async fn apt_get_changelog(
> +    remote: String,
> +    node: String,
> +    options: APTGetChangelogOptions,
> +) -> Result<String, Error> {
> +    let (config, _digest) = pdm_config::remotes::config()?;
> +    let remote = get_remote(&config, &remote)?;
> +
> +    remote_updates::get_changelog(remote.clone(), &node, options.name).await
> +}
> +
> +const SUBDIRS: SubdirMap = &[
> +    (
> +        "changelog",
> +        &Router::new().get(&API_METHOD_APT_GET_CHANGELOG),
> +    ),
> +    (
> +        "update",
> +        &Router::new()
> +            .get(&API_METHOD_APT_UPDATE_AVAILABLE)
> +            .post(&API_METHOD_APT_UPDATE_DATABASE),
> +    ),
> +];
> +
> +pub const ROUTER: Router = Router::new()
> +    .get(&list_subdirs_api_method!(SUBDIRS))
> +    .subdirs(SUBDIRS);
> diff --git a/server/src/api/pve/mod.rs b/server/src/api/pve/mod.rs
> index 2cfdc5b7..0768083d 100644
> --- a/server/src/api/pve/mod.rs
> +++ b/server/src/api/pve/mod.rs
> @@ -31,6 +31,7 @@ use crate::connection::PveClient;
>  use crate::connection::{self, probe_tls_connection};
>  use crate::remote_tasks;
>  
> +mod apt;
>  mod lxc;
>  mod node;
>  mod qemu;
> @@ -77,7 +78,7 @@ const RESOURCES_ROUTER: Router = Router::new().get(&API_METHOD_CLUSTER_RESOURCES
>  const STATUS_ROUTER: Router = Router::new().get(&API_METHOD_CLUSTER_STATUS);
>  
>  // converts a remote + PveUpid into a RemoteUpid and starts tracking it
> -async fn new_remote_upid(remote: String, upid: PveUpid) -> Result<RemoteUpid, Error> {
> +pub async fn new_remote_upid(remote: String, upid: PveUpid) -> Result<RemoteUpid, Error> {
>      let remote_upid: RemoteUpid = (remote, upid.to_string()).try_into()?;
>      remote_tasks::track_running_task(remote_upid.clone()).await?;
>      Ok(remote_upid)
> diff --git a/server/src/api/pve/node.rs b/server/src/api/pve/node.rs
> index df96a1c3..99539d1c 100644
> --- a/server/src/api/pve/node.rs
> +++ b/server/src/api/pve/node.rs
> @@ -13,6 +13,7 @@ pub const ROUTER: Router = Router::new()
>  
>  #[sortable]
>  const SUBDIRS: SubdirMap = &sorted!([
> +    ("apt", &super::apt::ROUTER),
>      ("rrddata", &super::rrddata::NODE_RRD_ROUTER),
>      ("network", &Router::new().get(&API_METHOD_GET_NETWORK)),
>      ("storage", &Router::new().get(&API_METHOD_GET_STORAGES)),
> diff --git a/server/src/lib.rs b/server/src/lib.rs
> index 3f8b7708..a58190d8 100644
> --- a/server/src/lib.rs
> +++ b/server/src/lib.rs
> @@ -9,6 +9,7 @@ pub mod metric_collection;
>  pub mod parallel_fetcher;
>  pub mod remote_cache;
>  pub mod remote_tasks;
> +pub mod remote_updates;
>  pub mod resource_cache;
>  pub mod task_utils;
>  
> diff --git a/server/src/remote_updates.rs b/server/src/remote_updates.rs
> new file mode 100644
> index 00000000..809e5de1
> --- /dev/null
> +++ b/server/src/remote_updates.rs
> @@ -0,0 +1,96 @@
> +use anyhow::Error;
> +use pdm_api_types::RemoteUpid;
> +
> +use proxmox_apt_api_types::APTUpdateInfo;
> +
> +use pdm_api_types::remotes::{Remote, RemoteType};
> +
> +use crate::api::pve::new_remote_upid;
> +use crate::connection;
> +
> +/// Return a list of available updates for a given remote node.
> +pub async fn list_available_updates(
> +    remote: Remote,
> +    node: &str,
> +) -> Result<Vec<APTUpdateInfo>, Error> {
> +    let updates = fetch_available_updates(remote, node.to_string()).await?;
> +    Ok(updates)
> +}
> +
> +/// Trigger `apt update` on a remote node.
> +///
> +/// The function returns a `[RemoteUpid]` for the started update task.
> +pub async fn update_apt_database(remote: &Remote, node: &str) -> Result<RemoteUpid, Error> {
> +    match remote.ty {
> +        RemoteType::Pve => {
> +            let client = connection::make_pve_client(remote)?;
> +
> +            let params = pve_api_types::AptUpdateParams {
> +                notify: Some(false),
> +                quiet: Some(false),
> +            };
> +            let upid = client.update_apt_database(node, params).await?;
> +
> +            new_remote_upid(remote.id.clone(), upid).await
> +        }
> +        RemoteType::Pbs => todo!(),

Missed that one during my self review yesterday, I'll replace this with
a `bail!` in v2, which I will post once I get first review feedback from
somebody else.


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


  reply	other threads:[~2025-09-03  8:42 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-02 15:14 [pdm-devel] [PATCH manager/proxmox{-api-types, -yew-comp, -datacenter-manager} 00/10] PVE node update view Lukas Wagner
2025-09-02 15:14 ` [pdm-devel] [PATCH manager 1/1] api: apt: add JSON schema for 'list_updates' endpoint Lukas Wagner
2025-09-03  8:25   ` [pdm-devel] applied: " Thomas Lamprecht
2025-09-02 15:14 ` [pdm-devel] [PATCH proxmox-api-types 1/3] Schema2Rust: fix handling of non-optional params Lukas Wagner
2025-09-03  8:57   ` [pdm-devel] applied: " Wolfgang Bumiller
2025-09-02 15:14 ` [pdm-devel] [PATCH proxmox-api-types 2/3] generate: add bindings for various APT functions Lukas Wagner
2025-09-03  8:58   ` [pdm-devel] applied: " Wolfgang Bumiller
2025-09-02 15:14 ` [pdm-devel] [PATCH proxmox-api-types 3/3] refresh bindings Lukas Wagner
2025-09-03  8:59   ` Wolfgang Bumiller
2025-09-02 15:14 ` [pdm-devel] [PATCH proxmox-yew-comp 1/2] apt view: allow to set task_base_url Lukas Wagner
2025-09-02 15:14 ` [pdm-devel] [PATCH proxmox-yew-comp 2/2] apt view: reload if base urls have changed Lukas Wagner
2025-09-02 15:14 ` [pdm-devel] [PATCH proxmox-datacenter-manager 1/4] server: add api for getting available updates/changelogs for remote nodes Lukas Wagner
2025-09-03  8:42   ` Lukas Wagner [this message]
2025-09-03  9:02   ` Stefan Hanreich
2025-09-03  9:19     ` Stefan Hanreich
2025-09-03  9:23     ` Lukas Wagner
2025-09-02 15:14 ` [pdm-devel] [PATCH proxmox-datacenter-manager 2/4] ui: pve: promote node.rs to dir-style module Lukas Wagner
2025-09-02 15:14 ` [pdm-devel] [PATCH proxmox-datacenter-manager 3/4] ui: pve: move node overview to a new overview tab Lukas Wagner
2025-09-03  9:10   ` Stefan Hanreich
2025-09-03  9:48     ` Lukas Wagner
2025-09-03  9:52       ` Stefan Hanreich
2025-09-03  9:55         ` Lukas Wagner
2025-09-02 15:14 ` [pdm-devel] [PATCH proxmox-datacenter-manager 4/4] ui: pve: node: add update tab Lukas Wagner
2025-09-03 10:20 ` [pdm-devel] [PATCH manager/proxmox{-api-types, -yew-comp, -datacenter-manager} 00/10] PVE node update view Stefan Hanreich
2025-09-03 11:43 ` [pdm-devel] superseded: " Lukas Wagner

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=DCJ15C93262W.2M1Y8V0JV8DEI@proxmox.com \
    --to=l.wagner@proxmox.com \
    --cc=pdm-devel-bounces@lists.proxmox.com \
    --cc=pdm-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