refactor(plugins): harden target-plugin system and admin plugin contract (#4217)

This commit is contained in:
Zhengchao An
2026-07-03 10:18:21 +08:00
committed by GitHub
parent c260a2f20f
commit fb0e2d6d8f
30 changed files with 683 additions and 328 deletions
+5 -6
View File
@@ -18,6 +18,7 @@
//! Queue-store mode uses the shared target store and replays the same raw JSON
//! body through `send_raw_from_store`.
use crate::plugin::PluginEvent;
use crate::{
StoreError, Target,
arn::TargetID,
@@ -42,8 +43,6 @@ use lapin::{
use parking_lot::Mutex;
use rustfs_config::{AMQP_TLS_CA, AMQP_TLS_CLIENT_CERT, AMQP_TLS_CLIENT_KEY};
use rustfs_tls_runtime::load_cert_bundle_der_bytes;
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::fmt;
use std::path::Path;
use std::sync::Arc;
@@ -298,7 +297,7 @@ pub struct AMQPConnection {
pub struct AMQPTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
id: TargetID,
args: AMQPArgs,
@@ -314,7 +313,7 @@ where
impl<E> AMQPTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
pub fn clone_box(&self) -> Box<dyn Target<E> + Send + Sync> {
Box::new(AMQPTarget::<E> {
@@ -464,7 +463,7 @@ where
#[async_trait]
impl<E> ReloadableTargetTls for AMQPTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
type Material = AMQPConnection;
@@ -500,7 +499,7 @@ where
#[async_trait]
impl<E> Target<E> for AMQPTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
fn id(&self) -> TargetID {
self.id.clone()
+5 -6
View File
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::plugin::PluginEvent;
use crate::{
StoreError, Target,
arn::TargetID,
@@ -31,8 +32,6 @@ use async_trait::async_trait;
use rustfs_kafka_async::error::{ConnectionError, Error as KafkaError};
use rustfs_kafka_async::{AsyncProducer, AsyncProducerConfig, Record, RequiredAcks, SaslConfig, SecurityConfig};
use rustfs_tls_runtime::{load_cert_bundle_der_bytes, load_private_key};
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::{fmt, marker::PhantomData, sync::Arc, time::Duration};
use tokio::sync::Mutex;
use tracing::{debug, error, info, instrument, warn};
@@ -228,7 +227,7 @@ impl KafkaArgs {
/// A target that sends events to an Apache Kafka topic
pub struct KafkaTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
id: TargetID,
args: KafkaArgs,
@@ -245,7 +244,7 @@ where
impl<E> KafkaTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
fn map_kafka_error(err: KafkaError, context: &str) -> TargetError {
match err {
@@ -397,7 +396,7 @@ where
#[async_trait]
impl<E> Target<E> for KafkaTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
fn id(&self) -> TargetID {
self.id.clone()
@@ -490,7 +489,7 @@ where
#[async_trait]
impl<E> ReloadableTargetTls for KafkaTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
type Material = Arc<AsyncProducer>;
+4 -4
View File
@@ -13,11 +13,11 @@
// limitations under the License.
use crate::arn::TargetID;
use crate::plugin::PluginEvent;
use crate::store::{Key, QueueStore, Store};
use crate::{StoreError, TargetError, TargetLog};
use async_trait::async_trait;
use rustfs_s3_types::EventName;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt::Formatter;
use std::future::Future;
@@ -96,7 +96,7 @@ impl TargetDeliveryCounters {
#[async_trait]
pub trait Target<E>: Send + Sync + 'static
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
/// Returns the ID of the target
fn id(&self) -> TargetID;
@@ -428,7 +428,7 @@ pub fn decode_object_name(encoded: &str) -> Result<String, TargetError> {
pub(crate) fn build_queued_payload<E>(event: &EntityTarget<E>) -> Result<QueuedPayload, TargetError>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
build_queued_payload_with_records(event, vec![event.data.clone()])
}
@@ -438,7 +438,7 @@ pub(crate) fn build_queued_payload_with_records<E, R>(
records: Vec<R>,
) -> Result<QueuedPayload, TargetError>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
R: Serialize,
{
let object_name = decode_object_name(&event.object_name)?;
+5 -6
View File
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::plugin::PluginEvent;
use crate::{
StoreError, Target,
arn::TargetID,
@@ -39,8 +40,6 @@ use rustfs_config::{
};
use rustfs_tls_runtime::{load_certs, load_private_key};
use rustls::ClientConfig;
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::fmt;
use std::sync::Arc;
use std::{
@@ -524,7 +523,7 @@ struct BgTaskManager {
/// A target that sends events to an MQTT broker
pub struct MQTTTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
id: TargetID,
args: MQTTArgs,
@@ -544,7 +543,7 @@ where
impl<E> MQTTTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
/// Creates a new MQTTTarget
#[instrument(skip(args), fields(target_id_as_string = %id))]
@@ -844,7 +843,7 @@ where
#[async_trait]
impl<E> ReloadableTargetTls for MQTTTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
type Material = MqttOptions;
@@ -1130,7 +1129,7 @@ fn is_fatal_mqtt_error(err: &ConnectionError) -> bool {
#[async_trait]
impl<E> Target<E> for MQTTTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
fn id(&self) -> TargetID {
self.id.clone()
+5 -6
View File
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::plugin::PluginEvent;
use crate::{
StoreError, Target,
arn::TargetID,
@@ -31,8 +32,6 @@ use async_trait::async_trait;
use mysql_async::{Conn, Opts, OptsBuilder, Pool, PoolConstraints, PoolOpts, SslOpts, prelude::Queryable};
use rustfs_config::{MYSQL_TLS_CA, MYSQL_TLS_CLIENT_CERT, MYSQL_TLS_CLIENT_KEY};
use rustfs_tls_runtime::{load_certs, load_private_key};
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::fmt;
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
@@ -505,7 +504,7 @@ async fn validate_existing_schema(conn: &mut Conn, table: &str) -> Result<(), Ta
/// ```
pub struct MySqlTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
/// Unique target identifier (name + type)
id: TargetID,
@@ -528,7 +527,7 @@ where
impl<E> MySqlTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
/// Creates a new MySqlTarget.
///
@@ -770,7 +769,7 @@ pub(crate) fn map_mysql_error(err: mysql_async::Error, operation: &str) -> Targe
#[async_trait]
impl<E> Target<E> for MySqlTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
fn id(&self) -> TargetID {
self.id.clone()
@@ -927,7 +926,7 @@ where
#[async_trait]
impl<E> ReloadableTargetTls for MySqlTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
type Material = Pool;
+5 -6
View File
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::plugin::PluginEvent;
use crate::{
StoreError, Target,
arn::TargetID,
@@ -29,8 +30,6 @@ use crate::{
};
use async_trait::async_trait;
use rustfs_config::{NATS_CREDENTIALS_FILE, NATS_TLS_CA, NATS_TLS_CLIENT_CERT, NATS_TLS_CLIENT_KEY};
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::fmt;
use std::path::{Path, PathBuf};
use std::str::FromStr;
@@ -192,7 +191,7 @@ pub async fn connect_nats(args: &NATSArgs) -> Result<async_nats::Client, TargetE
pub struct NATSTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
id: TargetID,
args: NATSArgs,
@@ -210,7 +209,7 @@ where
impl<E> NATSTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
pub fn clone_box(&self) -> Box<dyn Target<E> + Send + Sync> {
Box::new(NATSTarget::<E> {
@@ -318,7 +317,7 @@ where
#[async_trait]
impl<E> Target<E> for NATSTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
fn id(&self) -> TargetID {
self.id.clone()
@@ -415,7 +414,7 @@ where
#[async_trait]
impl<E> ReloadableTargetTls for NATSTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
type Material = async_nats::Client;
+5 -6
View File
@@ -25,6 +25,7 @@
//! Connection pooling is delegated to `deadpool-postgres`; the pool itself
//! is `Clone`, so no `Mutex` is required around it.
use crate::plugin::PluginEvent;
use crate::{
StoreError, Target,
arn::TargetID,
@@ -44,8 +45,6 @@ use async_trait::async_trait;
use deadpool_postgres::{Manager, ManagerConfig, Pool, RecyclingMethod};
use rustfs_config::{POSTGRES_DSN_STRING, POSTGRES_TLS_CA, POSTGRES_TLS_CLIENT_CERT, POSTGRES_TLS_CLIENT_KEY};
use rustfs_tls_runtime::{load_certs, load_private_key};
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::fmt;
use std::path::Path;
use std::sync::Arc;
@@ -555,7 +554,7 @@ fn resolve_payload_key(payload: &serde_json::Value, meta: &QueuedPayloadMeta) ->
/// the legacy inline fingerprint check is used as a fallback.
pub struct PostgresTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
id: TargetID,
args: PostgresArgs,
@@ -573,7 +572,7 @@ where
impl<E> PostgresTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
pub fn clone_box(&self) -> Box<dyn Target<E> + Send + Sync> {
Box::new(PostgresTarget::<E> {
@@ -692,7 +691,7 @@ where
#[async_trait]
impl<E> ReloadableTargetTls for PostgresTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
type Material = Pool;
@@ -727,7 +726,7 @@ where
#[async_trait]
impl<E> Target<E> for PostgresTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
fn id(&self) -> TargetID {
self.id.clone()
+5 -6
View File
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::plugin::PluginEvent;
use crate::{
StoreError, Target,
arn::TargetID,
@@ -30,8 +31,6 @@ use crate::{
use async_trait::async_trait;
use pulsar::{Authentication, Producer, Pulsar, TokioExecutor};
use rustfs_tls_runtime::load_cert_bundle_der_bytes;
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::fmt;
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
@@ -186,7 +185,7 @@ pub async fn connect_pulsar(args: &PulsarArgs) -> Result<Pulsar<TokioExecutor>,
pub struct PulsarTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
id: TargetID,
args: PulsarArgs,
@@ -203,7 +202,7 @@ where
impl<E> PulsarTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
pub fn clone_box(&self) -> Box<dyn Target<E> + Send + Sync> {
Box::new(PulsarTarget::<E> {
@@ -339,7 +338,7 @@ where
#[async_trait]
impl<E> ReloadableTargetTls for PulsarTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
type Material = Pulsar<TokioExecutor>;
@@ -384,7 +383,7 @@ where
#[async_trait]
impl<E> Target<E> for PulsarTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
fn id(&self) -> TargetID {
self.id.clone()
+5 -6
View File
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::plugin::PluginEvent;
use crate::{
StoreError, Target,
arn::TargetID,
@@ -37,8 +38,6 @@ use redis::{
use rustfs_config::{REDIS_TLS_CA, REDIS_TLS_CLIENT_CERT, REDIS_TLS_CLIENT_KEY, REDIS_TLS_POLICY};
use rustls::pki_types::CertificateDer;
use rustls::pki_types::pem::PemObject;
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::fmt;
use std::io::BufReader;
use std::path::Path;
@@ -300,7 +299,7 @@ fn validate_redis_tls_config(url: &Url, tls: &RedisTlsConfig) -> Result<(), Targ
pub struct RedisTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
id: TargetID,
args: RedisArgs,
@@ -326,7 +325,7 @@ where
impl<E> RedisTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
#[instrument(skip(args), fields(target_id_as_string = %id))]
pub fn new(id: String, args: RedisArgs) -> Result<Self, TargetError> {
@@ -518,7 +517,7 @@ where
#[async_trait]
impl<E> Target<E> for RedisTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
fn id(&self) -> TargetID {
self.id.clone()
@@ -796,7 +795,7 @@ fn compute_retry_delay(attempt: usize, min_delay: Duration, max_delay: Duration)
#[async_trait]
impl<E> ReloadableTargetTls for RedisTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
type Material = Client;
+5 -6
View File
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::plugin::PluginEvent;
use crate::{
StoreError, Target,
arn::TargetID,
@@ -32,8 +33,6 @@ use parking_lot::Mutex;
use reqwest::{Client, StatusCode, Url};
use rustfs_tls_runtime::load_cert_bundle_der_bytes;
use rustfs_utils::egress::validate_outbound_url;
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::{
fmt,
marker::PhantomData,
@@ -132,7 +131,7 @@ impl WebhookArgs {
/// A target that sends events to a webhook
pub struct WebhookTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
id: TargetID,
args: WebhookArgs,
@@ -152,7 +151,7 @@ where
impl<E> WebhookTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
/// Clones the WebhookTarget, creating a new instance with the same configuration
pub fn clone_box(&self) -> Box<dyn Target<E> + Send + Sync> {
@@ -476,7 +475,7 @@ where
#[async_trait]
impl<E> Target<E> for WebhookTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
fn id(&self) -> TargetID {
self.id.clone()
@@ -643,7 +642,7 @@ where
#[async_trait]
impl<E> ReloadableTargetTls for WebhookTarget<E>
where
E: Send + Sync + 'static + Clone + Serialize + DeserializeOwned,
E: PluginEvent,
{
type Material = Client;