ИШ обновляется через сервер лицензий вместе с bj; наглядный ход установки ИШ

- Дистрибутив ИШ — компонент выпуска igate-dist (версия = версия ИШ). В выпуск
  попадает только одобренный архив: deploy/release/igate-approved (версия и
  sha256), сборка сверяет контрольную сумму. Клиент получает его обновлением;
  bj-server после обновления (и при запуске) сам обновляет ИШ, если на сервере
  он старее; итог — событие в «Новостях». Настройки, каналы и база ИШ
  сохраняются, прежняя версия — в /var/backups.
- Установка «с сервера» берёт самый новый проверенный дистрибутив: с образа или из
  обновления. С сайта НРД — только в режиме разработки.
- Большие файлы: одна прежняя копия вместо пяти; предел скачивания — по размеру.
- Ход установки ИШ — этапами: пройденные, текущий («идёт 0:41 · обычно до 1:30»,
  дольше обычного — предупреждение), журнал под раскрытием; кнопка на время
  установки выключена.
- Установщик: уникальные этапы; igate-cli и сессия ключницы с принудительным
  завершением (timeout -k); зависшая установка (ход не менялся 15 минут)
  снимается повторным запуском; служба установки — не дольше 40 минут.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
zuevav
2026-10-06 17:42:54 +03:00
parent 920c030ca5
commit 648a5ae05f
11 changed files with 196 additions and 42 deletions
+81 -13
View File
@@ -5,6 +5,8 @@ package lkgateway
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
@@ -153,13 +155,14 @@ func registerIgateAdmin(mux *http.ServeMux, rc *RuntimeConfig) {
return
}
params["file"] = dst
} else if r.FormValue("bundled") == "1" {
if igateBundledVersion() == "" {
jsonOut(w, map[string]any{"ok": false, "error": "в образе нет дистрибутива ИШ — скачайте с сайта НРД или загрузите файл"})
} else if r.FormValue("bundled") == "1" { // проверенный дистрибутив на сервере (образ или обновление bj)
file, _ := igateDistFile()
if file == "" {
jsonOut(w, map[string]any{"ok": false, "error": "дистрибутива ИШ на сервере нет — он приходит с обновлением bj"})
return
}
params["bundled"] = "1"
} else if v := strings.TrimSpace(r.FormValue("version")); igateVersionRe.MatchString(v) {
params["file"] = file
} else if v := strings.TrimSpace(r.FormValue("version")); igateVersionRe.MatchString(v) && devMode(rc) { // с сайта НРД — только разработчикам
params["version"] = v
} else {
jsonOut(w, map[string]any{"ok": false, "error": "загрузите архив ИШ или укажите версию вида 107.0.930"})
@@ -170,15 +173,22 @@ func registerIgateAdmin(mux *http.ServeMux, rc *RuntimeConfig) {
defer cancel()
jsonOut(w, opResult(runKeymediaOp(ctx, "igate-install", map[string]string{"extra": string(extra)})))
})
// ход установки: этап, сообщение, журнал и сколько секунд идёт текущий этап (по времени записи файла)
mux.HandleFunc("/admin/setup/igate/install-status", func(w http.ResponseWriter, r *http.Request) {
raw, err := os.ReadFile("/var/lib/bj/igate-install-status.json")
if err != nil {
raw, err := os.ReadFile(igateInstallStatusPath)
out := map[string]any{}
if err != nil || json.Unmarshal(raw, &out) != nil {
jsonOut(w, map[string]any{})
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.Header().Set("Cache-Control", "no-store")
_, _ = w.Write(raw)
if fi, err := os.Stat(igateInstallStatusPath); err == nil {
out["elapsed"] = int(time.Since(fi.ModTime()).Seconds())
}
st := igateInstallStatus()
if st.Stage == "error" && out["stage"] != "error" { // молчит 20 минут — прервалась
out["stage"], out["msg"] = "error", st.Msg
}
jsonOut(w, out)
})
// канал контура в ИШ (адрес, отпечаток УЦ, папка обмена); ключ — мастером установки ключа
mux.HandleFunc("/admin/setup/igate/contour", func(w http.ResponseWriter, r *http.Request) {
@@ -361,13 +371,71 @@ func igateInstallStatus() igateInstallState {
}
func igateBundledVersion() string {
_, v := igateDistFile()
return v
}
// igateDistFile — самый новый дистрибутив ИШ на сервере (файл и версия): с образа (igate-<версия>-amd64.zip)
// или из обновления bj (компонент igate-dist, версия — в списке установленных компонентов). Это версии,
// проверенные ZETIT; другой ИШ клиенту не ставится.
func igateDistFile() (string, string) {
best, file := "", ""
m, _ := filepath.Glob("/var/lib/bj/igate-dist/igate-*-amd64.zip")
best := ""
for _, f := range m {
v := strings.ReplaceAll(strings.TrimSuffix(strings.TrimPrefix(filepath.Base(f), "igate-"), "-amd64.zip"), "-", ".")
if igateVersionRe.MatchString(v) && (best == "" || release.IsNewer(v, best)) {
best = v
best, file = v, f
}
}
if c, ok := release.ComponentByName("igate-dist"); ok {
if inst, ok := release.LoadInstalled(release.InstalledFile)[c.Name]; ok && igateVersionRe.MatchString(inst.Version) {
if _, err := os.Stat(c.Path); err == nil && (best == "" || release.IsNewer(inst.Version, best)) {
best, file = inst.Version, c.Path
}
}
}
return file, best
}
// igateInstallDist — поставить (или обновить) ИШ из самого нового дистрибутива на сервере.
func igateInstallDist(ctx context.Context) error {
file, _ := igateDistFile()
if file == "" {
return errors.New("дистрибутива ИШ на сервере нет — он приходит с обновлением bj")
}
extra, _ := json.Marshal(map[string]string{"file": file})
res, err := runKeymediaOp(ctx, "igate-install", map[string]string{"extra": string(extra)})
if err == nil {
if ok, _ := res["ok"].(bool); !ok {
err = fmt.Errorf("%v", res["error"])
}
}
return err
}
// igateAutoUpgrade — ИШ на сервере старее дистрибутива, пришедшего с обновлением bj: обновить (настройки,
// каналы и база ИШ сохраняются; прежняя версия копируется в /var/backups). Вызывается после обновления bj
// и при запуске bj-server (обновление ядра перезапускает его до того, как успел бы сработать первый вызов).
func igateAutoUpgrade(ctx context.Context, rc *RuntimeConfig) {
cur := ishInstalledVersion()
_, dist := igateDistFile()
if !igateInstalled() || cur == "" || dist == "" || !release.IsNewer(dist, cur) || igateInstallStatus().Busy {
return
}
log.Printf("lk-gateway: ИШ %s старее проверенного дистрибутива %s — обновляю", cur, dist)
if err := igateInstallDist(ctx); err != nil {
raiseSystemAlert(rc, "ИШ не обновлён до "+dist, "Пришёл дистрибутив ИШ "+dist+", но обновление не запустилось: "+err.Error()+". Повторить — мастер, шаг «Шлюз НРД».")
return
}
for ctx.Err() == nil {
time.Sleep(5 * time.Second)
if st := igateInstallStatus(); !st.Busy {
if st.Stage == "done" {
raiseSystemAlert(rc, "ИШ обновлён до "+dist, "Интеграционный шлюз НРД обновлён с "+cur+" до "+dist+" (версия, проверенная ZETIT). Настройки и каналы сохранены.")
} else {
raiseSystemAlert(rc, "ИШ не обновлён до "+dist, "Обновление ИШ с "+cur+" до "+dist+" не прошло: "+st.Msg+". Прежняя версия сохранена в /var/backups; повторить — мастер, шаг «Шлюз НРД».")
}
return
}
}
return best
}