diff --git a/.claude/skills/yakcloud-deploy/SKILL.md b/.claude/skills/yakcloud-deploy/SKILL.md index 265ac08..bf78280 100644 --- a/.claude/skills/yakcloud-deploy/SKILL.md +++ b/.claude/skills/yakcloud-deploy/SKILL.md @@ -32,6 +32,7 @@ YakCloud 콘솔 API로 **데이터소스 리컨실 + 워크로드 배포 + 바 - `yakcloud scale ` replicas - `yakcloud set --image/--port/--health/--cpu/--mem/--path/--rewrite` - `yakcloud env KEY=VAL … [--secret KEY] [--unset KEY]` +- `yakcloud datasource ls` (=`source ls`) 클러스터에서 이용 가능한 소스 목록(이름·타입·내부/외부·상태·플랜) - `yakcloud source add [plan]` · `yakcloud source rm ` - `yakcloud bind ` · `yakcloud unbind ` diff --git a/bin/yakcloud b/bin/yakcloud index a6b2f2b..33533ea 100755 --- a/bin/yakcloud +++ b/bin/yakcloud @@ -332,6 +332,7 @@ yakcloud $VERSION — YakCloud 프로젝트 CLI scale replicas 변경 set --image/--port/--health/--cpu/--mem/--path/--rewrite env KEY=VAL … [--secret KEY] [--unset KEY] + datasource ls 클러스터에서 이용 가능한 소스 목록(이름·타입·내부/외부·상태) source add [plan] | source rm bind | unbind env: YAKCLOUD_URL, YAKCLOUD_TOKEN(배포토큰 yakd_…), (선택) YAKCLOUD_CLUSTER @@ -354,6 +355,7 @@ case "${1:-help}" in login) cmd_login ;; config) cmd_config ;; logout) cmd_logout ;; + datasource|ds) shift; ctl source "$@" ;; domain|scale|set|env|source|bind|unbind) ctl "$@" ;; upgrade|self-update) cmd_upgrade ;; version|-v|--version) echo "yakcloud $VERSION" ;; diff --git a/scripts/yakcloud_ctl.py b/scripts/yakcloud_ctl.py index 32aead8..0a1f523 100644 --- a/scripts/yakcloud_ctl.py +++ b/scripts/yakcloud_ctl.py @@ -5,7 +5,7 @@ scale/set/env/domain 은 이미 배포돼 있으면 재빌드 없이 라이브 반영(PATCH)한다. env: YAKCLOUD_URL, YAKCLOUD_TOKEN(배포토큰 yakd_…), (선택) YAKCLOUD_CLUSTER -서브커맨드: info · domain · scale · set · env · source · bind · unbind +서브커맨드: info · domain · scale · set · env · source(ls/add/rm) · bind · unbind """ from __future__ import annotations @@ -240,6 +240,33 @@ def cmd_source(a) -> None: print(" ✓ requires 에서 제거: %s (관련 binds 정리). 'yakcloud deploy' 로 반영" % a.name) +def cmd_source_ls(a) -> None: + """클러스터에서 이용 가능한 데이터 소스 목록 — 이름·타입·내부/외부·상태·플랜.""" + need_api() + m = (yaml.safe_load(open(MANIFEST)) if os.path.exists(MANIFEST) else {}) or {} + c = resolve_cluster(cluster_ref(m)) + svcs = unwrap(api("GET", "/clusters/%s/services" % c["id"])) or [] + if not svcs: + print("클러스터 '%s' — 데이터 소스 없음. 콘솔 '데이터 소스' 또는 'yakcloud source add' 로 추가." + % (c.get("name") or c["id"])) + return + used = {r.get("name") for r in (m.get("requires", []) or [])} + rows = [] + for s in sorted(svcs, key=lambda x: (x.get("type", ""), x.get("name", ""))): + mode = s.get("mode") + loc = "내부" if mode == "LOCAL" else "외부" if mode == "REMOTE" else (mode or "?") + mark = "✓" if s.get("name") in used else " " + rows.append((mark, s.get("name", "?"), s.get("type", "?"), loc, + s.get("status", "?"), s.get("size") or s.get("plan") or "-")) + wn = max([len(r[1]) for r in rows] + [4]) + wt = max([len(r[2]) for r in rows] + [4]) + print("클러스터 '%s' 데이터 소스 (%d)" % (c.get("name") or c["id"], len(rows))) + print(" %-*s %-*s %-4s %-8s 플랜" % (wn, "이름", wt, "타입", "위치", "상태")) + for mark, name, typ, loc, st, plan in rows: + print(" %s %-*s %-*s %-4s %-8s %s" % (mark, wn, name, wt, typ, loc, st, plan)) + print("\n ✓=매니페스트 requires 에 이미 있음 · 바인딩: yakcloud bind <이름> ") + + def cmd_bind(a) -> None: m = load_manifest(); w = find_workload(m, a.workload) if not any(r.get("name") == a.source for r in (m.get("requires", []) or [])): @@ -326,8 +353,9 @@ def build_parser() -> argparse.ArgumentParser: e.add_argument("--unset", action="append", help="이 KEY 제거") e.set_defaults(fn=cmd_env) - so = sub.add_parser("source", help="데이터 소스(requires) 추가/삭제") + so = sub.add_parser("source", help="데이터 소스: ls(클러스터 목록) · add · rm") sosub = so.add_subparsers(dest="action", required=True) + sol = sosub.add_parser("ls", help="클러스터에서 이용 가능한 소스 목록"); sol.set_defaults(fn=cmd_source_ls) soa = sosub.add_parser("add"); soa.add_argument("name"); soa.add_argument("type") soa.add_argument("plan", nargs="?", default="small"); soa.set_defaults(fn=cmd_source) sor = sosub.add_parser("rm"); sor.add_argument("name"); sor.set_defaults(fn=cmd_source)