Обновления несут установщик: 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] {
|
switch os.Args[1] {
|
||||||
case "install":
|
case "install":
|
||||||
cmdInstall(os.Args[2:])
|
cmdInstall(os.Args[2:])
|
||||||
|
case "refresh-system":
|
||||||
|
os.Exit(refreshSystem())
|
||||||
case "first-run-code":
|
case "first-run-code":
|
||||||
cmdFirstRunCode(os.Args[2:])
|
cmdFirstRunCode(os.Args[2:])
|
||||||
case "version":
|
case "version":
|
||||||
@@ -93,6 +95,7 @@ func main() {
|
|||||||
func usage() {
|
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 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 first-run-code")
|
||||||
|
fmt.Fprintln(os.Stderr, "bj-installer refresh-system (переписать службы и скрипты из этой версии установщика)")
|
||||||
fmt.Fprintln(os.Stderr, "bj-installer version")
|
fmt.Fprintln(os.Stderr, "bj-installer version")
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
@@ -742,6 +745,26 @@ func (in *installer) stepServices() (string, error) {
|
|||||||
return strings.Join(files, ", ") + "; криптосервис включается после настройки ключей в bj", nil
|
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) {
|
func (in *installer) stepFirstRun() (string, error) {
|
||||||
code, err := in.newFirstRunCode()
|
code, err := in.newFirstRunCode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -169,10 +169,11 @@ type WizardData struct {
|
|||||||
Lic LicenseStatus
|
Lic LicenseStatus
|
||||||
DepoLicensed bool
|
DepoLicensed bool
|
||||||
DepoDB string
|
DepoDB string
|
||||||
ValidataVer string // версия установленной «Валидаты Клиент L» (пусто — не установлена)
|
ValidataVer string // версия установленной «Валидаты Клиент L» (пусто — не установлена)
|
||||||
CryptoSvc string // состояние службы bj-crypto (systemctl is-active)
|
CryptoSvc string // состояние службы bj-crypto (systemctl is-active)
|
||||||
PGSummary string // куда подключена база bj, без пароля
|
PGSummary string // куда подключена база bj, без пароля
|
||||||
AutoDone []string // шаги до текущего, уже выполненные без участия пользователя, — с пояснением
|
Update *UpdateStatus // шаг 1: есть ли на сервере обновлений версия новее, чем в образе
|
||||||
|
AutoDone []string // шаги до текущего, уже выполненные без участия пользователя, — с пояснением
|
||||||
Certs []cryptocli.Certificate
|
Certs []cryptocli.Certificate
|
||||||
Flash string
|
Flash string
|
||||||
Done struct {
|
Done struct {
|
||||||
@@ -263,6 +264,15 @@ func (h *setupHandlers) renderWizard(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
d.Step = step
|
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)
|
d.AutoDone = wizardAutoDone(&d)
|
||||||
render(w, h.tpl.a.wizard, 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 {
|
if installerOutdated {
|
||||||
kept := selected[:0]
|
kept := selected[:0]
|
||||||
for _, c := range selected {
|
for _, c := range selected {
|
||||||
if c.New {
|
if c.New || c.Name == "bj-installer" { // bj-installer прежний установщик обновлений не знает
|
||||||
deferred = append(deferred, c.Name)
|
deferred = append(deferred, c.Name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -269,11 +269,24 @@ func (u *Updater) allowed(feature string) bool { return moduleAllowed(u.rc, feat
|
|||||||
// этих модулей в bj не работают. Раз за запуск bj-server; ошибки — только в журнал и «Последнюю проверку».
|
// этих модулей в bj не работают. Раз за запуск bj-server; ошибки — только в журнал и «Последнюю проверку».
|
||||||
func (u *Updater) ensureSplitModules(ctx context.Context) {
|
func (u *Updater) ensureSplitModules(ctx context.Context) {
|
||||||
var names []string
|
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) {
|
if c.FromCore && !c.Present && u.allowed(c.Feature) {
|
||||||
names = append(names, c.Name)
|
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 {
|
if len(names) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,16 @@
|
|||||||
<strong>Зачем.</strong> От лицензии зависят модули (обмен с DEPO и QUIK, личный кабинет, НПР/УДС) и получение обновлений: bj скачает и поставит модули, входящие в лицензию, автоматически.<br>
|
<strong>Зачем.</strong> От лицензии зависят модули (обмен с DEPO и QUIK, личный кабинет, НПР/УДС) и получение обновлений: bj скачает и поставит модули, входящие в лицензию, автоматически.<br>
|
||||||
<strong>Где взять.</strong> Портал клиента <a href="https://lic.zetit.ru/portal/" target="_blank">lic.zetit.ru/portal</a> или письмо от ZETIT.</div>
|
<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}}
|
{{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">
|
<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">
|
<input type="hidden" name="return_to" value="/admin/wizard?step=1">
|
||||||
<label>Ключ продукта</label>
|
<label>Ключ продукта</label>
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ type Component struct {
|
|||||||
var Components = []Component{
|
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-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"},
|
{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: "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-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-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)))
|
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 {
|
if err := SaveInstalled(installedPath, installed); err != nil {
|
||||||
return fail("запись установленных версий: %v", err)
|
return fail("запись установленных версий: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user