cli v0.12.1: 배포 이름 충돌 가드 — 같은 이름·다른 이미지 repo 면 덮어쓰기 거부(fail-fast). 백엔드 create_deployment 도 409 로 강제
This commit is contained in:
@ -14,7 +14,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.12.0"
|
||||
VERSION="0.12.1"
|
||||
CONFIG_DIR="${YAKCLOUD_CONFIG_DIR:-$HOME/.config/yakcloud}"
|
||||
CONFIG_FILE="$CONFIG_DIR/config"
|
||||
|
||||
|
||||
@ -153,6 +153,12 @@ def reconcile_domain(fqdn: str) -> None:
|
||||
log(f' 외부 도메인 — DNS 에 TXT 추가 후 검증: {v["host"]} TXT "{v["value"]}"')
|
||||
|
||||
|
||||
def _repo(img: str) -> str:
|
||||
"""이미지에서 :tag 제거한 repo(레지스트리 포트 host:5000/… 의 콜론은 보존)."""
|
||||
seg = img.rsplit("/", 1)[-1]
|
||||
return img.rsplit(":", 1)[0] if ":" in seg else img
|
||||
|
||||
|
||||
def deploy_workload(w: dict) -> tuple[str | None, bool]:
|
||||
image = w["image"].replace("${TAG}", TAG)
|
||||
ex = w.get("expose", {}) or {}
|
||||
@ -178,17 +184,26 @@ def deploy_workload(w: dict) -> tuple[str | None, bool]:
|
||||
env = [{"key": k, "value": v} for k, v in env.items()]
|
||||
body["env"] = [{"key": e["key"], "value": str(e.get("value", "")),
|
||||
"secret": bool(e.get("secret", False))} for e in env]
|
||||
# 도메인 리컨실 — 워크로드 host(들) + 환경(운영) 도메인을 레지스트리에 등록(없으면).
|
||||
# 충돌 가드 — 같은 이름의 기존 배포가 '다른 앱'(다른 이미지 repo)이면 덮어쓰기 거부(무조건 배포 금지).
|
||||
# 같은 repo(태그만 다름)=정상 재배포는 PATCH 로 진행. (백엔드 create_deployment 도 동일하게 409 로 강제.)
|
||||
existing = next((d for d in cluster_deployments() if d.get("name") == w["name"]), None)
|
||||
if existing:
|
||||
ex_repo = _repo(existing.get("image") or "")
|
||||
if ex_repo and ex_repo != _repo(image):
|
||||
raise SystemExit(
|
||||
f"배포명 '{w['name']}' 충돌 — 이 클러스터에 이미 다른 앱이 그 이름을 쓰고 있습니다.\n"
|
||||
f" 기존 image: {existing.get('image')}\n 내 image: {image}\n"
|
||||
f" → 워크로드 이름을 바꾸거나(yakcloud set 로 name 변경) 기존 배포를 먼저 정리하세요.")
|
||||
# 도메인 리컨실 — 워크로드 host(들) + 환경(운영) 도메인 등록(없으면). 충돌 통과 뒤에만.
|
||||
for h in custom:
|
||||
reconcile_domain(h)
|
||||
if DRY:
|
||||
log(f"deploy '{w['name']}' 예정: image={image} port={body['port']} path={body['path']} "
|
||||
note = "기존 동일 앱 → PATCH 롤링" if existing else "신규 생성"
|
||||
log(f"deploy '{w['name']}' 예정({note}): image={image} port={body['port']} path={body['path']} "
|
||||
f"replicas={body['replicasDesired']} hosts={body.get('exposeHosts') or body.get('exposeHost')}")
|
||||
return None, False
|
||||
# 멱등: 기존 배포면 PATCH(이미지 갱신 → kubectl apply = 무중단 롤링). 없으면 신규 생성.
|
||||
existing = next((d for d in cluster_deployments() if d.get("name") == w["name"]), None)
|
||||
if existing:
|
||||
log(f"deploy '{w['name']}' 기존 존재 → PATCH 롤링 업데이트 image={image}")
|
||||
log(f"deploy '{w['name']}' 기존(동일 앱) → PATCH 롤링 업데이트 image={image}")
|
||||
api("PATCH", f"/deployments/{existing['id']}", body)
|
||||
return existing["id"], False
|
||||
log(f"deploy '{w['name']}' 신규 생성 image={image}")
|
||||
|
||||
Reference in New Issue
Block a user