Обновления несут установщик: bj-installer — компонент ядра, refresh-system переписывает службы и скрипты; мастер предлагает обновиться сразу после ввода ключа
Правки установки приезжают обновлением с сервера лицензий, образ ISO пересобирать не нужно. Прежний установщик обновлений bj-installer не знает — он откладывается и ставится после перезапуска bj-server (ensureSplitModules). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -55,6 +55,16 @@
|
||||
<strong>Зачем.</strong> От лицензии зависят модули (обмен с DEPO и QUIK, личный кабинет, НПР/УДС) и получение обновлений: bj скачает и поставит модули, входящие в лицензию, автоматически.<br>
|
||||
<strong>Где взять.</strong> Портал клиента <a href="https://lic.zetit.ru/portal/" target="_blank">lic.zetit.ru/portal</a> или письмо от ZETIT.</div>
|
||||
{{if .Done.License}}<p>✓ Лицензия активна: <strong>{{.Lic.Tenant}}</strong>, план {{.Lic.Plan}}, модули: {{if .Lic.Features}}{{range $i, $f := .Lic.Features}}{{if $i}}, {{end}}{{$f}}{{end}}{{else}}только ядро{{end}}; {{.Lic.Message}}</p>{{end}}
|
||||
{{with .Update}}{{if .HasUpdate}}
|
||||
<div style="border:1px solid var(--accent);border-radius:8px;padding:12px 14px;margin-top:12px;background:var(--accent-weak)">
|
||||
<p style="margin:0 0 6px;font-weight:600">Есть обновление: версия {{.Available}}</p>
|
||||
<p class="muted" style="margin:0 0 10px;font-size:13px">Образ установил ту версию bj, с которой был собран. На сервере лицензий есть более новая — с исправлениями ядра, установщика и служб. Поставьте её сейчас, до настройки: bj перезапустится примерно на 10 секунд, затем обновите страницу.<br>{{.Message}}</p>
|
||||
<form method="post" action="/admin/setup/update/apply" style="margin:0">
|
||||
<input type="hidden" name="return_to" value="/admin/wizard?step=1">
|
||||
<button type="submit" class="btn">Обновить сейчас</button>
|
||||
</form>
|
||||
</div>
|
||||
{{else}}<p class="muted" style="font-size:13px">✓ Установлена последняя версия bj ({{.Available}}).</p>{{end}}{{end}}
|
||||
<form method="post" action="/admin/setup/license/activate" style="margin-top:12px;display:grid;gap:10px">
|
||||
<input type="hidden" name="return_to" value="/admin/wizard?step=1">
|
||||
<label>Ключ продукта</label>
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user