From fdd70c06dd3b7072f4ab3f200700351cd7dda857 Mon Sep 17 00:00:00 2001 From: zuevav Date: Thu, 24 Sep 2026 17:44:28 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D0=BD=D0=BE=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=BD=D0=B5=D1=81=D1=83=D1=82=20=D1=83?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D1=89=D0=B8=D0=BA:=20bj-in?= =?UTF-8?q?staller=20=E2=80=94=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=20=D1=8F=D0=B4=D1=80=D0=B0,=20refresh-system?= =?UTF-8?q?=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BF=D0=B8=D1=81=D1=8B=D0=B2=D0=B0?= =?UTF-8?q?=D0=B5=D1=82=20=D1=81=D0=BB=D1=83=D0=B6=D0=B1=D1=8B=20=D0=B8=20?= =?UTF-8?q?=D1=81=D0=BA=D1=80=D0=B8=D0=BF=D1=82=D1=8B;=20=D0=BC=D0=B0?= =?UTF-8?q?=D1=81=D1=82=D0=B5=D1=80=20=D0=BF=D1=80=D0=B5=D0=B4=D0=BB=D0=B0?= =?UTF-8?q?=D0=B3=D0=B0=D0=B5=D1=82=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=D1=81=D1=8F=20=D1=81=D1=80=D0=B0=D0=B7=D1=83=20?= =?UTF-8?q?=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D0=B2=D0=B2=D0=BE=D0=B4=D0=B0?= =?UTF-8?q?=20=D0=BA=D0=BB=D1=8E=D1=87=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Правки установки приезжают обновлением с сервера лицензий, образ ISO пересобирать не нужно. Прежний установщик обновлений bj-installer не знает — он откладывается и ставится после перезапуска bj-server (ensureSplitModules). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi --- cmd/bj-installer/main.go | 23 +++++++++++++++++++ internal/lkgateway/setup.go | 18 +++++++++++---- internal/lkgateway/updater.go | 17 ++++++++++++-- .../lkgateway/web/templates/admin_wizard.html | 10 ++++++++ internal/release/components.go | 4 ++++ internal/release/install.go | 8 +++++++ 6 files changed, 74 insertions(+), 6 deletions(-) diff --git a/cmd/bj-installer/main.go b/cmd/bj-installer/main.go index 4d1ce9e..36ff6d2 100644 --- a/cmd/bj-installer/main.go +++ b/cmd/bj-installer/main.go @@ -81,6 +81,8 @@ func main() { switch os.Args[1] { case "install": cmdInstall(os.Args[2:]) + case "refresh-system": + os.Exit(refreshSystem()) case "first-run-code": cmdFirstRunCode(os.Args[2:]) case "version": @@ -93,6 +95,7 @@ func main() { func usage() { fmt.Fprintln(os.Stderr, "bj-installer install --code BJ-XXXX-XXXX-XXXX [--server https://lic.zetit.ru] [--channel stable] [--dry-run]") fmt.Fprintln(os.Stderr, "bj-installer first-run-code") + fmt.Fprintln(os.Stderr, "bj-installer refresh-system (переписать службы и скрипты из этой версии установщика)") fmt.Fprintln(os.Stderr, "bj-installer version") os.Exit(2) } @@ -742,6 +745,26 @@ func (in *installer) stepServices() (string, error) { return strings.Join(files, ", ") + "; криптосервис включается после настройки ключей в bj", nil } +// refreshSystem — «bj-installer refresh-system»: переписать службы systemd и служебные скрипты +// из этой версии установщика (вызывается root-установщиком обновлений после обновления самого +// bj-installer). Работающие службы не перезапускаются: ядро перезапустит обновление, а служба +// root-операций bj-keymedia — через минуту, когда обновление, которое она выполняет, закончится. +func refreshSystem() int { + if os.Geteuid() != 0 { + fmt.Fprintln(os.Stderr, "refresh-system: нужны права root") + return 1 + } + in := &installer{} + msg, err := in.stepServices() + if err != nil { + fmt.Fprintln(os.Stderr, "refresh-system:", err) + return 1 + } + _, _ = run("systemd-run", "--on-active=60", "--unit=bj-keymedia-refresh", "systemctl", "try-restart", "bj-keymedia.service") + fmt.Println("системные файлы обновлены:", msg) + return 0 +} + func (in *installer) stepFirstRun() (string, error) { code, err := in.newFirstRunCode() if err != nil { diff --git a/internal/lkgateway/setup.go b/internal/lkgateway/setup.go index 4d32867..dab39bf 100644 --- a/internal/lkgateway/setup.go +++ b/internal/lkgateway/setup.go @@ -169,10 +169,11 @@ type WizardData struct { Lic LicenseStatus DepoLicensed bool DepoDB string - ValidataVer string // версия установленной «Валидаты Клиент L» (пусто — не установлена) - CryptoSvc string // состояние службы bj-crypto (systemctl is-active) - PGSummary string // куда подключена база bj, без пароля - AutoDone []string // шаги до текущего, уже выполненные без участия пользователя, — с пояснением + ValidataVer string // версия установленной «Валидаты Клиент L» (пусто — не установлена) + CryptoSvc string // состояние службы bj-crypto (systemctl is-active) + PGSummary string // куда подключена база bj, без пароля + Update *UpdateStatus // шаг 1: есть ли на сервере обновлений версия новее, чем в образе + AutoDone []string // шаги до текущего, уже выполненные без участия пользователя, — с пояснением Certs []cryptocli.Certificate Flash string Done struct { @@ -263,6 +264,15 @@ func (h *setupHandlers) renderWizard(w http.ResponseWriter, r *http.Request) { } } d.Step = step + if step == 1 && d.Done.License { + // Образ ставит ядро той версии, с которой собран; всё, что вышло позже (исправления ядра, + // установщика и служб), приезжает обновлением — предлагаем его сразу после ввода ключа. + ctx, cancel := context.WithTimeout(r.Context(), 8*time.Second) + if st, err := NewUpdater(h.rc).CheckForUpdate(ctx); err == nil { + d.Update = &st + } + cancel() + } d.AutoDone = wizardAutoDone(&d) render(w, h.tpl.a.wizard, d) } diff --git a/internal/lkgateway/updater.go b/internal/lkgateway/updater.go index 2774cb5..3782b2c 100644 --- a/internal/lkgateway/updater.go +++ b/internal/lkgateway/updater.go @@ -195,7 +195,7 @@ func (u *Updater) ApplyUpdate(ctx context.Context, names []string) (string, erro if installerOutdated { kept := selected[:0] for _, c := range selected { - if c.New { + if c.New || c.Name == "bj-installer" { // bj-installer прежний установщик обновлений не знает deferred = append(deferred, c.Name) continue } @@ -269,11 +269,24 @@ func (u *Updater) allowed(feature string) bool { return moduleAllowed(u.rc, feat // этих модулей в bj не работают. Раз за запуск bj-server; ошибки — только в журнал и «Последнюю проверку». func (u *Updater) ensureSplitModules(ctx context.Context) { var names []string - for _, c := range release.Plan("", nil, release.LoadInstalled(release.InstalledFile), BuildVersion) { + installed := release.LoadInstalled(release.InstalledFile) + for _, c := range release.Plan("", nil, installed, BuildVersion) { if c.FromCore && !c.Present && u.allowed(c.Feature) { names = append(names, c.Name) } } + // bj-installer обновлением ещё не ставился (сервер с образа или обновлён прежним установщиком + // обновлений): ставим сразу — с ним приезжают свежие службы и скрипты. Только если на сервере + // обновлений он новее, иначе ApplyUpdate вернёт «обновление не требуется» — это не ошибка. + if _, ok := installed["bj-installer"]; !ok && licenseStatus(u.rc).AllowsUpdates { + if st, err := u.CheckForUpdate(ctx); err == nil { + for _, c := range st.Components { + if c.Name == "bj-installer" && c.Update { + names = append(names, c.Name) + } + } + } + } if len(names) == 0 { return } diff --git a/internal/lkgateway/web/templates/admin_wizard.html b/internal/lkgateway/web/templates/admin_wizard.html index d59a130..3f62de2 100644 --- a/internal/lkgateway/web/templates/admin_wizard.html +++ b/internal/lkgateway/web/templates/admin_wizard.html @@ -55,6 +55,16 @@ Зачем. От лицензии зависят модули (обмен с DEPO и QUIK, личный кабинет, НПР/УДС) и получение обновлений: bj скачает и поставит модули, входящие в лицензию, автоматически.
Где взять. Портал клиента lic.zetit.ru/portal или письмо от ZETIT. {{if .Done.License}}

✓ Лицензия активна: {{.Lic.Tenant}}, план {{.Lic.Plan}}, модули: {{if .Lic.Features}}{{range $i, $f := .Lic.Features}}{{if $i}}, {{end}}{{$f}}{{end}}{{else}}только ядро{{end}}; {{.Lic.Message}}

{{end}} + {{with .Update}}{{if .HasUpdate}} +
+

Есть обновление: версия {{.Available}}

+

Образ установил ту версию bj, с которой был собран. На сервере лицензий есть более новая — с исправлениями ядра, установщика и служб. Поставьте её сейчас, до настройки: bj перезапустится примерно на 10 секунд, затем обновите страницу.
{{.Message}}

+
+ + +
+
+ {{else}}

✓ Установлена последняя версия bj ({{.Available}}).

{{end}}{{end}}
diff --git a/internal/release/components.go b/internal/release/components.go index c642abd..b39dc32 100644 --- a/internal/release/components.go +++ b/internal/release/components.go @@ -32,6 +32,10 @@ type Component struct { var Components = []Component{ {Name: "bj-server", Title: "Ядро bj: переводы M2M, DEPO, QUIK МНП, ЛК ЕСИА", Path: "/opt/bj/bj-server", Unit: "bj-server", Exec: true, Core: true, Feature: "updates"}, {Name: "bj-component-install", Title: "Установщик обновлений", Path: "/usr/local/sbin/bj-component-install", Exec: true, Core: true, Feature: "updates"}, + // Установщик несёт системные файлы (службы systemd, служебные скрипты): после его обновления + // они переписываются командой «bj-installer refresh-system» — правки установки приезжают + // обновлением, образ ISO пересобирать не нужно. + {Name: "bj-installer", Title: "Установщик и системные файлы (службы, скрипты)", Path: "/usr/local/sbin/bj-installer", Exec: true, Core: true, Feature: "updates"}, {Name: "crypto-service.jar", Title: "Криптосервис", Path: "/opt/bj/crypto-service.jar", Unit: "bj-crypto", Feature: "updates"}, {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"}, diff --git a/internal/release/install.go b/internal/release/install.go index 207d25f..a441732 100644 --- a/internal/release/install.go +++ b/internal/release/install.go @@ -153,6 +153,14 @@ func InstallStaged(staging string, opt InstallOptions) (res InstallResult) { return fail("включение служб %s: %v: %s", strings.Join(newUnits, ", "), err, strings.TrimSpace(string(out))) } } + for _, j := range jobs { + if j.c.Name == "bj-installer" && opt.Root == "" { + // новые службы и скрипты из обновлённого установщика; перезапуск служб — отдельно ниже + if out, err := exec.Command(j.c.Path, "refresh-system").CombinedOutput(); err != nil { + return fail("обновление системных файлов (bj-installer refresh-system): %v: %s", err, strings.TrimSpace(string(out))) + } + } + } if err := SaveInstalled(installedPath, installed); err != nil { return fail("запись установленных версий: %v", err) }