diff --git a/internal/lkgateway/igate.go b/internal/lkgateway/igate.go index 3eaa3ec..0379c7e 100644 --- a/internal/lkgateway/igate.go +++ b/internal/lkgateway/igate.go @@ -329,6 +329,37 @@ func refreshCertStore(rc *RuntimeConfig) { } // igateBundledVersion — версия ИШ, лежащего на сервере с образа (/var/lib/bj/igate-dist; "" — нет). +// igateInstallState — ход установки ИШ (файл пишет установщик bj-igate-admin). +type igateInstallState struct { + Stage, Msg string + Busy bool // установка идёт (не done/error и файл обновлялся недавно) +} + +// igateInstallStatusPath — файл хода установки; var — для тестов. +var igateInstallStatusPath = "/var/lib/bj/igate-install-status.json" + +func igateInstallStatus() igateInstallState { + p := igateInstallStatusPath + var st igateInstallState + b, err := os.ReadFile(p) + if err != nil { + return st + } + var raw struct{ Stage, Msg string } + if json.Unmarshal(b, &raw) != nil { + return st + } + st.Stage, st.Msg = raw.Stage, raw.Msg + if fi, err := os.Stat(p); err == nil && st.Stage != "done" && st.Stage != "error" { + // установщик обновляет файл на каждом этапе; молчит 20 минут — установка прервалась + st.Busy = time.Since(fi.ModTime()) < 20*time.Minute + if !st.Busy { + st.Stage, st.Msg = "error", "установка прервалась ("+st.Msg+")" + } + } + return st +} + func igateBundledVersion() string { m, _ := filepath.Glob("/var/lib/bj/igate-dist/igate-*-amd64.zip") best := "" diff --git a/internal/lkgateway/onboarding_test.go b/internal/lkgateway/onboarding_test.go index e3427a1..f9108b5 100644 --- a/internal/lkgateway/onboarding_test.go +++ b/internal/lkgateway/onboarding_test.go @@ -314,8 +314,9 @@ func TestPrepareScreen(t *testing.T) { } } rc, _ := NewRuntimeConfig(t.TempDir() + "/setup.json") - if items := prepareItems(rc, false, false, ""); len(items) != 2 || items[0].Title != "Обновление bj" || items[0].State != "wait" || items[1].State != "ok" { - t.Fatalf("без лицензии — обновление (ещё не проверялось) и ядро: %+v", items) + // ИШ — у любой установки: ставится при подготовке из проверенного дистрибутива на сервере + if items := prepareItems(rc, false, false, ""); len(items) != 3 || items[0].Title != "Обновление bj" || items[0].State != "wait" || items[1].State != "ok" || !strings.Contains(items[2].Title, "Интеграционный шлюз") { + t.Fatalf("без лицензии — обновление (ещё не проверялось), ядро и ИШ: %+v", items) } // выпуск, который подготовка уже ставила, после перезапуска bj не ставится по кругу oldTried := prepareTriedFile @@ -730,3 +731,30 @@ func TestQuikModeAndCertHint(t *testing.T) { t.Fatal("ошибка сертификата распознаётся, ошибка логина — нет") } } + +// Ход установки ИШ для «Подготовки модулей»: идёт — пока установщик обновляет файл; молчит 20 минут — прервалась. +func TestIgateInstallStatus(t *testing.T) { + old := igateInstallStatusPath + igateInstallStatusPath = filepath.Join(t.TempDir(), "st.json") + defer func() { igateInstallStatusPath = old }() + if st := igateInstallStatus(); st.Busy || st.Stage != "" { + t.Fatalf("нет файла: %+v", st) + } + write := func(stage string, age time.Duration) { + _ = os.WriteFile(igateInstallStatusPath, []byte(`{"stage":"`+stage+`","msg":"шаг"}`), 0o644) + at := time.Now().Add(-age) + _ = os.Chtimes(igateInstallStatusPath, at, at) + } + write("package", time.Minute) + if st := igateInstallStatus(); !st.Busy { + t.Fatalf("идёт: %+v", st) + } + write("package", 25*time.Minute) + if st := igateInstallStatus(); st.Busy || st.Stage != "error" || !strings.Contains(st.Msg, "прервалась") { + t.Fatalf("прервалась: %+v", st) + } + write("done", time.Hour) + if st := igateInstallStatus(); st.Busy || st.Stage != "done" { + t.Fatalf("готово: %+v", st) + } +} diff --git a/internal/lkgateway/prepare.go b/internal/lkgateway/prepare.go index 93fa290..70707a0 100644 --- a/internal/lkgateway/prepare.go +++ b/internal/lkgateway/prepare.go @@ -7,6 +7,7 @@ package lkgateway import ( "context" + "fmt" "net/http" "os" "os/exec" @@ -30,6 +31,7 @@ var prepareRun struct { Running, Finished bool Result, Err string UpdState, UpdText string // этап «Обновление bj»: пусто — ещё не проверялось в этом запуске bj + IgateErr string // установку ИШ не удалось даже запустить } // prepareBoot — метка запуска bj: страница видит перезапуск (после обновления ядра) и продолжает подготовку. @@ -86,6 +88,7 @@ func prepareItems(rc *RuntimeConfig, running, finished bool, runErr string) []Pr } out = append(out, it) } + out = append(out, prepareIgateItem()) if moduleAllowed(rc, "web-cabinet") { out = append(out, PrepareItem{Title: moduleTitles["web-cabinet"], State: "info", Detail: "ставится на веб-сервер организации: дистрибутив и ключ кабинета — в мастере, глава «Запуск»"}) } @@ -169,7 +172,7 @@ func prepareStart(rc *RuntimeConfig) { prepareRun.Running, prepareRun.Finished, prepareRun.Result, prepareRun.Err = true, false, "", "" prepareRun.Unlock() go func() { - ctx, cancel := context.WithTimeout(context.Background(), 12*time.Minute) + ctx, cancel := context.WithTimeout(context.Background(), 25*time.Minute) defer cancel() prepareUpdates(ctx, rc) // сначала — обновления (при обновлении ядра bj перезапустится, страница продолжит) var msg, errText string @@ -180,12 +183,73 @@ func prepareStart(rc *RuntimeConfig) { } else { msg = m } + prepareIgate(ctx) prepareRun.Lock() prepareRun.Running, prepareRun.Finished, prepareRun.Result, prepareRun.Err = false, true, msg, errText prepareRun.Unlock() }() } +// prepareIgate — интеграционный шлюз НРД при подготовке: нет на сервере — поставить из дистрибутива, который +// лежит на сервере (образ или обновление bj): это версия, проверенная ZETIT вместе с bj. С сайта НРД ИШ не +// качается — новые версии приходят только с обновлением bj. Ход установки пишет установщик (igateInstallStatus). +func prepareIgate(ctx context.Context) { + if igateInstalled() || igateBundledVersion() == "" { + return + } + if st := igateInstallStatus(); !st.Busy { + res, err := runKeymediaOp(ctx, "igate-install", map[string]string{"extra": `{"bundled":"1"}`}) + if err == nil { + if ok, _ := res["ok"].(bool); !ok { + err = fmt.Errorf("%v", res["error"]) + } + } + if err != nil { + prepareRun.Lock() + prepareRun.IgateErr = err.Error() + prepareRun.Unlock() + return + } + } + for ctx.Err() == nil { // 2–5 минут: пакеты, пользователь igate, база, служба + time.Sleep(3 * time.Second) + if st := igateInstallStatus(); !st.Busy && st.Stage != "" { + return + } + } +} + +// prepareIgateItem — строка «Интеграционный шлюз НРД» на экране подготовки. +func prepareIgateItem() PrepareItem { + it := PrepareItem{Title: "Интеграционный шлюз НРД (ИШ)"} + st := igateInstallStatus() + prepareRun.Lock() + igErr := prepareRun.IgateErr + prepareRun.Unlock() + switch { + case st.Busy: + it.State, it.Detail = "run", "устанавливается: "+st.Msg+" (2–5 минут)" + case igateInstalled(): + it.State, it.Detail = "ok", "установлен"+pickStr(prefixed(", версия ", ishInstalledVersion()), "") + case st.Stage == "error": + it.State, it.Detail = "err", "не установился: "+st.Msg+" — повторить можно в мастере, шаг «Шлюз НРД», кнопка «Установить ИШ заново»" + case igErr != "": + it.State, it.Detail = "err", "установка не запустилась: "+igErr+" — повторить можно в мастере, шаг «Шлюз НРД»" + case igateBundledVersion() == "": + it.State, it.Detail = "warn", "дистрибутива ИШ на сервере нет — он приходит с обновлением bj" + default: + it.State, it.Detail = "wait", "будет установлен, версия "+igateBundledVersion() + } + return it +} + +func prefixed(p, v string) string { + if v == "" { + return "" + } + return p + v +} + // prepareUpdates — первый этап подготовки: есть обновление — поставить, нет — дальше. func prepareUpdates(ctx context.Context, rc *RuntimeConfig) { set := func(state, text string) { diff --git a/internal/lkgateway/web/templates/admin_wizard.html b/internal/lkgateway/web/templates/admin_wizard.html index 02dba1f..bfde931 100644 --- a/internal/lkgateway/web/templates/admin_wizard.html +++ b/internal/lkgateway/web/templates/admin_wizard.html @@ -528,12 +528,9 @@ {{if eq .Step 7}}
Через интеграционный шлюз НРД (ИШ) на этом сервере bj отправляет переводы и получает ответы. bj ставит и настраивает шлюз сам; доступ к тестовому и боевому контуру НРД открывает по заявке — письма готовы.
+Через интеграционный шлюз НРД (ИШ) на этом сервере bj отправляет переводы и получает ответы. bj ставит и настраивает шлюз сам; доступ к тестовому и боевому контуру НРД открывает по заявке на своём сайте — как её подать, ниже.
{{template "igateInstall" .}} -