62 lines
4.8 KiB
Bash
62 lines
4.8 KiB
Bash
# Общие настройки и функции loopctl. Подключается через source из скриптов.
|
||
# Настройки: config.env в корне репо (не в git, пример: config.env.example). Секретов в нём нет.
|
||
|
||
LOOPOPS_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
||
[ -f "$LOOPOPS_DIR/config.env" ] && . "$LOOPOPS_DIR/config.env"
|
||
|
||
export KUBECONFIG=${LOOPOPS_KUBECONFIG:?задай LOOPOPS_KUBECONFIG в config.env}
|
||
VIZOR_KUBECONFIG=${VIZOR_KUBECONFIG:?задай VIZOR_KUBECONFIG в config.env}
|
||
VIZOR_API=${VIZOR_API:-https://vizor.loop.ru/api/loop/instances}
|
||
VIZOR_DB_DSN=${VIZOR_DB_DSN:-postgresql://loopvizordbuser@master.e1cc6255-8ae4-41cb-9eb5-525cf24e50eb.c.dbaas.selcloud.ru:6432/loopvizor?sslmode=require}
|
||
DST=${DST:-loop-3} # куб, куда переносим
|
||
DST_DNS=${DST_DNS:-l${DST#loop-}.clusters.loop.ru} # loop-3 -> l3.clusters.loop.ru
|
||
SOURCES=${SOURCES:-loop-2 loop-1} # где искать CR при переносе
|
||
DNS_ENV=${DNS_ENV:-$HOME/.selectel-dns.env}
|
||
STATE=$LOOPOPS_DIR/state # прокси, логи, cleanup-список (не в git)
|
||
mkdir -p "$STATE"
|
||
|
||
dst_lb() { dig +short "$DST_DNS" | tail -1; } # IP балансировщика целевого куба
|
||
|
||
confirm() { # confirm "что сделаем" — без LOOPCTL_YES=1 спрашивает
|
||
[ "${LOOPCTL_YES:-0}" = 1 ] && return 0
|
||
read -r -p "$1 Продолжить? [y/N] " a </dev/tty; [ "$a" = y ]
|
||
}
|
||
|
||
vsql() { # SQL со stdin -> psql -c во временном поде в loop-support, пароль из секрета визора
|
||
local sql name=vizor-sql-$(sh -c 'echo $PPID')-$RANDOM # PID копии: у параллельных копий $RANDOM одинаковый
|
||
sql=$(cat | sed 's/\$/$$/g') # k8s в command превращает $$ в $
|
||
KUBECONFIG=$VIZOR_KUBECONFIG kubectl -n loop-support run "$name" --rm -i --quiet --restart=Never --image=postgres:15-alpine \
|
||
--overrides="$(jq -nc --arg n "$name" --arg sql "$sql" --arg dsn "$VIZOR_DB_DSN" '{spec:{containers:[{name:$n,image:"postgres:15-alpine",stdin:true,env:[{name:"PGPASSWORD",valueFrom:{secretKeyRef:{name:"loop-vizor-production-secrets",key:"SPRING_DATASOURCE_PASSWORD"}}}],command:["psql",$dsn,"-v","ON_ERROR_STOP=1","-qAt","-F","|","-c",$sql]}]}}')" \
|
||
2> >(grep -v -E "couldn't attach|recorded in container|command prompt" >&2) </dev/null
|
||
}
|
||
|
||
# --- API визора. Токен Keycloak живёт 10 минут, обновляется refresh-токеном (~/.vizor-refresh).
|
||
# Дать токен: DevTools на vizor.loop.ru -> Network -> запрос kc.wilix.dev/.../token -> Response -> скопировать JSON в буфер.
|
||
TOKEN=$(tr -d ' \n\r' < ~/.vizor-token 2>/dev/null || true); TOKEN=${TOKEN#Bearer}
|
||
KC=https://kc.wilix.dev/auth/realms/loop/protocol/openid-connect/token
|
||
token_ok() { # минут до истечения токена
|
||
local p; p=$(cut -d. -f2 <<<"$TOKEN" | tr '_-' '/+'); while [ $((${#p} % 4)) -ne 0 ]; do p="$p="; done
|
||
local exp; exp=$(base64 -d <<<"$p" 2>/dev/null | jq -r ".exp // 0" 2>/dev/null); echo $(( (${exp:-0} - $(date +%s)) / 60 ))
|
||
}
|
||
refresh_token() {
|
||
local clip RT NT
|
||
clip=$(pbpaste 2>/dev/null || true)
|
||
RT=$(grep -o '"refresh_token": *"[^"]*"' <<<"$clip" | head -1 | cut -d'"' -f4 || true)
|
||
[ -n "$RT" ] && (umask 077; printf '%s' "$RT" > ~/.vizor-refresh)
|
||
if [ -s ~/.vizor-refresh ]; then
|
||
local R; R=$(curl -s "$KC" -d grant_type=refresh_token -d client_id=frontend --data-urlencode "refresh_token=$(cat ~/.vizor-refresh)")
|
||
NT=$(jq -r '.access_token // empty' <<<"$R" 2>/dev/null)
|
||
RT=$(jq -r '.refresh_token // empty' <<<"$R" 2>/dev/null); [ -n "$RT" ] && (umask 077; printf '%s' "$RT" > ~/.vizor-refresh)
|
||
fi
|
||
[ -n "${NT:-}" ] || NT=$(grep -o 'Bearer [A-Za-z0-9._-]*' <<<"$clip" | head -1 | cut -d' ' -f2 || true)
|
||
[ -n "${NT:-}" ] && { (umask 077; printf '%s' "$NT" > ~/.vizor-token); TOKEN=$NT; }
|
||
[ "$(token_ok)" -ge 5 ] || { echo "нет свежего токена визора. Скопируй в DevTools ответ (Response) запроса kc.wilix.dev/.../token с refresh_token и запусти снова"; return 1; }
|
||
echo "-- токен визора обновлён, осталось $(token_ok) мин"
|
||
}
|
||
ensure_token() { [ "$(token_ok)" -ge "${1:-5}" ] || refresh_token; }
|
||
vapi() { curl -sS -f -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' "$@"; }
|
||
vizor_id() { printf "select id from loop_instance where id like '%%%s' and deleted_at is null;\n" "${1#loop}" | vsql; }
|
||
|
||
# ping с проверкой БД и файлов, в обход DNS: ping_db <домен> <ip>
|
||
ping_db() { curl -s -m 10 --resolve "$1:443:$2" "https://$1/api/v4/system/ping?get_server_status=true" | jq -r '"\(.status)/\(.database_status)/\(.filestore_status)"' 2>/dev/null; }
|