chore: 针对备份及还原处理
This commit is contained in:
538
scripts/patch_tools/patch_verifier.sh
Normal file
538
scripts/patch_tools/patch_verifier.sh
Normal file
@@ -0,0 +1,538 @@
|
||||
#!/bin/bash
|
||||
# patch_verifier.sh - 企业级补丁包验证脚本
|
||||
|
||||
set -euo pipefail
|
||||
shopt -s nullglob extglob
|
||||
|
||||
# 脚本目录
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CONFIG_FILE="${SCRIPT_DIR}/patch_config.sh"
|
||||
|
||||
# 颜色定义
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
PURPLE='\033[0;35m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m'
|
||||
|
||||
# 日志函数
|
||||
log() {
|
||||
local level="$1"
|
||||
local message="$2"
|
||||
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
case "$level" in
|
||||
"INFO") color="$GREEN" ;;
|
||||
"WARN") color="$YELLOW" ;;
|
||||
"ERROR") color="$RED" ;;
|
||||
"DEBUG") color="$BLUE" ;;
|
||||
*) color="$NC" ;;
|
||||
esac
|
||||
|
||||
echo -e "${color}[$timestamp] [$level]${NC} $message" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
info() { log "INFO" "$1"; }
|
||||
warn() { log "WARN" "$1"; }
|
||||
error() { log "ERROR" "$1"; }
|
||||
debug() { log "DEBUG" "$1"; }
|
||||
|
||||
# 配置和初始化
|
||||
load_config() {
|
||||
if [[ ! -f "$CONFIG_FILE" ]]; then
|
||||
error "配置文件不存在: $CONFIG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source "$CONFIG_FILE"
|
||||
info "配置文件加载完成"
|
||||
|
||||
: ${LOG_LEVEL:="INFO"}
|
||||
: ${LOG_FILE:="/tmp/patch_verify_$(date +%Y%m%d_%H%M%S).log"}
|
||||
: ${TEMP_DIR:="/tmp/patch_verify_$$"}
|
||||
}
|
||||
|
||||
setup_environment() {
|
||||
mkdir -p "$(dirname "$LOG_FILE")"
|
||||
mkdir -p "$TEMP_DIR"
|
||||
|
||||
info "日志文件: $LOG_FILE"
|
||||
info "临时目录: $TEMP_DIR"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [[ -d "$TEMP_DIR" ]]; then
|
||||
rm -rf "$TEMP_DIR"
|
||||
info "临时目录已清理: $TEMP_DIR"
|
||||
fi
|
||||
}
|
||||
|
||||
trap cleanup EXIT
|
||||
|
||||
# 验证函数
|
||||
verify_package_integrity() {
|
||||
local package_path="$1"
|
||||
local verify_type="$2" # pre-apply, post-apply, standalone
|
||||
|
||||
info "开始验证补丁包: $package_path (类型: $verify_type)"
|
||||
|
||||
local overall_result=true
|
||||
|
||||
# 1. 基础验证
|
||||
if ! verify_basic_integrity "$package_path"; then
|
||||
overall_result=false
|
||||
fi
|
||||
|
||||
# 2. 安全验证
|
||||
if ! verify_security "$package_path"; then
|
||||
overall_result=false
|
||||
fi
|
||||
|
||||
# 3. 内容验证
|
||||
if ! verify_content "$package_path" "$verify_type"; then
|
||||
overall_result=false
|
||||
fi
|
||||
|
||||
# 4. 系统状态验证
|
||||
if [[ "$verify_type" == "post-apply" ]]; then
|
||||
if ! verify_system_state "$package_path"; then
|
||||
overall_result=false
|
||||
fi
|
||||
fi
|
||||
|
||||
# 生成验证报告
|
||||
generate_verification_report "$package_path" "$overall_result" "$verify_type"
|
||||
|
||||
if $overall_result; then
|
||||
info "✅ 补丁包验证通过"
|
||||
return 0
|
||||
else
|
||||
error "❌ 补丁包验证失败"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
verify_basic_integrity() {
|
||||
local package_path="$1"
|
||||
|
||||
info "执行基础完整性验证..."
|
||||
local result=true
|
||||
|
||||
# 文件存在性检查
|
||||
if [[ ! -f "$package_path" ]]; then
|
||||
error "❌ 补丁包不存在: $package_path"
|
||||
result=false
|
||||
fi
|
||||
|
||||
# 文件大小检查
|
||||
local size=$(stat -c%s "$package_path" 2>/dev/null || echo "0")
|
||||
if [[ $size -eq 0 ]]; then
|
||||
error "❌ 补丁包为空"
|
||||
result=false
|
||||
else
|
||||
info "✅ 文件大小: $(numfmt --to=iec-i --suffix=B $size)"
|
||||
fi
|
||||
|
||||
# 压缩包完整性检查
|
||||
if ! tar -tzf "$package_path" >/dev/null 2>&1; then
|
||||
error "❌ 压缩包损坏或格式错误"
|
||||
result=false
|
||||
else
|
||||
info "✅ 压缩包完整性验证通过"
|
||||
fi
|
||||
|
||||
$result
|
||||
}
|
||||
|
||||
verify_security() {
|
||||
local package_path="$1"
|
||||
|
||||
info "执行安全验证..."
|
||||
local result=true
|
||||
|
||||
# 校验和验证
|
||||
if [[ -f "${package_path}.sha256" ]]; then
|
||||
if sha256sum -c "${package_path}.sha256" >/dev/null 2>&1; then
|
||||
info "✅ 校验和验证通过"
|
||||
else
|
||||
error "❌ 校验和验证失败"
|
||||
result=false
|
||||
fi
|
||||
else
|
||||
warn "⚠️ 未找到校验和文件"
|
||||
fi
|
||||
|
||||
# 签名验证
|
||||
if [[ -f "${package_path}.sig" ]]; then
|
||||
if command -v gpg >/dev/null 2>&1; then
|
||||
if gpg --verify "${package_path}.sig" "$package_path" >/dev/null 2>&1; then
|
||||
info "✅ 签名验证通过"
|
||||
else
|
||||
error "❌ 签名验证失败"
|
||||
result=false
|
||||
fi
|
||||
else
|
||||
warn "⚠️ GPG未安装,跳过签名验证"
|
||||
fi
|
||||
else
|
||||
warn "⚠️ 未找到签名文件"
|
||||
fi
|
||||
|
||||
$result
|
||||
}
|
||||
|
||||
verify_content() {
|
||||
local package_path="$1"
|
||||
local verify_type="$2"
|
||||
|
||||
info "执行内容验证..."
|
||||
local result=true
|
||||
|
||||
# 解压补丁包
|
||||
local extract_dir="$TEMP_DIR/extract"
|
||||
if ! tar -xzf "$package_path" -C "$extract_dir"; then
|
||||
error "❌ 补丁包解压失败"
|
||||
return false
|
||||
fi
|
||||
|
||||
# 检查清单文件
|
||||
local manifest_file="$extract_dir/MANIFEST.MF"
|
||||
if [[ ! -f "$manifest_file" ]]; then
|
||||
error "❌ 清单文件不存在"
|
||||
result=false
|
||||
else
|
||||
info "✅ 清单文件存在"
|
||||
|
||||
# 验证清单格式
|
||||
if ! validate_manifest_format "$manifest_file"; then
|
||||
result=false
|
||||
fi
|
||||
|
||||
# 验证文件完整性
|
||||
if ! validate_file_integrity "$extract_dir" "$manifest_file" "$verify_type"; then
|
||||
result=false
|
||||
fi
|
||||
fi
|
||||
|
||||
$result
|
||||
}
|
||||
|
||||
validate_manifest_format() {
|
||||
local manifest_file="$1"
|
||||
local result=true
|
||||
|
||||
# 检查必需字段
|
||||
local required_fields=("名称" "版本" "生成时间")
|
||||
for field in "${required_fields[@]}"; do
|
||||
if ! grep -q "^$field:" "$manifest_file"; then
|
||||
error "❌ 清单缺少必需字段: $field"
|
||||
result=false
|
||||
fi
|
||||
done
|
||||
|
||||
# 检查变更记录格式
|
||||
local change_count=$(grep -c -E "^(ADDED|MODIFIED|DELETED)" "$manifest_file" || true)
|
||||
if [[ $change_count -eq 0 ]]; then
|
||||
warn "⚠️ 清单中没有变更记录"
|
||||
else
|
||||
info "✅ 变更记录数量: $change_count"
|
||||
fi
|
||||
|
||||
$result
|
||||
}
|
||||
|
||||
validate_file_integrity() {
|
||||
local extract_dir="$1"
|
||||
local manifest_file="$2"
|
||||
local verify_type="$3"
|
||||
|
||||
info "验证文件完整性..."
|
||||
local result=true
|
||||
local verified_count=0
|
||||
local error_count=0
|
||||
|
||||
# 处理清单中的每个变更记录
|
||||
while IFS='|' read -r change_type path extra_info; do
|
||||
case "$change_type" in
|
||||
"ADDED"|"MODIFIED")
|
||||
if ! verify_patch_file "$extract_dir" "$path" "$change_type" "$verify_type"; then
|
||||
error_count=$((error_count + 1))
|
||||
result=false
|
||||
else
|
||||
verified_count=$((verified_count + 1))
|
||||
fi
|
||||
;;
|
||||
"DELETED")
|
||||
if ! verify_deleted_file "$path" "$verify_type"; then
|
||||
error_count=$((error_count + 1))
|
||||
result=false
|
||||
else
|
||||
verified_count=$((verified_count + 1))
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done < <(grep -E "^(ADDED|MODIFIED|DELETED)" "$manifest_file")
|
||||
|
||||
info "文件验证完成: $verified_count 个文件成功, $error_count 个文件失败"
|
||||
$result
|
||||
}
|
||||
|
||||
verify_patch_file() {
|
||||
local extract_dir="$1"
|
||||
local file_path="$2"
|
||||
local change_type="$3"
|
||||
local verify_type="$4"
|
||||
|
||||
local patch_file="$extract_dir/files/$file_path"
|
||||
local target_file="/$file_path"
|
||||
|
||||
# 检查补丁包中的文件是否存在
|
||||
if [[ ! -f "$patch_file" ]]; then
|
||||
error "❌ 补丁包中文件不存在: $file_path"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 对于应用后验证,检查目标文件
|
||||
if [[ "$verify_type" == "post-apply" ]]; then
|
||||
if [[ ! -f "$target_file" ]]; then
|
||||
error "❌ 目标文件不存在: $target_file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# 比较文件内容
|
||||
local patch_hash=$(sha256sum "$patch_file" | cut -d' ' -f1)
|
||||
local target_hash=$(sha256sum "$target_file" | cut -d' ' -f1)
|
||||
|
||||
if [[ "$patch_hash" != "$target_hash" ]]; then
|
||||
error "❌ 文件内容不匹配: $file_path"
|
||||
return 1
|
||||
fi
|
||||
|
||||
info "✅ 文件验证通过: $file_path"
|
||||
else
|
||||
info "✅ 补丁文件存在: $file_path"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
verify_deleted_file() {
|
||||
local file_path="$1"
|
||||
local verify_type="$2"
|
||||
|
||||
local target_file="/$file_path"
|
||||
|
||||
# 对于应用后验证,检查文件是否已删除
|
||||
if [[ "$verify_type" == "post-apply" ]]; then
|
||||
if [[ -f "$target_file" ]]; then
|
||||
error "❌ 文件未成功删除: $target_file"
|
||||
return 1
|
||||
fi
|
||||
info "✅ 文件删除验证通过: $file_path"
|
||||
else
|
||||
info "✅ 删除操作记录存在: $file_path"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
verify_system_state() {
|
||||
local package_path="$1"
|
||||
|
||||
info "验证系统状态..."
|
||||
local result=true
|
||||
|
||||
# 检查关键服务状态
|
||||
if ! verify_services; then
|
||||
result=false
|
||||
fi
|
||||
|
||||
# 检查磁盘空间
|
||||
if ! verify_disk_space; then
|
||||
result=false
|
||||
fi
|
||||
|
||||
# 检查系统负载
|
||||
if ! verify_system_load; then
|
||||
result=false
|
||||
fi
|
||||
|
||||
$result
|
||||
}
|
||||
|
||||
verify_services() {
|
||||
local result=true
|
||||
|
||||
# 检查Web服务
|
||||
if systemctl is-active --quiet nginx || systemctl is-active --quiet apache2; then
|
||||
info "✅ Web服务运行正常"
|
||||
else
|
||||
warn "⚠️ Web服务未运行"
|
||||
fi
|
||||
|
||||
# 检查数据库服务
|
||||
if systemctl is-active --quiet mysql || systemctl is-active --quiet postgresql; then
|
||||
info "✅ 数据库服务运行正常"
|
||||
else
|
||||
warn "⚠️ 数据库服务未运行"
|
||||
fi
|
||||
|
||||
$result
|
||||
}
|
||||
|
||||
verify_disk_space() {
|
||||
local result=true
|
||||
local threshold=90 # 90% 使用率阈值
|
||||
|
||||
# 检查根分区使用率
|
||||
local usage=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')
|
||||
if [[ $usage -gt $threshold ]]; then
|
||||
error "❌ 磁盘空间不足: / 分区使用率 $usage%"
|
||||
result=false
|
||||
else
|
||||
info "✅ 磁盘空间正常: / 分区使用率 $usage%"
|
||||
fi
|
||||
|
||||
$result
|
||||
}
|
||||
|
||||
verify_system_load() {
|
||||
local result=true
|
||||
local load_threshold=10.0 # 负载阈值
|
||||
|
||||
# 获取系统负载
|
||||
local load=$(awk '{print $1}' /proc/loadavg)
|
||||
local cores=$(nproc)
|
||||
|
||||
if (( $(echo "$load > $cores" | bc -l) )); then
|
||||
warn "⚠️ 系统负载较高: $load (CPU核心数: $cores)"
|
||||
else
|
||||
info "✅ 系统负载正常: $load (CPU核心数: $cores)"
|
||||
fi
|
||||
|
||||
$result
|
||||
}
|
||||
|
||||
# 报告生成函数
|
||||
generate_verification_report() {
|
||||
local package_path="$1"
|
||||
local overall_result="$2"
|
||||
local verify_type="$3"
|
||||
|
||||
local report_file="$TEMP_DIR/verification_report_$(date +%Y%m%d_%H%M%S).txt"
|
||||
|
||||
cat > "$report_file" << EOF
|
||||
补丁包验证报告
|
||||
========================================
|
||||
补丁包: $(basename "$package_path")
|
||||
验证类型: $verify_type
|
||||
验证时间: $(date)
|
||||
验证主机: $(hostname)
|
||||
总体结果: $(if $overall_result; then echo "通过"; else echo "失败"; fi)
|
||||
|
||||
详细验证结果:
|
||||
EOF
|
||||
|
||||
# 添加详细验证信息
|
||||
{
|
||||
echo "1. 基础完整性验证: $(if verify_basic_integrity "$package_path"; then echo "通过"; else echo "失败"; fi)"
|
||||
echo "2. 安全验证: $(if verify_security "$package_path"; then echo "通过"; else echo "失败"; fi)"
|
||||
echo "3. 内容验证: $(if verify_content "$package_path" "$verify_type"; then echo "通过"; else echo "失败"; fi)"
|
||||
if [[ "$verify_type" == "post-apply" ]]; then
|
||||
echo "4. 系统状态验证: $(if verify_system_state "$package_path"; then echo "通过"; else echo "失败"; fi)"
|
||||
fi
|
||||
} >> "$report_file"
|
||||
|
||||
info "验证报告生成完成: $report_file"
|
||||
|
||||
# 显示报告摘要
|
||||
echo
|
||||
echo "验证报告摘要:"
|
||||
cat "$report_file"
|
||||
echo
|
||||
}
|
||||
|
||||
# 批量验证函数
|
||||
batch_verify() {
|
||||
local patch_dir="${1:-$OUTPUT_DIRECTORY}"
|
||||
local verify_type="${2:-standalone}"
|
||||
|
||||
info "开始批量验证补丁包目录: $patch_dir"
|
||||
|
||||
local total_count=0
|
||||
local success_count=0
|
||||
local fail_count=0
|
||||
|
||||
# 查找所有补丁包文件
|
||||
for patch_file in "$patch_dir"/*.tar.gz; do
|
||||
if [[ -f "$patch_file" ]]; then
|
||||
((total_count++))
|
||||
info "验证补丁包: $(basename "$patch_file")"
|
||||
|
||||
if verify_package_integrity "$patch_file" "$verify_type"; then
|
||||
((success_count++))
|
||||
else
|
||||
((fail_count++))
|
||||
fi
|
||||
|
||||
echo "----------------------------------------"
|
||||
fi
|
||||
done
|
||||
|
||||
info "批量验证完成: $success_count/$total_count 成功, $fail_count/$total_count 失败"
|
||||
|
||||
if [[ $fail_count -eq 0 ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 主验证函数
|
||||
main_verify() {
|
||||
local patch_path="${1:-}"
|
||||
local verify_type="${2:-standalone}"
|
||||
local batch_mode="${3:-false}"
|
||||
|
||||
# 加载配置
|
||||
load_config
|
||||
|
||||
# 设置环境
|
||||
setup_environment
|
||||
|
||||
# 批量验证模式
|
||||
if [[ "$batch_mode" == "true" ]] || [[ -d "$patch_path" ]]; then
|
||||
batch_verify "$patch_path" "$verify_type"
|
||||
return $?
|
||||
fi
|
||||
|
||||
# 单个文件验证
|
||||
if [[ -z "$patch_path" ]]; then
|
||||
echo "用法: $0 <补丁包路径|目录> [verify-type] [batch]"
|
||||
echo "验证类型:"
|
||||
echo " standalone - 独立验证(默认)"
|
||||
echo " pre-apply - 应用前验证"
|
||||
echo " post-apply - 应用后验证"
|
||||
echo "示例:"
|
||||
echo " $0 /opt/patches/patch.tar.gz"
|
||||
echo " $0 /opt/patches/patch.tar.gz pre-apply"
|
||||
echo " $0 /opt/patches/ batch"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 执行验证
|
||||
if verify_package_integrity "$patch_path" "$verify_type"; then
|
||||
info "🎉 补丁包验证成功"
|
||||
exit 0
|
||||
else
|
||||
error "💥 补丁包验证失败"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# 异常处理
|
||||
trap 'error "脚本执行中断"; cleanup; exit 1' INT TERM
|
||||
|
||||
main_verify "$@"
|
||||
Reference in New Issue
Block a user