Files
loop-ops/lib/common.sh
T
2026-10-27 14:29:43 +03:00

62 lines
4.8 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Общие настройки и функции 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; }