feat: yakcloud domain check + 승격/init 도메인 사전검사 (v0.30.0)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
jungwoo choi
2026-08-31 17:39:12 +09:00
parent 6ffc6425bb
commit 68bedebb1c

View File

@ -16,7 +16,7 @@ set -uo pipefail # -e 미사용: 'test && action' 관용구가 값 없을 때
REPO="${YAKCLOUD_STARTER_REPO:-https://gitea.yakenator.io/yakenator/yakcloud-starter}"
BRANCH="${YAKCLOUD_STARTER_BRANCH:-main}"
VERSION="0.29.0"
VERSION="0.30.0"
# 로컬 개발 클러스터(kind) — 개발 후 운영까지 '직선 배포'의 dev 구간.
DEV_CTX="${YAK_DEV_CONTEXT:-kind-yak-dev}"
DEV_CLUSTER="${YAK_DEV_CLUSTER:-yak-dev}"
@ -263,6 +263,14 @@ _init_wizard() {
printf "검증 클러스터(관리형, 선택, 엔터=생략): "; read -r valc
printf "운영 클러스터(관리형, 선택, 엔터=생략): "; read -r prodc
printf "운영 도메인(선택, 예 app.example.com): "; read -r pdom
# 도메인 사전검사(로그인돼 있으면) — 예약·중복이면 지금 경고(빌드·배포 다 하고 나서 발견 방지).
if [ -n "$pdom" ]; then
local _dc; _dc="$(_domain_check "$pdom")"
case "$_dc" in
X*) echo " ⚠ '$pdom' 지금은 사용 불가 — ${_dc#X$'\t'}. 다른 도메인을 쓰거나 배포 전 'yakcloud domain check' 로 확인하세요." ;;
OK) echo " ✓ '$pdom' 사용 가능" ;;
esac
fi
printf "컨테이너 포트 [8080]: "; read -r port; port="${port:-8080}"
printf "헬스체크 경로 [/healthz]: "; read -r health; health="${health:-/healthz}"
printf "CPU 요청 [25m]: "; read -r cpu; cpu="${cpu:-25m}"
@ -310,6 +318,56 @@ if v is not None: print(v)
PY
}
# 도메인 사용 가능 사전검사(부작용 없음) — 등록과 같은 규칙(형식·존·예약·쿼터·중복).
# stdout: "OK" | "X<TAB>사유". 미로그인/네트워크 실패면 빈 출력(스킵 — 블록하지 않음).
_domain_check() {
local fqdn="$1" out
[ -n "${YAKCLOUD_URL:-}" ] && [ -n "${YAKCLOUD_TOKEN:-}" ] || return 0
out="$(curl -fsS -H "Authorization: Bearer $YAKCLOUD_TOKEN" --max-time 15 "$YAKCLOUD_URL/api/v1/domains/check?fqdn=$fqdn" 2>/dev/null)" || return 0
printf '%s' "$out" | python3 -c "import sys,json;d=json.load(sys.stdin);d=d.get('data',d);print('OK' if d.get('available') else 'X\t'+(d.get('reason') or '사용 불가'))" 2>/dev/null
}
# 매니페스트 environments.<env>.domains 목록(줄바꿈 구분).
_env_domains() {
ensure_pyyaml >/dev/null 2>&1 || true
python3 - "$1" <<'PY' 2>/dev/null
import sys, yaml
try: m = yaml.safe_load(open("yakcloud.yaml")) or {}
except Exception: sys.exit(0)
e = ((m.get("environments") or {}).get(sys.argv[1]) or {})
for d in (e.get("domains") or []):
if d: print(d)
PY
}
# 환경 도메인 전부 사전검사. 사용 불가가 하나라도 있으면 1(비어있거나 전부 OK/스킵이면 0).
_precheck_env_domains() {
local bad=0 d r
while IFS= read -r d; do
[ -n "$d" ] || continue
r="$(_domain_check "$d")"
case "$r" in
X*) echo " ✗ 도메인 '$d' 사용 불가 — ${r#X$'\t'}"; bad=1 ;;
OK) echo " ✓ 도메인 '$d' 사용 가능" ;;
esac
done <<EOF
$(_env_domains "$1")
EOF
return $bad
}
# yakcloud domain check <fqdn> — 단독 사전검사(등록 안 함).
cmd_domain_check() {
local fqdn="$1"; [ -n "$fqdn" ] || die "사용법: yakcloud domain check <fqdn>"
: "${YAKCLOUD_URL:?YAKCLOUD_URL 필요 — 'yakcloud login'}"; : "${YAKCLOUD_TOKEN:?YAKCLOUD_TOKEN 필요 — 'yakcloud login'}"
local r; r="$(_domain_check "$fqdn")"
case "$r" in
OK) echo "✓ '$fqdn' 사용 가능 — 등록/노출할 수 있습니다." ;;
X*) printf "✗ '%s' 사용 불가 — %s\n" "$fqdn" "${r#X$'\t'}"; return 1 ;;
*) die "확인 실패 — 네트워크/로그인 상태 확인('yakcloud login')" ;;
esac
}
# 로컬 kind 클러스터(yak-<name>) 존재 여부.
_dev_cluster_exists() {
local n="$1" rt kenv=""; rt="$(_dev_runtime)"; [ "$rt" = podman ] && kenv="KIND_EXPERIMENTAL_PROVIDER=podman"
@ -385,6 +443,8 @@ cmd_project_check() {
return
fi
: "${YAKCLOUD_URL:?YAKCLOUD_URL 필요}"; : "${YAKCLOUD_TOKEN:?YAKCLOUD_TOKEN 필요}"
echo "도메인 사전검사 ($env):"
_precheck_env_domains "$env" || echo " ⚠ 위 도메인은 지금 상태로 승격 시 실패합니다 — 배포 전에 해결하세요."
YAKCLOUD_ENV="$env" python3 "$(_engine deploy)" yakcloud.yaml --dry-run
}
@ -466,6 +526,10 @@ cmd_project_promote() {
[ -n "$tag" ] || tag="v0.1.0"
fi
case "$tag" in v*) : ;; *) tag="v$tag" ;; esac
# 도메인 사전검사 — 예약·중복 등으로 못 쓰는 도메인이면 build/push 前에 빠르게 실패(kanban 사태 방지).
if ! _precheck_env_domains "$env"; then
die "도메인 사전검사 실패 — 위 도메인을 해결(예약 해제·다른 이름·소유자 확인)한 뒤 다시 승격하세요. 이미지 빌드/푸시 안 함."
fi
# 운영 승격 게이트 — prod 는 개발계 검증 확정(그 버전)이 선행돼야 한다(val 은 자유).
[ "$env" = prod ] && _require_dev_confirm "$tag"
info "'$env' 승격: $tag → 관리형 '$env' 클러스터(이미지 build+push 후 리컨실; 이미 있으면 재빌드 없이 그 이미지)"
@ -910,6 +974,7 @@ yakcloud $VERSION — YakCloud 프로젝트 CLI (개발·검증·운영 모두
config 저장된 설정 표시(비밀 마스킹)
logout 저장 자격 삭제
── 배포환경 설정(앱별) ──
domain check <fqdn> 도메인 사용 가능 사전검사(등록 안 함) — 예약·중복·형식 확인. 도메인 정하기 前 권장
domain <fqdn> [wl] 도메인 등록 + 워크로드 할당(관리형=즉시/배포중이면 라이브)
scale <wl> <n> replicas 변경
set <wl> --image/--port/--health/--cpu/--mem/--path/--rewrite
@ -942,7 +1007,9 @@ case "${1:-help}" in
config) cmd_config ;;
logout) cmd_logout ;;
datasource|ds) shift; ctl source "$@" ;;
domain|scale|set|env|source|bind|unbind) ctl "$@" ;;
domain)
if [ "${2:-}" = check ]; then shift 2; cmd_domain_check "${1:-}"; else ctl "$@"; fi ;;
scale|set|env|source|bind|unbind) ctl "$@" ;;
upgrade|self-update) cmd_upgrade ;;
version|-v|--version) echo "yakcloud $VERSION" ;;
help|-h|--help|"") usage ;;