Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions blocks/slot_decide.sh
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ parse_slot() {
case "$PARSED_PORT" in
*[!0-9]*) echo "$arg_name port must be numeric (got: $value)" >&2; exit 2;;
esac
# 출력이 eval 로 소비되는 계약이라 이름 charset 을 허용목록으로 닫는다 - 지금 호출부는 고정
# 리터럴(blue/green)이지만, 훗날 슬롯 이름이 외부 입력을 타는 호출부가 생겨도 eval 이
# 명령 주입 표면이 되지 않게 블록이 스스로 막는다.
case "$PARSED_NAME" in
*[!a-zA-Z0-9_-]*) echo "$arg_name name must match [A-Za-z0-9_-] (got: $value)" >&2; exit 2;;
esac
}

parse_slot "--slot-a" "$SLOT_A"
Expand Down
2 changes: 2 additions & 0 deletions blocks/slot_decide.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ check "알 수 없는 인자 -> exit 2" 2 "$?"
check "콜론 없는 슬롯 -> exit 2" 2 "$?"
"$DECIDE" --state-file "$WORKDIR/up.conf" --slot-a blue:1809x --slot-b green:18091 >/dev/null 2>&1
check "비숫자 포트 -> exit 2" 2 "$?"
"$DECIDE" --state-file "$WORKDIR/up.conf" --slot-a 'bl;ue:18090' --slot-b green:18091 >/dev/null 2>&1
check "허용목록 밖 슬롯 이름(eval 주입 표면) -> exit 2" 2 "$?"

# 4. 부트스트랩: 상태 파일 없음 -> INACTIVE = slot-a
OUT=$("$DECIDE" --state-file "$WORKDIR/none.conf" --slot-a blue:18090 --slot-b green:18091)
Expand Down
8 changes: 7 additions & 1 deletion blocks/slot_switch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,13 @@ restore() {
return 0
fi
printf '%s\n' "$PREV" | run_priv tee "$STATE_FILE" >/dev/null
run_priv systemctl reload nginx 2>/dev/null || true
# 원복 반영도 전환과 같은 폴백(reload -> restart)을 쓴다. reload 만 두고 실패를 삼키면
# 상태 파일은 이전 슬롯인데 nginx 런타임은 새 슬롯을 계속 가리키는 창이 남고, 그 사이
# 호출자가 새 슬롯 컨테이너를 정리하면 서빙이 끊긴다. restart 폴백이면 그 창이 사라진다:
# restart 마저 실패 = nginx 정지이므로, 수동 기동만으로 상태 파일(이전 슬롯) 그대로 복귀한다.
if ! { run_priv systemctl reload nginx 2>/dev/null || run_priv systemctl restart nginx 2>/dev/null; }; then
echo "restore reload/restart FAILED - nginx 가 정지 상태일 수 있다. 상태 파일은 이전 슬롯으로 원복돼 있어 nginx 기동만으로 복귀된다" >&2
fi
echo "switch FAILED at $stage - restored previous upstream" >&2
}

Expand Down
14 changes: 13 additions & 1 deletion blocks/slot_switch.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ cat >"$WORKDIR/bin/systemctl" <<'EOF'
#!/usr/bin/env bash
echo "$*" >>"$FAKE_DIR/systemctl.log"
[ -f "$FAKE_DIR/systemctl_fail" ] && exit 1
[ "$1" = "reload" ] && [ -f "$FAKE_DIR/systemctl_reload_fail" ] && exit 1
exit 0
EOF
chmod +x "$WORKDIR/bin/sudo" "$WORKDIR/bin/nginx" "$WORKDIR/bin/systemctl"
Expand All @@ -53,7 +54,8 @@ check() {
}

reset_fakes() {
rm -f "$FAKE_DIR/nginx_t_fail" "$FAKE_DIR/systemctl_fail" "$FAKE_DIR/systemctl.log"
rm -f "$FAKE_DIR/nginx_t_fail" "$FAKE_DIR/systemctl_fail" "$FAKE_DIR/systemctl_reload_fail" \
"$FAKE_DIR/systemctl.log"
}

# 1. 필수 인자 누락 / 알 수 없는 인자
Expand Down Expand Up @@ -97,6 +99,16 @@ check "verify 실패 -> exit 1" 1 "$?"
check "verify 실패 -> 상태 파일 원복" "server 127.0.0.1:18090;" "$(cat "$STATE")"
check "verify 실패 -> reload 2회(전환+원복)" 2 "$(grep -c "reload nginx" "$FAKE_DIR/systemctl.log")"

# 5b. verify 실패 + 원복 reload 실패 -> restart 폴백으로 원복 반영 (상태·런타임 괴리 창 차단)
reset_fakes
echo "server 127.0.0.1:18090;" >"$STATE"
touch "$FAKE_DIR/systemctl_reload_fail"
"$SWITCH" --state-file "$STATE" --server 127.0.0.1:18091 --verify-cmd "false" >/dev/null 2>&1
check "verify 실패 + reload 불가 -> exit 1" 1 "$?"
check "verify 실패 + reload 불가 -> 상태 파일 원복" "server 127.0.0.1:18090;" "$(cat "$STATE")"
# 전환(reload 실패 -> restart)과 원복(reload 실패 -> restart)에서 각각 restart 가 불렸다
check "verify 실패 + reload 불가 -> restart 폴백 2회(전환+원복)" 2 "$(grep -c "restart nginx" "$FAKE_DIR/systemctl.log")"

# 6. verify 성공 -> 전환 유지
reset_fakes
echo "server 127.0.0.1:18090;" >"$STATE"
Expand Down
Loading