blue-green 슬롯 판정·전환 공용 블록 신설 - #38
Conversation
- core·extractor 에 복제된 blue-green 오케스트레이션에서 범용 골격 둘을 블록으로 추출: 상태 파일 기반 슬롯 판정(slot_decide)과 실패 시 원복하는 upstream 전환(slot_switch). 복제 구현에서 실증된 두 사고가 동인: 포트 추출이 host 의 127 을 먼저 무는 함정, 전환 실패 시 상태·현실 괴리로 다음 배포가 서빙 슬롯을 오제거하는 결함(#37) - 블록 원칙 준수: 포트·이름·URL 은 default 없는 필수 인자(값 무소유), 순수 bash(실행 위치 중립), sudo 는 비루트일 때만 - decide 출력은 eval 가능한 한 줄 - 소비는 할당 후 eval 2단계로 문서화 (eval "$(...)" 직결은 치환 실패가 성공으로 위장) - switch 의 verify-cmd 까지 원복 경계 안에 둔다 - 전환 후 검증 실패도 이전 슬롯으로 복귀 - 셀프 테스트 28케이스(판정 11 + 전환 17, PATH 스텁으로 nginx·systemctl 대체) + CI 등록
📝 WalkthroughWalkthroughChangesBlue-green 슬롯 관리
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related issues
Possibly related PRs
Sequence Diagram(s)sequenceDiagram
participant slot_switch.sh
participant StateFile
participant nginx
participant systemctl
slot_switch.sh->>StateFile: 새 upstream 상태 기록
slot_switch.sh->>nginx: nginx -t 실행
nginx-->>slot_switch.sh: 설정 검증 결과 반환
slot_switch.sh->>systemctl: nginx reload 요청
systemctl-->>slot_switch.sh: reload 결과 반환
slot_switch.sh->>slot_switch.sh: 선택적 검증 명령 실행
slot_switch.sh->>StateFile: 실패 시 이전 상태 복원
slot_switch.sh->>systemctl: 복원 후 nginx reload 요청
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@blocks/slot_decide.sh`:
- Around line 38-40: 통합된 옵션 인자 검사를 blocks/slot_decide.sh 38-40과
blocks/slot_switch.sh 38-40의 각 옵션 처리부에 추가하십시오. `shift 2` 전에 필요한 값이 실제로 존재하는지
확인하고, 없으면 오류 메시지를 출력한 뒤 반드시 종료 코드 2로 종료하게 하며, blocks/slot_switch.sh의
`--verify-cmd`가 마지막 인자인 경우도 동일하게 처리하십시오.
- Around line 52-60: Update parse_slot and the output path that serializes slot
names so values consumed by eval in blocks/slot_decide.test.sh are Bash-safe,
preferably by encoding each emitted value with printf '%q' while preserving
valid names and ports. Add an eval regression case covering malicious slot names
containing spaces, semicolons, or command-substitution characters, and verify
they are not executed.
- Around line 63-66: After parsing both slots in the slot-decision flow,
validate that NAME_A differs from NAME_B and PORT_A differs from PORT_B; when
either value is duplicated, print an appropriate error and exit with status 2.
Add regression coverage for both identical names and identical ports, while
preserving valid distinct-slot behavior.
In `@blocks/slot_switch.sh`:
- Around line 61-63: Update the verify-failure rollback path in slot_switch.sh
so writing PREV to STATE_FILE is followed by nginx -t and a reload, with a
restart fallback when reload fails. Track rollback application success, emit a
separate error when restoration fails, and only print “restored previous
upstream” after nginx successfully applies it; add coverage in the rollback
tests for reload failure, success-log suppression, and the actual nginx state.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 85259b8e-af2b-4bd0-a5d8-f175ed803013
📒 Files selected for processing (5)
.github/workflows/ci.ymlblocks/slot_decide.shblocks/slot_decide.test.shblocks/slot_switch.shblocks/slot_switch.test.sh
| --state-file) STATE_FILE="${2:-}"; shift 2;; | ||
| --slot-a) SLOT_A="${2:-}"; shift 2;; | ||
| --slot-b) SLOT_B="${2:-}"; shift 2;; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
값이 없는 옵션의 종료 코드 계약을 두 스크립트에서 통일하십시오.
두 스크립트는 옵션 값이 없을 때 shift 2를 실행합니다. 이 동작은 set -e로 인해 문서화한 인자 오류 코드 2를 보장하지 않습니다.
blocks/slot_decide.sh#L38-L40: 각 옵션에서shift 2전에 인자 개수를 확인하고, 값이 없으면 오류 메시지와 함께exit 2하십시오.blocks/slot_switch.sh#L38-L40: 동일한 검사를 적용하고,--verify-cmd가 마지막 인자인 경우도 종료 코드 2로 처리하십시오.
📍 Affects 2 files
blocks/slot_decide.sh#L38-L40(this comment)blocks/slot_switch.sh#L38-L40
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@blocks/slot_decide.sh` around lines 38 - 40, 통합된 옵션 인자 검사를
blocks/slot_decide.sh 38-40과 blocks/slot_switch.sh 38-40의 각 옵션 처리부에 추가하십시오.
`shift 2` 전에 필요한 값이 실제로 존재하는지 확인하고, 없으면 오류 메시지를 출력한 뒤 반드시 종료 코드 2로 종료하게 하며,
blocks/slot_switch.sh의 `--verify-cmd`가 마지막 인자인 경우도 동일하게 처리하십시오.
| PARSED_NAME="${value%%:*}" | ||
| PARSED_PORT="${value##*:}" | ||
| if [ "$value" = "$PARSED_NAME" ] || [ -z "$PARSED_NAME" ] || [ -z "$PARSED_PORT" ]; then | ||
| echo "$arg_name must be NAME:PORT (got: $value)" >&2 | ||
| exit 2 | ||
| fi | ||
| case "$PARSED_PORT" in | ||
| *[!0-9]*) echo "$arg_name port must be numeric (got: $value)" >&2; exit 2;; | ||
| esac |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
eval 출력에 슬롯 이름을 안전하게 직렬화하십시오.
parse_slot은 슬롯 이름의 공백, 세미콜론, 명령 치환 문자를 허용합니다. Line 74~78은 그 값을 그대로 출력하고, blocks/slot_decide.test.sh Line 70은 출력을 eval합니다. 예를 들어 조작된 --slot-a 이름은 소비자 셸에서 명령으로 재해석될 수 있습니다.
모든 출력 값을 Bash 안전 형식으로 인코딩하십시오. 예를 들어 printf '%q'를 사용하십시오. 또는 슬롯 이름을 안전한 문자 집합으로 제한하십시오. 이 경우 eval 회귀 테스트에 악성 문자 입력도 추가하십시오.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@blocks/slot_decide.sh` around lines 52 - 60, Update parse_slot and the output
path that serializes slot names so values consumed by eval in
blocks/slot_decide.test.sh are Bash-safe, preferably by encoding each emitted
value with printf '%q' while preserving valid names and ports. Add an eval
regression case covering malicious slot names containing spaces, semicolons, or
command-substitution characters, and verify they are not executed.
Source: Linters/SAST tools
| parse_slot "--slot-a" "$SLOT_A" | ||
| NAME_A="$PARSED_NAME" PORT_A="$PARSED_PORT" | ||
| parse_slot "--slot-b" "$SLOT_B" | ||
| NAME_B="$PARSED_NAME" PORT_B="$PARSED_PORT" |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
두 슬롯의 이름과 포트가 서로 다르도록 검증하십시오.
--slot-a blue:18090 --slot-b green:18090이 현재 통과합니다. 이 상태에서 INACTIVE_PORT도 활성 포트인 18090이 됩니다. 배포 대상이 현재 서빙 슬롯과 분리되지 않습니다.
NAME_A != NAME_B 및 PORT_A != PORT_B를 확인하고, 중복이면 종료 코드 2를 반환하십시오. 동일 포트와 동일 이름의 회귀 테스트도 추가하십시오.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@blocks/slot_decide.sh` around lines 63 - 66, After parsing both slots in the
slot-decision flow, validate that NAME_A differs from NAME_B and PORT_A differs
from PORT_B; when either value is duplicated, print an appropriate error and
exit with status 2. Add regression coverage for both identical names and
identical ports, while preserving valid distinct-slot behavior.
| printf '%s\n' "$PREV" | run_priv tee "$STATE_FILE" >/dev/null | ||
| run_priv systemctl reload nginx 2>/dev/null || true | ||
| echo "switch FAILED at $stage - restored previous upstream" >&2 |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
nginx 원복 실패를 성공으로 처리하지 마십시오.
verify 실패 경로에서는 새 upstream이 이미 Line 72에서 적용되었습니다. 이후 Line 62의 reload가 실패하면 상태 파일만 이전 값으로 돌아가고 nginx는 새 upstream을 계속 사용할 수 있습니다. 그러나 현재 코드는 실패를 무시하고 "restored previous upstream"을 출력합니다.
원복 상태 파일을 쓴 뒤 nginx -t와 reload, 필요 시 restart를 수행하십시오. 원복 적용이 실패하면 별도 오류를 출력하고 성공 복원 로그를 출력하지 마십시오. blocks/slot_switch.test.sh에는 원복 reload 실패 시 성공 로그와 실제 적용 상태를 검증하는 케이스를 추가하십시오.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@blocks/slot_switch.sh` around lines 61 - 63, Update the verify-failure
rollback path in slot_switch.sh so writing PREV to STATE_FILE is followed by
nginx -t and a reload, with a restart fallback when reload fails. Track rollback
application success, emit a separate error when restoration fails, and only
print “restored previous upstream” after nginx successfully applies it; add
coverage in the rollback tests for reload failure, success-log suppression, and
the actual nginx state.
- 인라인 구현을 TeamPiKi/infra#38 의 블록 호출로 대체 (-56/+25줄). 판정·원복 로직의 정본이 infra 로 옮겨져 core 와 공유된다 (infra#37 3단계) - 값(슬롯 이름·포트·상태 파일·검증 명령)은 종전대로 이 호출부가 소유 - 블록 무값 원칙 - 이행 1회차 전용이던 레거시 단일 컨테이너 분기 제거 (2026-08-10 이행 완료로 도달 불가) - 부트스트랩(ACTIVE 빈 값) teardown 에 가드 추가 - 종전엔 빈 이름으로 no-op 호출이 나갔다 - 검증: 머지된 실제 블록으로 decide→eval→switch→verify(healthcheck 중첩 인용 포함) 로컬 통합 시뮬레이션 통과, yaml·bash -n·shellcheck 클린
Situation
server 127.0.0.1:18090;에서 무차별 숫자 grep 은127을 먼저 문다. core 는localhost표기라 우연히 무사했을 뿐 같은 코드가 잠복해 있다.Task
Action
slot_decide.shslot_switch.sh--slot-a blue:18090)·상태 파일 경로·검증 명령 전부 default 없는 필수/명시 인자다 (conventions/blocks.md 2번).eval "$(...)"직결은 치환 실패가 빈 문자열 eval(성공)로 위장되므로 "할당 후 eval" 2단계를 헤더에 문서화했다.Result
연관 이슈
Summary by CodeRabbit
새 기능
버그 수정
테스트