From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 44A7A1FF13B for ; Wed, 03 Jun 2026 14:28:12 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 2495FAABE; Wed, 3 Jun 2026 14:28:12 +0200 (CEST) From: Shannon Sterz To: pdm-devel@lists.proxmox.com Subject: [PATCH datacenter-manager v2 1/2] server: subscription: always get fresh subscription info on update Date: Wed, 3 Jun 2026 14:27:32 +0200 Message-ID: <20260603122735.425390-2-s.sterz@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260603122735.425390-1-s.sterz@proxmox.com> References: <20260603122735.425390-1-s.sterz@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1780489620155 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.110 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: CHIIM3TB24EQ6F5H4IFT4BKXDWIRWDCH X-Message-ID-Hash: CHIIM3TB24EQ6F5H4IFT4BKXDWIRWDCH X-MailFrom: s.sterz@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox Datacenter Manager development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: otherwise the cached information is used. since the cache does not include the server id, not suitable information will be detected. to avoid that freshly fetch the subscription info, including the server id, when updating the subscription status. adds a helper that guarantees complete, freshly fetched information as the existing `get_subscription_info_for_remote` helper can still return cached information. this can happen when another thread updated the cache sooner than the current thread can. Signed-off-by: Shannon Sterz --- server/src/api/nodes/subscription.rs | 47 +++++++++++++++++++++++----- server/src/api/resources.rs | 13 ++++++++ 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/server/src/api/nodes/subscription.rs b/server/src/api/nodes/subscription.rs index d6ae48b5..dd47836e 100644 --- a/server/src/api/nodes/subscription.rs +++ b/server/src/api/nodes/subscription.rs @@ -15,7 +15,9 @@ use pdm_api_types::subscription::{ NodeSubscriptionInfo, PdmSubscriptionInfo, SubscriptionLevel, SubscriptionStatistics, }; -use crate::api::resources::get_subscription_info_for_remote; +use crate::api::resources::{ + fetch_complete_subscription_info_for_remote, get_subscription_info_for_remote, +}; const PRODUCT_URL: &str = "https://pdm.proxmox.com/faq.html"; const APT_AUTH_FN: &str = "/etc/apt/auth.conf.d/pdm.conf"; @@ -153,7 +155,7 @@ pub async fn check_subscription() -> Result<(), Error> { } let mut found = false; - 'outer: for (remote, (remote_type, remote_info)) in infos.iter() { + 'outer: for (remote_name, (remote_type, remote_info)) in infos.iter() { if *remote_type != RemoteType::Pve && *remote_type != RemoteType::Pbs { log::warn!("skipping unknown remote type {remote_type}"); continue; @@ -163,12 +165,43 @@ pub async fn check_subscription() -> Result<(), Error> { if info.status == SubscriptionStatus::Active && info.level >= SubscriptionLevel::Basic && info.key.is_some() - && info.serverid.is_some() { - log::info!( - "Using subscription of node '{node}' of remote '{remote}' for enterprise \ - repository access" - ); + // Get fresh subscription info. The cache does not store the serverid, so we + // need to fetch it from the remote. This has the upside of always yielding + // fresh results. + let (remote_config, _digest) = pdm_config::remotes::config()?; + let Some(remote) = remote_config.get(remote_name) else { + log::debug!( + "Remote vanished while updating subscription information \ + '{remote_name}'." + ); + continue 'outer; + }; + let node_info = fetch_complete_subscription_info_for_remote(remote).await?; + let Some(info) = node_info.iter().find_map(|(node, val)| { + if let Some(info) = val.as_ref() { + if info.status == SubscriptionStatus::Active + && info.level >= SubscriptionLevel::Basic + && info.key.is_some() + && info.serverid.is_some() + { + log::info!( + "Using subscription of node '{node}' of remote '{remote_name}' \ + for enterprise repository access" + ); + + return Some(info.clone()); + } + } + None + }) else { + log::debug!( + "Could not find any eligibile subscription information for remote \ + '{remote_name}' while updating the subscription information." + ); + continue 'outer; + }; + update_apt_auth( APT_AUTH_FN, apt_auth_file_opts(), diff --git a/server/src/api/resources.rs b/server/src/api/resources.rs index f2ef4101..09d2b88d 100644 --- a/server/src/api/resources.rs +++ b/server/src/api/resources.rs @@ -866,6 +866,19 @@ pub async fn get_subscription_info_for_remote( } } +/// Always fetches fresh complete subscription information for a remote. +/// +/// The cache will be updated, but never read from. This guarantees that the `serverid` is set, as +/// it cannot be stored in the cache. +pub async fn fetch_complete_subscription_info_for_remote( + remote: &Remote, +) -> Result>, Error> { + let node_info = fetch_remote_subscription_info(remote).await?; + let now = proxmox_time::epoch_i64(); + let _ = update_cached_subscription_info(&remote.id, node_info.clone(), now).await?; + Ok(node_info) +} + const SUBSCRIPTION_STATE_CACHE_KEY: &str = "subscription-state"; async fn get_cached_subscription_info( -- 2.47.3