This commit is contained in:
2026-10-27 14:29:43 +03:00
commit 1769946c99
24 changed files with 3762 additions and 0 deletions
Executable
+87
View File
@@ -0,0 +1,87 @@
#!/bin/bash
# Первый шаг переноса инстанса на другой куб (DST, по умолчанию loop-3): секреты + перепривязка в визоре.
# loopctl k8s rebind loop12a40f770b5a
# Источник = куб из SOURCES, где есть CR; провайдер БД в визоре ищется по host:port из секрета.
# Не делает: sync, гашение на исходном кубе, DNS (это loopctl k8s move).
set -euo pipefail
ID=${1:?loop id, например loop12a40f770b5a}
. "$(dirname "$0")/../lib/common.sh"
copy_secret() { # $1 имя; копирует с $SRC на $DST без вывода данных
kubectl --context "$SRC" -n loop get secret "$1" -o json \
| jq '{apiVersion,kind,type,data,metadata:{name:.metadata.name,namespace:"loop"}}' \
| kubectl --context "$DST" apply -f - >/dev/null
echo " $1 скопирован"
}
hash_secrets() { # $1 context
kubectl --context "$1" -n loop get secret "$ID-db-credentials" "$ID-s3-credentials" "$ID-license" -o json \
| jq -S '[.items[]|{n:.metadata.name,t:.type,d:.data}]' | shasum | cut -c1-12
}
echo "== $ID"
# источник = куб, где есть CR (registry неактуален); домен из ingress CR
for SRC in $SOURCES ""; do
[ -n "$SRC" ] || { echo "CR $ID не найден ни на одном исходном кубе"; exit 1; }
DOMAIN=$(kubectl --context "$SRC" -n loop get mattermost "$ID" -o jsonpath='{.spec.ingress.host}' 2>/dev/null) && [ -n "$DOMAIN" ] && break
done
SRC_CLUSTER=$(echo "select id from cluster_instance where name='$SRC';" | vsql)
DST_CLUSTER=$(echo "select id from cluster_instance where name='$DST';" | vsql)
[ -n "$DST_CLUSTER" ] || { echo "в визоре нет кластера $DST"; exit 1; }
echo " $DOMAIN $SRC"
# база в секрете на исходном кубе уже должна смотреть в Selectel
DBHOST=$(kubectl --context "$SRC" -n loop get secret "$ID-db-credentials" -o json | jq -r '.data.DB_CONNECTION_STRING|@base64d' | sed -E 's#.*@([^/]+)/.*#\1#')
echo " БД в секрете: $DBHOST"
case $DBHOST in *.mdb.yandexcloud.net*) echo "секрет ещё смотрит в Yandex, стоп"; exit 1;; esac
# CR с таким именем на $DST быть не должно
if kubectl --context "$DST" -n loop get mattermost "$ID" >/dev/null 2>&1; then echo "CR уже есть на $DST, стоп"; exit 1; fi
# провайдер БД = ON_PREM-провайдер визора с тем же host:port, что в секрете
PROVIDER=$(echo "select id||'|'||name from database_provider where vendor='ON_PREM' and host_name='${DBHOST%:*}' and host_port::text='${DBHOST##*:}';" | vsql)
[ "$(grep -c . <<<"$PROVIDER")" = 1 ] || { echo "в визоре не один ON_PREM-провайдер для $DBHOST: '$PROVIDER'"; exit 1; }
TARGET=${PROVIDER#*|}; PROVIDER=${PROVIDER%%|*}; echo " провайдер: $TARGET"
confirm "Скопирую секреты $ID на $DST и перепривяжу инстанс в визоре на $DST." || exit 1
echo "-- секреты на $DST"
for s in wildcard-loop.ru-tls private.docker.wilix.dev; do # общие, копируем один раз
kubectl --context "$DST" -n loop get secret "$s" >/dev/null 2>&1 || copy_secret "$s"
done
for s in db-credentials s3-credentials license; do copy_secret "$ID-$s"; done
H1=$(hash_secrets "$SRC"); H3=$(hash_secrets "$DST")
[ "$H1" = "$H3" ] || { echo "секреты не совпали: $H1 / $H3"; exit 1; }
echo " совпадают ($H1)"
# свой сертификат (не wildcard): cluster-issuer оставляем, иначе на новом кубе он перестанет продлеваться
OWN_TLS=$(kubectl --context "$SRC" -n loop get mattermost "$ID" -o jsonpath='{.spec.ingress.tlsSecret}')
[ "$OWN_TLS" = wildcard-loop.ru-tls ] && OWN_TLS=""
echo "-- визор"
TAIL=${ID#loop}
FOUND=$(printf "select i.id, i.domain, c.name, d.id from loop_instance i join cluster_instance c on c.id=i.cluster_instance_id join database_instance d on d.id=i.database_instance_id where i.id like '%%%s' and i.deleted_at is null;\n" "$TAIL" | vsql)
[ "$(printf '%s\n' "$FOUND" | grep -c .)" = 1 ] || { echo "в визоре не один инстанс: $FOUND"; exit 1; }
IFS='|' read -r VID VDOMAIN VCLUSTER DBID <<<"$FOUND"
echo " $VID $VDOMAIN куб=$VCLUSTER"
[ "$VDOMAIN" = "$DOMAIN" ] || { echo "домен в визоре другой: $VDOMAIN"; exit 1; }
vsql <<SQL
begin;
update loop_instance set cluster_instance_id='$DST_CLUSTER', synchronization_status='NOT_SYNCHRONIZED' where id='$VID';
update database_instance set vendor='ON_PREM',
database_provider_id='$PROVIDER'
where id='$DBID';
-- на целевом кубе traefik; без класса оператор ставит nginx. cluster-issuer убрать: с wildcard он перезапишет wildcard-loop.ru-tls
delete from loop_instance_annotation where loop_instance_id='$VID'
and (annotation='kubernetes.io/ingress.class' or (annotation='cert-manager.io/cluster-issuer' and '$OWN_TLS'=''));
insert into loop_instance_annotation(id, loop_instance_id, annotation, annotation_value)
values (gen_random_uuid()::text, '$VID', 'kubernetes.io/ingress.class', '${DST_INGRESS_CLASS:-traefik}');
select i.domain, c.name, p.name, d.vendor from loop_instance i
join cluster_instance c on c.id=i.cluster_instance_id
join database_instance d on d.id=i.database_instance_id
join database_provider p on p.id=d.database_provider_id where i.id='$VID';
commit;
SQL
echo "Готово. Дальше: loopctl k8s move $ID (env, sync, проверки, прокси, DNS)."
echo "Откат визора: update loop_instance set cluster_instance_id='$SRC_CLUSTER' where id='$VID';"