ИШ обновляется через сервер лицензий вместе с 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
+8 -1
View File
@@ -88,7 +88,14 @@ func (c *Client) DownloadArtifact(ctx context.Context, a Artifact, destDir strin
return "", err
}
c.authorize(req)
resp, err := c.HTTP.Do(req)
hc := c.HTTP
if hc.Timeout > 0 && a.Size > 16<<20 {
// большой файл (дистрибутив ИШ — 160 МБ): общий предел запроса — по размеру, из расчёта 256 КБ/с
cp := *hc
cp.Timeout = hc.Timeout + time.Duration(a.Size/(256<<10))*time.Second
hc = &cp
}
resp, err := hc.Do(req)
if err != nil {
return "", fmt.Errorf("release: скачивание %s: %w", a.Name, err)
}
+6
View File
@@ -26,6 +26,8 @@ type Component struct {
FromCore bool
// Feature — признак лицензии, без которого сервер обновлений не отдаёт файл компонента.
Feature string
// Big — большой файл (дистрибутив): при замене хранится одна прежняя копия, а не пять.
Big bool
}
// Components — все компоненты bj. Порядок — порядок показа и установки (ядро перезапускается последним).
@@ -42,6 +44,10 @@ var Components = []Component{
{Name: "bj-lk", Title: "Личный кабинет клиента", Path: "/opt/bj/bj-lk", Unit: "bj-lk", Exec: true, Feature: "web-cabinet"},
{Name: "bj-npr", Title: "Риск-монитор НПР / УДС", Path: "/opt/bj/bj-npr", Unit: "bj-npr", Exec: true, FromCore: true, Feature: "npr"},
{Name: "bj-quikexport", Title: "Экспорт QUIK", Path: "/opt/bj/bj-quikexport", Unit: "bj-quikexport", Exec: true, FromCore: true, Feature: "quik-export"},
// Дистрибутив интеграционного шлюза НРД — версия, проверенная ZETIT вместе с bj (версия компонента =
// версия ИШ). Обновление кладёт файл; bj-server сам переставляет ИШ, если на сервере он старее
// (настройки и каналы сохраняются). С сайта НРД ИШ у клиента не качается.
{Name: "igate-dist", Title: "Интеграционный шлюз НРД (дистрибутив)", Path: "/var/lib/bj/igate-dist/igate-dist.zip", Core: true, Big: true, Feature: "updates"},
}
// ComponentByName — компонент по имени артефакта.
+8 -3
View File
@@ -134,7 +134,7 @@ func InstallStaged(staging string, opt InstallOptions) (res InstallResult) {
// с образа кладёт unit-файлы всех служб сразу (выключенными), а файл модуля приезжает обновлением.
_, statErr := os.Stat(j.c.PathAt(opt.Root))
fresh := statErr != nil
if err := installFile(j.file, j.c.PathAt(opt.Root), j.c.Exec, ts); err != nil {
if err := installFile(j.file, j.c.PathAt(opt.Root), j.c.Exec, ts, j.c.Big); err != nil {
return fail("установка %s: %v", j.c.Name, err)
}
installed[j.c.Name] = Installed{Version: j.a.Version, SHA256: j.a.SHA256, InstalledAt: time.Now().UTC()}
@@ -248,15 +248,20 @@ func readStagedJSON(dir, name string, v any) error {
}
// installFile — копия прежнего файла, запись нового рядом и атомарная замена.
func installFile(src, dst string, execBit bool, ts string) error {
func installFile(src, dst string, execBit bool, ts string, big bool) error {
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
return err
}
if _, err := os.Stat(dst); err == nil {
keep := 5
if big { // дистрибутив в сотни мегабайт — одна прежняя копия
keep = 1
}
pruneBackups(dst, keep-1)
if err := copyFileMode(dst, dst+".bak-"+ts, 0o755); err != nil {
return fmt.Errorf("копия прежнего файла: %w", err)
}
pruneBackups(dst, 5)
pruneBackups(dst, keep)
}
mode := os.FileMode(0o644)
if execBit {