G-1: 메신저 Webhook Relay + _send_to_room 실제 httpx 호출 구현 G-2: POST /api/tasks/bulk SR 대량작업 엔드포인트 (최대 100건) G-3: 라이선스 만료 알림 스케줄러 (매일 09:00 KST) G-4: 체험판 upgrade_banner 필드 + license.py 배너 로직 G-5: core/auto_rca.py + incidents/problem auto-rca 엔드포인트 G-6: core/deploy_impact.py + vibe impact-analysis 엔드포인트 G-7: core/ticket_classifier.py + SR 생성 시 AI 분류 + ai-suggestion API G-8: VulnPatchRecord 모델 + vuln_scan 패치추적 4개 엔드포인트 G-9: core/jira_sync.py + gateway Jira/Confluence 연동 엔드포인트 G-10: core/push_notify.py + routers/push.py + PushSubscription 모델 G-11: approvals 다중승인 (위임/서명/기한초과/마감연장) G-12: alembic.ini + migrations/ + cicd/migrate_to_postgres.sh 하네스: guardia-orchestrator 확장기능 Phase 반영 봇명령어: /sr /status /license /bulk 슬래시 명령어 추가 설치스크립트: setup/ (Ubuntu, CentOS, RHEL, Windows) --test 옵션 포함 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
143 lines
5.8 KiB
Bash
143 lines
5.8 KiB
Bash
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# GUARDiA ITSM 결과 콜백 스크립트
|
|
# Jenkins Stage 8: ITSM Callback에서 호출
|
|
# 사용: itsm_callback.sh <STATUS> [MESSAGE]
|
|
# STATUS: BUILDING|TESTING|DEPLOYING|COMPLETED|FAILED|UNSTABLE|PENDING_APPROVAL|ROLLED_BACK
|
|
# =============================================================================
|
|
set -euo pipefail
|
|
|
|
STATUS="${1:-FAILED}"
|
|
MESSAGE="${2:-파이프라인 실행 완료}"
|
|
|
|
# ── 환경변수 ─────────────────────────────────────────────────────────────────
|
|
ITSM_URL="${ITSM_URL:-http://itsm.agency.go.kr:8000}"
|
|
ITSM_CALLBACK_URL="${ITSM_CALLBACK_URL:-${ITSM_URL}/api/vibe/callback}"
|
|
ITSM_TOKEN="${ITSM_TOKEN:-}" # withCredentials로 주입
|
|
ITSM_SESSION_ID="${ITSM_SESSION_ID:-}" # Jenkins 파라미터
|
|
ITSM_SR_ID="${ITSM_SR_ID:-}" # Jenkins 파라미터
|
|
BUILD_NUMBER="${BUILD_NUMBER:-0}"
|
|
BUILD_URL="${BUILD_URL:-}"
|
|
DEPLOY_ENV="${DEPLOY_ENV:-dev}"
|
|
JOB_NAME="${JOB_NAME:-unknown-job}"
|
|
STAGE_NAME="${STAGE_NAME:-}"
|
|
LOG_DIR="${WORKSPACE:-/tmp}/build-logs"
|
|
CALLBACK_LOG="${LOG_DIR}/itsm-callback.log"
|
|
|
|
GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'; NC='\033[0m'
|
|
log() { echo -e "${GREEN}[ITSM]${NC} $*" | tee -a "${CALLBACK_LOG}"; }
|
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*" | tee -a "${CALLBACK_LOG}"; }
|
|
err() { echo -e "${RED}[ERROR]${NC} $*" | tee -a "${CALLBACK_LOG}"; }
|
|
|
|
mkdir -p "${LOG_DIR}"
|
|
|
|
# ── 세션 ID 없으면 경고 후 종료 (비배포 브랜치 등) ─────────────────────────
|
|
if [[ -z "${ITSM_SESSION_ID}" ]]; then
|
|
warn "ITSM_SESSION_ID가 설정되지 않았습니다 — 콜백 건너뜀"
|
|
exit 0
|
|
fi
|
|
|
|
log "ITSM 콜백: ${STATUS} — ${MESSAGE}"
|
|
|
|
# ── 빌드 로그 요약 수집 ──────────────────────────────────────────────────────
|
|
get_log_summary() {
|
|
local log_type="$1"
|
|
local log_file="${LOG_DIR}/${log_type}-"*".log" 2>/dev/null
|
|
if compgen -G "${log_file}" > /dev/null; then
|
|
tail -n 5 "$(ls ${log_file} | tail -1)" 2>/dev/null | tr '\n' ' ' | \
|
|
sed 's/["\t]/ /g' | cut -c1-200 || echo ""
|
|
else
|
|
echo ""
|
|
fi
|
|
}
|
|
|
|
BUILD_LOG_SUMMARY=$(get_log_summary "build")
|
|
TEST_LOG_SUMMARY=$(get_log_summary "test")
|
|
DEPLOY_LOG_SUMMARY=$(get_log_summary "deploy")
|
|
HC_LOG_SUMMARY=$(get_log_summary "healthcheck")
|
|
|
|
# ── 현재 타임스탬프 ──────────────────────────────────────────────────────────
|
|
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
|
|
# ── JSON 페이로드 구성 ───────────────────────────────────────────────────────
|
|
JSON_PAYLOAD=$(cat <<JSONEOF
|
|
{
|
|
"session_id": "${ITSM_SESSION_ID}",
|
|
"sr_id": "${ITSM_SR_ID}",
|
|
"status": "${STATUS}",
|
|
"stage": "${STAGE_NAME}",
|
|
"message": "${MESSAGE}",
|
|
"build_number": "${BUILD_NUMBER}",
|
|
"build_url": "${BUILD_URL}",
|
|
"deploy_env": "${DEPLOY_ENV}",
|
|
"job_name": "${JOB_NAME}",
|
|
"timestamp": "${TIMESTAMP}",
|
|
"logs": {
|
|
"build": "${BUILD_LOG_SUMMARY}",
|
|
"test": "${TEST_LOG_SUMMARY}",
|
|
"deploy": "${DEPLOY_LOG_SUMMARY}",
|
|
"health_check": "${HC_LOG_SUMMARY}"
|
|
}
|
|
}
|
|
JSONEOF
|
|
)
|
|
|
|
# ── HTTP 콜백 전송 (재시도 3회) ──────────────────────────────────────────────
|
|
send_callback() {
|
|
local retries=3
|
|
local attempt=0
|
|
|
|
while [[ ${attempt} -lt ${retries} ]]; do
|
|
attempt=$((attempt + 1))
|
|
log " 콜백 전송 시도 ${attempt}/${retries}: ${ITSM_CALLBACK_URL}"
|
|
|
|
local http_code
|
|
http_code=$(curl \
|
|
--silent \
|
|
--output "${LOG_DIR}/callback-response.json" \
|
|
--write-out "%{http_code}" \
|
|
--max-time 30 \
|
|
--connect-timeout 10 \
|
|
--request POST \
|
|
--header "Content-Type: application/json" \
|
|
--header "Authorization: Bearer ${ITSM_TOKEN}" \
|
|
--data "${JSON_PAYLOAD}" \
|
|
"${ITSM_CALLBACK_URL}" 2>/dev/null || echo "000")
|
|
|
|
if [[ "${http_code}" =~ ^2[0-9]{2}$ ]]; then
|
|
log "✅ 콜백 성공: HTTP ${http_code}"
|
|
if [[ -f "${LOG_DIR}/callback-response.json" ]]; then
|
|
log " 응답: $(cat ${LOG_DIR}/callback-response.json)"
|
|
fi
|
|
return 0
|
|
else
|
|
warn " 콜백 실패: HTTP ${http_code}"
|
|
if [[ ${attempt} -lt ${retries} ]]; then
|
|
sleep $((attempt * 5))
|
|
fi
|
|
fi
|
|
done
|
|
|
|
err "ITSM 콜백 실패 (${retries}회 재시도 후)"
|
|
err "URL: ${ITSM_CALLBACK_URL}"
|
|
err "상태: ${STATUS}"
|
|
# 콜백 실패는 파이프라인 실패로 처리하지 않음
|
|
return 0
|
|
}
|
|
|
|
# ── 로컬 로그에도 기록 ────────────────────────────────────────────────────────
|
|
log_locally() {
|
|
local history_file="${LOG_DIR}/pipeline-history.log"
|
|
echo "${TIMESTAMP}|${JOB_NAME}|${BUILD_NUMBER}|${STATUS}|${MESSAGE}|${DEPLOY_ENV}" \
|
|
>> "${history_file}"
|
|
}
|
|
|
|
# ── 메인 ─────────────────────────────────────────────────────────────────────
|
|
main() {
|
|
log_locally
|
|
send_callback
|
|
log "ITSM 콜백 처리 완료: ${STATUS}"
|
|
}
|
|
|
|
main "$@"
|