From e19b0dd997891eebf0a5fd6b30ac16d4cca2a7b0 Mon Sep 17 00:00:00 2001 From: houseme Date: Wed, 6 May 2026 08:28:46 +0800 Subject: [PATCH] fix(notify): improve webhook target diagnostics for env setup (#2810) --- README.md | 16 ++++++++++++++++ crates/notify/src/integration.rs | 22 +++++++++++++++++++--- rustfs/src/server/event.rs | 3 ++- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 855c02b20..f22a683ba 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,22 @@ Similarly, you can run the command with podman podman compose --profile observability up -d ``` +Webhook notification quick start (Docker): + +```bash +docker run -d --name rustfs -p 9000:9000 \ + -e RUSTFS_NOTIFY_ENABLE=true \ + -e RUSTFS_NOTIFY_WEBHOOK_ENABLE_PRIMARY=on \ + -e RUSTFS_NOTIFY_WEBHOOK_ENDPOINT_PRIMARY=http://:3020/webhook \ + -e RUSTFS_NOTIFY_WEBHOOK_QUEUE_DIR_PRIMARY=/tmp/rustfs-events \ + rustfs/rustfs:latest +``` + +Notes: +- `RUSTFS_NOTIFY_ENABLE=true` enables the global notify module switch. +- For ARN `arn:rustfs:sqs::primary:webhook`, use instance-scoped env vars with `_PRIMARY`. +- If queue dir is omitted, default is `/opt/rustfs/events`; ensure it is writable by the container runtime user. + **NOTE**: We recommend reviewing the `docker-compose.yml` file before running. It defines several services including Grafana, Prometheus, and Jaeger, which are helpful for RustFS observability. If you wish to start Redis or Nginx containers, you can specify the corresponding profiles. ### 3\. Build from Source (Option 3) - Advanced Users diff --git a/crates/notify/src/integration.rs b/crates/notify/src/integration.rs index 5a3d9ebbe..d6db1b438 100644 --- a/crates/notify/src/integration.rs +++ b/crates/notify/src/integration.rs @@ -24,9 +24,11 @@ use crate::{ }; use hashbrown::HashMap; use rustfs_config::notify::{ - DEFAULT_NOTIFY_TARGET_STREAM_CONCURRENCY, ENV_NOTIFY_TARGET_STREAM_CONCURRENCY, NOTIFY_KAFKA_SUB_SYS, NOTIFY_MQTT_SUB_SYS, - NOTIFY_NATS_SUB_SYS, NOTIFY_PULSAR_SUB_SYS, NOTIFY_WEBHOOK_SUB_SYS, + DEFAULT_NOTIFY_TARGET_STREAM_CONCURRENCY, ENV_NOTIFY_TARGET_STREAM_CONCURRENCY, ENV_NOTIFY_WEBHOOK_ENABLE, + ENV_NOTIFY_WEBHOOK_ENDPOINT, NOTIFY_KAFKA_SUB_SYS, NOTIFY_MQTT_SUB_SYS, NOTIFY_NATS_SUB_SYS, NOTIFY_PULSAR_SUB_SYS, + NOTIFY_WEBHOOK_SUB_SYS, }; +use rustfs_config::{ENV_NOTIFY_ENABLE, EVENT_DEFAULT_DIR}; use rustfs_ecstore::config::{Config, KVS}; use rustfs_s3_common::EventName; use rustfs_targets::arn::TargetID; @@ -42,6 +44,14 @@ use tracing::{debug, info, warn}; const MAX_RECENT_LIVE_EVENTS: usize = 1024; +fn notify_configuration_hint() -> String { + let webhook_enable_primary = format!("{ENV_NOTIFY_WEBHOOK_ENABLE}_PRIMARY"); + let webhook_endpoint_primary = format!("{ENV_NOTIFY_WEBHOOK_ENDPOINT}_PRIMARY"); + format!( + "No notify targets configured. Check {ENV_NOTIFY_ENABLE}=true and instance-scoped target env vars (for example {webhook_enable_primary} + {webhook_endpoint_primary} for arn:rustfs:sqs::primary:webhook). If using default queue_dir, ensure {EVENT_DEFAULT_DIR} is writable." + ) +} + fn subsystem_target_type(target_type: &str) -> &str { match target_type { NOTIFY_WEBHOOK_SUB_SYS => "webhook", @@ -325,6 +335,9 @@ impl NotificationSystem { let targets: Vec + Send + Sync>> = self.registry.create_targets_from_config(&config).await?; info!("{} notification targets were created", targets.len()); + if targets.is_empty() { + warn!("{}", notify_configuration_hint()); + } // Initialize targets and start event streams let cancellers = self.init_targets_and_start_streams(&targets).await; @@ -586,6 +599,9 @@ impl NotificationSystem { .map_err(NotificationError::Target)?; info!("{} notification targets were created from the new configuration", targets.len()); + if targets.is_empty() { + warn!("{}", notify_configuration_hint()); + } // Initialize targets and start event streams using shared helper let new_cancellers = self.init_targets_and_start_streams(&targets).await; @@ -607,7 +623,7 @@ impl NotificationSystem { ) -> Result<(), NotificationError> { let arn_list = self.notifier.get_arn_list(&cfg.region).await; if arn_list.is_empty() { - return Err(NotificationError::Configuration("No targets configured".to_string())); + return Err(NotificationError::Configuration(notify_configuration_hint())); } info!("Available ARNs: {:?}", arn_list); // Validate the configuration against the available ARNs diff --git a/rustfs/src/server/event.rs b/rustfs/src/server/event.rs index ca0e084d9..10bf50f24 100644 --- a/rustfs/src/server/event.rs +++ b/rustfs/src/server/event.rs @@ -107,7 +107,8 @@ pub async fn init_event_notifier() { if !enabled { info!( target: "rustfs::main::init_event_notifier", - "Notify module is disabled, event notifier initialization is skipped. Enable the notify module first." + "Notify module is disabled, event notifier initialization is skipped. Set {}=true to enable notify initialization.", + rustfs_config::ENV_NOTIFY_ENABLE ); return; }