secra_plugins 0.1.32

生产级插件系统 - 插件的生命周期
Documentation
//! 插件加载相关操作

use crate::config::PluginConfig;
use crate::error::{PluginManagerError, PluginManagerResult};
use crate::status::PluginStatus;
use crate::manager::types::{PluginInstance, PluginMap};
use crate::manager::loader::{load_plugin, scan_plugin_directory};
use crate::manager::validation::sanitize_plugin_name;
use std::path::Path;
use tracing::{debug, error, info, trace, warn};
use tracing_shared::SharedLogger;

/// 加载所有插件
///
/// 从指定的插件目录扫描并加载所有插件文件(.spk 格式)。
/// 加载过程包括签名验证、解包、动态库加载和实例创建。
/// 子插件会在父插件加载完成后挂载到对应的父插件上。
///
/// # 参数
/// * `plugins` - 插件实例的共享映射表,使用 `Arc<RwLock<>>` 保证线程安全
/// * `plugin_dir` - 插件文件所在的目录路径
/// * `temp_dir` - 插件解包的临时目录路径
/// * `ed25519_public_key_path` - Ed25519 公钥文件路径,用于验证插件签名
/// * `rsa_private_key_path` - RSA 私钥文件路径,用于解密插件包
/// * `library_path` - 可选的动态库子路径,如果插件包中的动态库不在根目录
///
/// # 返回值
/// * `PluginManagerResult<()>` - 成功时返回 `Ok(())`
///
/// # 行为
/// * 清空并重新创建临时目录
/// * 扫描插件目录,查找所有 `.spk` 文件
/// * 逐个加载插件,主插件直接插入,子插件暂存
/// * 所有主插件加载完成后,将子插件挂载到对应的父插件
/// * 如果某个插件加载失败,会记录错误但继续加载其他插件
///
/// # 错误
/// * 如果插件目录不存在,会记录警告但不会返回错误
/// * 如果某个插件加载失败,会记录错误但继续处理其他插件
///
/// # 示例
/// ```no_run
/// use std::sync::Arc;
/// use tokio::sync::RwLock;
/// use std::collections::HashMap;
/// 
/// # async fn example() {
/// let plugins = Arc::new(RwLock::new(HashMap::new()));
/// load_all_plugins(
///     plugins,
///     "/opt/secra/plugins",
///     "/opt/secra/plugins/temp",
///     Some("/path/to/ed25519.pub".to_string()),
///     Some("/path/to/rsa.key".to_string()),
///     None,
/// ).await?;
/// # }
/// ```
pub async fn load_all_plugins(
    plugins: PluginMap,
    plugin_dir: &str,
    temp_dir: &str,
    ed25519_public_key_path: Option<String>,
    rsa_private_key_path: Option<String>,
    library_path: Option<&String>,
) -> PluginManagerResult<()> {
    trace!("开始批量加载插件 - 目录: {}, 临时目录: {}", plugin_dir, temp_dir);
    info!("开始加载插件...");

    // 清空临时目录
    debug!("步骤 1: 清空并创建临时目录");
    info!("清空插件临时目录: {}", temp_dir);
    if Path::new(temp_dir).exists() {
        trace!("临时目录已存在,删除: {}", temp_dir);
        tokio::fs::remove_dir_all(temp_dir).await?;
    }
    tokio::fs::create_dir_all(temp_dir).await?;
    debug!("临时目录创建成功");

    // 检查插件目录是否存在
    if !Path::new(plugin_dir).exists() {
        warn!("插件目录不存在: {}", plugin_dir);
        return Ok(());
    }
    trace!("插件目录存在: {}", plugin_dir);

    // 扫描插件目录
    debug!("步骤 2: 扫描插件目录");
    let plugin_files = scan_plugin_directory(plugin_dir).await?;

    if plugin_files.is_empty() {
        info!("未找到任何插件文件");
        return Ok(());
    } else {
        info!("发现 {} 个插件文件", plugin_files.len());
        debug!("插件文件列表: {:?}", plugin_files);
    }

    // 加载每个插件
    debug!("步骤 3: 逐个加载插件");
    let mut child_instances: Vec<PluginInstance> = Vec::new();
    let mut success_count = 0;
    let mut failure_count = 0;

    let logger = SharedLogger::new();
    
    for plugin_file in plugin_files {
        trace!("开始加载插件文件: {}", plugin_file);
        match load_plugin(
            &plugin_file,
            ed25519_public_key_path.clone(),
            rsa_private_key_path.clone(),
            temp_dir,
            library_path,
            logger.clone()
        )
        .await
        {
            Ok(instance) => {
                debug!("插件加载成功: {} (ID: {})", 
                       instance.metadata.name, instance.metadata.id);
                if instance.metadata.is_sub_plugin {
                    trace!("检测到子插件,暂存: {}", instance.metadata.id);
                    child_instances.push(instance);
                } else if let Err(e) = insert_plugin_instance(
                    plugins.clone(),
                    instance,
                ).await {
                    error!("保存插件实例失败 {}: {}", plugin_file, e);
                    failure_count += 1;
                } else {
                    success_count += 1;
                }
            }
            Err(e) => {
                error!("插件加载失败: {} - {}", plugin_file, e);
                failure_count += 1;
            }
        }
    }

    // 子插件需要在父插件之后挂载
    debug!("步骤 4: 挂载子插件 (共 {} 个)", child_instances.len());
    for child_instance in child_instances {
        trace!("挂载子插件: {}", child_instance.metadata.id);
        if let Err(e) = insert_child_plugin(
            plugins.clone(),
            child_instance,
        ).await {
            error!("子插件挂载失败: {}", e);
            failure_count += 1;
        } else {
            success_count += 1;
        }
    }

    info!("插件加载完成 - 成功: {}, 失败: {}, 总计: {}", 
          success_count, failure_count, success_count + failure_count);
    trace!("批量加载插件流程完成");
    Ok(())
}

/// 将插件实例写入管理器
///
/// 将已创建的插件实例插入到插件管理器的映射表中。
/// 此函数用于主插件的插入,会记录详细的加载日志。
///
/// # 参数
/// * `plugins` - 插件实例的共享映射表,使用 `Arc<RwLock<>>` 保证线程安全
/// * `plugin_instance` - 要插入的插件实例
///
/// # 返回值
/// * `PluginManagerResult<()>` - 成功时返回 `Ok(())`
///
/// # 行为
/// * 使用插件的ID作为键插入到映射表
/// * 记录包含插件ID、名称、版本和指纹的详细日志
/// * 使用 tracing span 注入上下文信息,便于日志追踪
///
/// # 注意
/// 此函数只用于主插件。子插件应使用 `insert_child_plugin` 函数。
///
/// # 示例
/// ```no_run
/// use crate::manager::types::PluginMap;
/// use std::sync::Arc;
/// use tokio::sync::RwLock;
/// use std::collections::HashMap;
/// 
/// # async fn example() {
/// let plugins: PluginMap = Arc::new(RwLock::new(HashMap::new()));
/// // ... 创建 plugin_instance ...
/// insert_plugin_instance(plugins, plugin_instance).await?;
/// # }
/// ```
pub async fn insert_plugin_instance(
    plugins: PluginMap,
    plugin_instance: PluginInstance,
) -> PluginManagerResult<()> {
    // 在插入前获取必要信息,避免在锁内克隆
    let plugin_id = plugin_instance.metadata.id.clone();
    let plugin_name = plugin_instance.metadata.name.clone();
    let plugin_version = plugin_instance.metadata.version.clone();
    let fingerprint = plugin_instance.fingerprint.clone();

    trace!("准备插入插件实例: {} ({})", plugin_name, plugin_id);
    
    let span = tracing::info_span!(
        "plugin_insert",
        plugin_id = %plugin_id,
        fingerprint = %fingerprint,
        plugin_version = %plugin_version
    );
    let _guard = span.enter();

    // 缩小锁范围:只在插入时持有写锁
    {
        debug!("获取写锁,插入插件实例");
        let mut plugins = plugins.write().await;
        plugins.insert(plugin_id.clone(), plugin_instance);
        trace!("插件实例已插入到映射表");
    }

    // 在锁外记录日志
    info!(
        plugin_id = %plugin_id,
        fingerprint = %fingerprint,
        plugin_version = %plugin_version,
        "插件加载成功: {} ({}) v{}",
        plugin_name, plugin_id, plugin_version
    );
    debug!("插件实例插入完成");

    Ok(())
}

/// 将子插件挂载到对应父插件
///
/// 将子插件实例插入到插件管理器,并同时将其信息添加到父插件的 `child_plugins` 列表中。
/// 这确保了父子插件关系的正确建立。
///
/// # 参数
/// * `plugins` - 插件实例的共享映射表,使用 `Arc<RwLock<>>` 保证线程安全
/// * `plugin_instance` - 要插入的子插件实例
///
/// # 返回值
/// * `PluginManagerResult<()>` - 成功时返回 `Ok(())`
///
/// # 错误
/// * `PluginManagerError::Other` - 如果子插件未配置父插件ID
/// * `PluginManagerError::NotFound` - 如果父插件不存在
///
/// # 行为
/// * 检查子插件是否配置了父插件ID
/// * 验证父插件是否存在
/// * 将子插件信息添加到父插件的 `child_plugins` 列表
/// * 将子插件实例插入到映射表
/// * 记录详细的挂载日志
///
/// # 示例
/// ```no_run
/// use crate::manager::types::PluginMap;
/// use std::sync::Arc;
/// use tokio::sync::RwLock;
/// use std::collections::HashMap;
/// 
/// # async fn example() {
/// let plugins: PluginMap = Arc::new(RwLock::new(HashMap::new()));
/// // ... 创建子插件实例 ...
/// insert_child_plugin(plugins, child_instance).await?;
/// # }
/// ```
pub async fn insert_child_plugin(
    plugins: PluginMap,
    plugin_instance: PluginInstance,
) -> PluginManagerResult<()> {
    // 在插入前获取必要信息,避免在锁内克隆
    let child_id = plugin_instance.metadata.id.clone();
    let child_name = plugin_instance.metadata.name.clone();
    let child_version = plugin_instance.metadata.version.clone();

    trace!("准备挂载子插件: {} -> 父插件", child_id);
    
    let parent_id = plugin_instance
        .metadata
        .parent_plugin_id
        .as_ref()
        .ok_or_else(|| {
            error!("子插件 {} 未配置父插件 ID", child_id);
            PluginManagerError::Other(format!("子插件 {} 未配置父插件 ID", child_id))
        })?
        .clone();
    
    debug!("子插件 {} 的父插件: {}", child_id, parent_id);

    let child_info = crate::status::ChildPluginInfo::from_metadata(&plugin_instance.metadata);

    // 缩小锁范围:只在修改映射表时持有写锁
    {
        debug!("获取写锁,挂载子插件到父插件");
        let mut plugins = plugins.write().await;

        if let Some(parent) = plugins.get_mut(&parent_id) {
            trace!("找到父插件,添加子插件信息");
            parent.child_plugins.push(child_info);
        } else {
            error!("未找到父插件 {},无法挂载子插件 {}", parent_id, child_id);
            return Err(PluginManagerError::not_found(format!(
                "未找到父插件 {},无法挂载子插件 {}",
                parent_id, child_id
            )));
        }

        plugins.insert(child_id.clone(), plugin_instance);
        trace!("子插件实例已插入到映射表");
    }

    // 在锁外记录日志
    info!(
        "子插件加载成功: {} ({}) v{} -> 父插件 {}",
        child_name, child_id, child_version, parent_id
    );
    debug!("子插件挂载完成");

    Ok(())
}