public inbox for pdm-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: "Shannon Sterz" <s.sterz@proxmox.com>
To: "Lukas Wagner" <l.wagner@proxmox.com>,
	"Arthur Bied-Charreton" <a.bied-charreton@proxmox.com>,
	<pdm-devel@lists.proxmox.com>
Subject: Re: [PATCH datacenter-manager v2] Add notifications backend
Date: Thu, 09 Apr 2026 14:39:05 +0200	[thread overview]
Message-ID: <DHOMPI11ZDOI.29QF11BL3QMF@proxmox.com> (raw)
In-Reply-To: <DHOM1M6980QN.2OMHX893260TH@proxmox.com>

On Thu Apr 9, 2026 at 2:07 PM CEST, Lukas Wagner wrote:
> On Thu Apr 9, 2026 at 11:20 AM CEST, Shannon Sterz wrote:
>> On Thu Apr 9, 2026 at 6:57 AM CEST, Arthur Bied-Charreton wrote:

-->8 snip 8<--

>>
>> any reason to not go ahead and add at least one notification in this
>> series too? being able to test that notifications function, but not
>> having anything to be notified about seems a little odd to me. one easy
>> notification you could add is the `send_certificate_renewal_mail` in
>> `api::nodes::certificates::spawn_certificate_worker()`. that should
>> essentially be identical to pbs anyway.
>>
>
> This was coordinated with me, I told him that it would be okay to send
> this part early. He can either build on top of this series and include
> more patches (e.g. adding the GUI, notification events, docs, etc.) in
> future versions, or if it makes sense we can also apply certain parts
> early.
>

ack, was not aware of that.

>
>>> The endpoints are made unprotected and the configuration files
>>> read/writable by `www-data`, like for `remotes.cfg`.
>>>
>>> Signed-off-by: Arthur Bied-Charreton <a.bied-charreton@proxmox.com>
>>> ---
>>>  Cargo.toml                                    |   1 +
>>>  Makefile                                      |   3 +-
>>>  debian/proxmox-datacenter-manager.install     |   2 +
>>>  defines.mk                                    |   1 +
>>>  lib/pdm-config/Cargo.toml                     |   1 +
>>>  lib/pdm-config/src/lib.rs                     |   1 +
>>>  lib/pdm-config/src/notifications.rs           |  39 ++++
>>>  server/Cargo.toml                             |   1 +
>>>  server/src/api/config/mod.rs                  |   2 +
>>>  server/src/api/config/notifications/gotify.rs | 185 +++++++++++++++++
>>>  .../src/api/config/notifications/matchers.rs  | 165 ++++++++++++++++
>>>  server/src/api/config/notifications/mod.rs    | 102 ++++++++++
>>>  .../src/api/config/notifications/sendmail.rs  | 173 ++++++++++++++++
>>>  server/src/api/config/notifications/smtp.rs   | 186 ++++++++++++++++++
>>>  .../src/api/config/notifications/targets.rs   |  61 ++++++
>>>  .../src/api/config/notifications/webhook.rs   | 170 ++++++++++++++++
>>>  server/src/context.rs                         |   2 +
>>>  server/src/lib.rs                             |   1 +
>>>  server/src/notifications/mod.rs               | 115 +++++++++++
>>>  templates/Makefile                            |  14 ++
>>>  templates/default/test-body.txt.hbs           |   1 +
>>>  templates/default/test-subject.txt.hbs        |   1 +
>>>  22 files changed, 1226 insertions(+), 1 deletion(-)
>>>  create mode 100644 lib/pdm-config/src/notifications.rs
>>>  create mode 100644 server/src/api/config/notifications/gotify.rs
>>>  create mode 100644 server/src/api/config/notifications/matchers.rs
>>>  create mode 100644 server/src/api/config/notifications/mod.rs
>>>  create mode 100644 server/src/api/config/notifications/sendmail.rs
>>>  create mode 100644 server/src/api/config/notifications/smtp.rs
>>>  create mode 100644 server/src/api/config/notifications/targets.rs
>>>  create mode 100644 server/src/api/config/notifications/webhook.rs
>>>  create mode 100644 server/src/notifications/mod.rs
>>>  create mode 100644 templates/Makefile
>>>  create mode 100644 templates/default/test-body.txt.hbs
>>>  create mode 100644 templates/default/test-subject.txt.hbs
>>>
>
> [...]
>
>>> +
>>> +use pdm_buildcfg::configdir;
>>> +use proxmox_product_config::{open_api_lockfile, replace_config, ApiLockGuard};
>>> +use proxmox_sys::fs::file_read_optional_string;
>>> +
>>> +/// Configuration file location for notification targets/matchers.
>>> +pub const NOTIFICATION_CONFIG_PATH: &str = configdir!("/notifications.cfg");
>>> +
>>> +/// Private configuration file location for secrets - only readable by `root`.
>>> +pub const NOTIFICATION_PRIV_CONFIG_PATH: &str = configdir!("/notifications-priv.cfg");
>>> +
>>> +/// Lockfile to prevent concurrent write access.
>>> +pub const NOTIFICATION_LOCK_FILE: &str = configdir!("/.notifications.lck");
>>> +
>>
>> you might want to move these to their own "notifications" sub-folder or
>> similar (maybe just "notify"?). in pdm we tend to put config files in
>> topically fitting sub-folders (e.g. access, acme, auth)
>>
>
> Would be okay for me, but I'd prefer `notifications` for consistency
> with what we do on other products.
>

sounds fine to me, i mostly suggested "notify" because it's shorter.

>>> +/// Get exclusive lock for `notifications.cfg`.
>>> +pub fn lock_config() -> Result<ApiLockGuard, Error> {
>>> +    open_api_lockfile(NOTIFICATION_LOCK_FILE, None, true)
>>> +}
>>> +
>>> +/// Load notification config.
>>> +pub fn config() -> Result<Config, Error> {
>>> +    let content = file_read_optional_string(NOTIFICATION_CONFIG_PATH)?.unwrap_or_default();
>>> +
>>> +    let priv_content =
>>> +        file_read_optional_string(NOTIFICATION_PRIV_CONFIG_PATH)?.unwrap_or_default();
>>> +
>>> +    Ok(Config::new(&content, &priv_content)?)
>>
>> you might want to consider returning a `ConfigDigest` here too.
>> returning that encourages validating the digest as its less easily
>> forgotten.
>
> digest-checking is handled in the API handler implementations in
> proxmox_notify::api where it seemed useful (see my later comments), it
> cannot really be forgotten since it is a required parameter for these
> functions.

yes that's true for this crate, but from what i can tell returning a
config alongside its digest is a common pattern. but you are righ, in
this case we enforce it through `proxmox_notify`, so should be fine.

> I guess we could do a
>
> 	let config = Config::new(&content, &priv_content)?;
>
> 	Ok((config, config.digest()))
>
> here, but I'm not sure it gains us much?
>
>>
>>> +}
>>> +
>>> +/// Save notification config.
>>> +pub fn save_config(config: Config) -> Result<(), Error> {
>>> +    let (cfg, priv_cfg) = config.write()?;
>>> +    replace_config(NOTIFICATION_CONFIG_PATH, cfg.as_bytes())?;
>>> +    replace_config(NOTIFICATION_PRIV_CONFIG_PATH, priv_cfg.as_bytes())?;
>>
>> the privileged config should probably be saved with higher permission
>> than the general config. consider using `replace_secret_config` here.
>>
>
> Unlike PBS, the notification system will run in the unprivileged process
> in PDM, so that webhook/smtp stuff will not run as root. This means that
> we also need to store the secrets with less strict permissions. We
> already do the same for remotes.cfg and remotes.shadow, so this should
> be fine here as well, I think.
> Of course, we could also use the same approach as in PBS (notification
> code runs as root, secrets are 0600 root:root), but I prefer the
> approach used here.

ack, was not aware that we want to switch to this approach here. but in
hindsight it makes sense why `test_target` wasn't marked as protected.

>>> +/// Add a new gotify endpoint.
>>> +pub fn add_endpoint(
>>> +    endpoint: GotifyConfig,
>>> +    token: String,
>>> +    _rpcenv: &mut dyn RpcEnvironment,
>>> +) -> Result<(), Error> {
>>> +    let _lock = pdm_config::notifications::lock_config()?;
>>> +    let mut config = pdm_config::notifications::config()?;
>>
>> this should probably verify the digest. if you return a config
>> digest as described above this could become:
>>
>>     let mut (config, expected_digest) = domains::config()?;
>>     expected_digest.detect_modification(digest.as_ref())?;
>>
>> assuming that you take the digest as an input parameter to this api call
>> with:
>>
>>     digest: {
>>         optional: true,
>>         type: ConfigDigest,
>>     }
>>
>> and:
>>
>>     digest: Option<ConfigDigest>
>>
>
> We lock the config anyway, so it does not really matter (I think?) if
> somebody else modified some other entity? And if somebody had added an
> entity with the same id, then we fail anyways... So I'm not sure if
> using the digest here gains us anything?
>
> I quickly checked a couple add_* handlers in PBS, we don't
> really check the digest when adding new entities there as well.
>

hm yeah i guess you are right, it wouldn't add too much. guess im just a
little paranoid about modifying on top of an unknown state. but should
probably be fine in these cases.

> Generally, with regards to using the ConfigDigest type: +1
> I think this type was introduced well after the notification API was
> added to PBS, which might be the reason I did not use them there.

yep hence why i think switching to it would make sense now.

>>> +pub fn delete_endpoint(name: String, _rpcenv: &mut dyn RpcEnvironment) -> Result<(), Error> {
>>> +    let _lock = pdm_config::notifications::lock_config()?;
>>> +    let mut config = pdm_config::notifications::config()?;
>>> +    proxmox_notify::api::gotify::delete_gotify_endpoint(&mut config, &name)?;
>>> +
>>
>> this would probably also benefit from checking the config digest.
>>
>
> Same here, I'm not really sure what the benefit would be here?
>
> IMO there are a couple cases to consider for concurrent modifications:
>   - somebody modified entity A, we delete B -> should be fine
>   - somebody modified entity A, we delete A -> does not matter, we want
>     to delete it anyways
>   - we deleted A, somebody else modifies A -> update fails anyways due
>     to the wrong config digest or the entity missing already
>   - both try to delete A -> fails for one of both due to the already
>     deleted entity (HTTP 404)
>
> Did I miss something?
>

see above.

-->8 snip 8<--




  reply	other threads:[~2026-04-09 12:38 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-09  4:57 Arthur Bied-Charreton
2026-04-09  9:20 ` Shannon Sterz
2026-04-09 10:18   ` Arthur Bied-Charreton
2026-04-09 11:13     ` Shannon Sterz
2026-04-09 12:07   ` Lukas Wagner
2026-04-09 12:39     ` Shannon Sterz [this message]
2026-04-09 18:26     ` Thomas Lamprecht
2026-04-10 12:00       ` Arthur Bied-Charreton

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=DHOMPI11ZDOI.29QF11BL3QMF@proxmox.com \
    --to=s.sterz@proxmox.com \
    --cc=a.bied-charreton@proxmox.com \
    --cc=l.wagner@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