Java 17 для службы подписи: установщик ставит openjdk-17-jre-headless там, где Java по умолчанию старее (Ubuntu 22.04 — 11; crypto-service собран под 17), в слой образа кладётся Java 17; «Подготовка модулей» и проверка готовности не принимают падающую по кругу службу за работающую (работает ≥ 5 с) и называют причину — в том числе «слишком старая Java» с командой исправления
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
@@ -262,23 +262,44 @@ func memTotal() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// javaPkg — Java для службы подписи (crypto-service собран под Java 17): где Java по умолчанию старше
|
||||
// не бывает (Ubuntu 22.04, Astra 1.7 — Java 11), ставится openjdk-17; где по умолчанию 17 и новее
|
||||
// (Debian 13 — Java 21), — пакет по умолчанию.
|
||||
func javaPkg(debs []string) string {
|
||||
if len(debs) > 0 { // пакеты с образа: какая Java положена в образ
|
||||
for _, d := range debs {
|
||||
if strings.HasPrefix(filepath.Base(d), "openjdk-17-jre-headless_") {
|
||||
return "openjdk-17-jre-headless"
|
||||
}
|
||||
}
|
||||
return "default-jre-headless"
|
||||
}
|
||||
out, err := run("apt-cache", "policy", "openjdk-17-jre-headless")
|
||||
if err == nil && strings.Contains(out, "Candidate:") && !strings.Contains(out, "Candidate: (none)") {
|
||||
return "openjdk-17-jre-headless"
|
||||
}
|
||||
return "default-jre-headless"
|
||||
}
|
||||
|
||||
func (in *installer) stepPackages() (string, error) {
|
||||
pkgs := []string{"ca-certificates", "curl", "postgresql", "postgresql-client", "default-jre-headless", "pcscd", "python3", "smbclient",
|
||||
pkgs := []string{"ca-certificates", "curl", "postgresql", "postgresql-client", "pcscd", "python3", "smbclient",
|
||||
// для интеграционного шлюза НРД без графики (ИШ 107: пароли в gnome-keyring, движок под виртуальным дисплеем)
|
||||
"gnome-keyring", "dbus", "xvfb", "xauth", "libsecret-tools", "unzip"}
|
||||
env := append(os.Environ(), "DEBIAN_FRONTEND=noninteractive")
|
||||
// Образ кладёт пакеты со всеми зависимостями в /var/lib/bj/debs — ставим с диска, без сети.
|
||||
// Не вышло (другая система, не хватает зависимости) — обычная установка из репозитория.
|
||||
if debs, _ := filepath.Glob(in.P(debsDir) + "/*.deb"); len(debs) > 0 && in.root == "" {
|
||||
withJava := append(pkgs, javaPkg(debs))
|
||||
args := append([]string{"install", "-y", "-qq", "--no-install-recommends"}, debs...)
|
||||
if _, err := runEnv(env, "apt-get", append(args, pkgs...)...); err == nil {
|
||||
if _, err := runEnv(env, "apt-get", append(args, withJava...)...); err == nil {
|
||||
_ = os.RemoveAll(in.P(debsDir))
|
||||
return strings.Join(pkgs, ", ") + " (с образа, без загрузки)", nil
|
||||
return strings.Join(withJava, ", ") + " (с образа, без загрузки)", nil
|
||||
}
|
||||
}
|
||||
if out, err := runEnv(env, "apt-get", "update", "-qq"); err != nil {
|
||||
return "", fmt.Errorf("apt-get update: %v\n%s", err, tail(out, 8))
|
||||
}
|
||||
pkgs = append(pkgs, javaPkg(nil))
|
||||
args := append([]string{"install", "-y", "-qq", "--no-install-recommends"}, pkgs...)
|
||||
if out, err := runEnv(env, "apt-get", args...); err != nil {
|
||||
return "", fmt.Errorf("apt-get install: %v\n%s", err, tail(out, 12))
|
||||
|
||||
@@ -18,8 +18,8 @@ SRC=${1:?ubuntu-22.04.x-live-server-amd64.iso}
|
||||
OUT=${2:?выходной каталог}
|
||||
[ "$(id -u)" = 0 ] || { echo "нужен root"; exit 1; }
|
||||
for c in mksquashfs xorriso; do command -v $c >/dev/null || { echo "нужен $c"; exit 1; }; done
|
||||
# пакеты — как в bj-installer (stepPackages)
|
||||
BJ_PKGS="ca-certificates curl postgresql postgresql-client default-jre-headless pcscd python3 smbclient gnome-keyring dbus xvfb xauth libsecret-tools unzip"
|
||||
# пакеты — как в bj-installer (stepPackages); Java 17 — служба подписи собрана под неё (в 22.04 по умолчанию 11)
|
||||
BJ_PKGS="ca-certificates curl postgresql postgresql-client openjdk-17-jre-headless pcscd python3 smbclient gnome-keyring dbus xvfb xauth libsecret-tools unzip"
|
||||
LAYER=ubuntu-server-minimal.ubuntu-server.bj.squashfs
|
||||
|
||||
mkdir -p "$OUT"
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -47,17 +49,19 @@ func prepareItems(rc *RuntimeConfig, running, finished bool, runErr string) []Pr
|
||||
it := PrepareItem{Title: prepareTitle(c)}
|
||||
_, statErr := os.Stat(c.Path)
|
||||
present := statErr == nil
|
||||
us := ""
|
||||
var u unitRun
|
||||
if present && c.Unit != "" {
|
||||
us = unitState(c.Unit).State
|
||||
u = unitRunState(c.Unit)
|
||||
}
|
||||
switch {
|
||||
case us == "active":
|
||||
case u.State == "active" && u.Up >= 5*time.Second:
|
||||
it.State, it.Detail = "ok", "установлен, служба работает"
|
||||
case us == "activating" || us == "reloading":
|
||||
case (u.State == "activating" || u.State == "active") && u.Restarts >= 3:
|
||||
it.State, it.Detail = "err", "установлен, но служба падает при запуске: "+unitFailCause(c)
|
||||
case u.State == "activating" || u.State == "reloading" || u.State == "active":
|
||||
it.State, it.Detail = "run", "установлен, служба запускается…"
|
||||
case present && c.Unit == "bj-crypto":
|
||||
it.State, it.Detail = "warn", "установлен, служба не запущена ("+us+") — обмену не мешает: пакеты для НРД подписывает шлюз"
|
||||
it.State, it.Detail = "warn", "установлен, служба не запущена ("+u.State+") — обмену не мешает: пакеты для НРД подписывает шлюз"
|
||||
case present:
|
||||
it.State, it.Detail = "ok", "установлен; служба заработает, когда модуль будет настроен (глава «Запуск»)"
|
||||
case running:
|
||||
@@ -77,6 +81,61 @@ func prepareItems(rc *RuntimeConfig, running, finished bool, runErr string) []Pr
|
||||
return out
|
||||
}
|
||||
|
||||
// unitRun — служба: состояние, сколько работает после последнего запуска, сколько раз systemd её перезапускал.
|
||||
type unitRun struct {
|
||||
State string
|
||||
Up time.Duration
|
||||
Restarts int
|
||||
}
|
||||
|
||||
func unitRunState(unit string) unitRun {
|
||||
r := unitRun{State: "unknown"}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
||||
defer cancel()
|
||||
out, err := exec.CommandContext(ctx, "systemctl", "show", unit, "--property=ActiveState,NRestarts,ActiveEnterTimestampMonotonic").Output()
|
||||
if err != nil {
|
||||
return r
|
||||
}
|
||||
var since int64
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
k, v, _ := strings.Cut(strings.TrimSpace(line), "=")
|
||||
switch k {
|
||||
case "ActiveState":
|
||||
r.State = v
|
||||
case "NRestarts":
|
||||
r.Restarts, _ = strconv.Atoi(v)
|
||||
case "ActiveEnterTimestampMonotonic":
|
||||
since, _ = strconv.ParseInt(v, 10, 64)
|
||||
}
|
||||
}
|
||||
if b, err := os.ReadFile("/proc/uptime"); err == nil && since > 0 {
|
||||
if f := strings.Fields(string(b)); len(f) > 0 {
|
||||
if up, err := strconv.ParseFloat(f[0], 64); err == nil {
|
||||
r.Up = time.Duration(up*float64(time.Second)) - time.Duration(since)*time.Microsecond
|
||||
}
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// unitFailCause — причина падения службы модуля по последней строке её журнала ошибок — понятными словами.
|
||||
func unitFailCause(c release.Component) string {
|
||||
logs := map[string]string{"bj-crypto": "/var/log/bj/crypto-service.err", "bj-npr": "/var/log/bj/bj-npr.err", "bj-quikexport": "/var/log/bj/bj-quikexport.err"}
|
||||
b, err := os.ReadFile(logs[c.Unit])
|
||||
if err != nil {
|
||||
return "подробности — в журнале службы " + c.Unit
|
||||
}
|
||||
lines := strings.Split(strings.TrimSpace(string(b)), "\n")
|
||||
last := strings.TrimSpace(lines[len(lines)-1])
|
||||
if strings.Contains(string(b[max(0, len(b)-4000):]), "UnsupportedClassVersionError") {
|
||||
return "на сервере слишком старая Java (нужна 17 или новее): sudo apt-get install openjdk-17-jre-headless — служба запустится сама"
|
||||
}
|
||||
if len(last) > 240 {
|
||||
last = last[:240] + "…"
|
||||
}
|
||||
return last
|
||||
}
|
||||
|
||||
// prepareTitle — название модуля для экрана.
|
||||
func prepareTitle(c release.Component) string {
|
||||
switch c.Name {
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/release"
|
||||
)
|
||||
|
||||
// cryptoJarPath — модуль службы пробной подписи bj-crypto (компонент crypto-service.jar).
|
||||
@@ -134,10 +136,12 @@ func readinessChecks(ctx context.Context, rc *RuntimeConfig) []ReadyCheck {
|
||||
// Служба bj-crypto — пробная подпись и проверка ключа из bj; пакеты для НРД подписывает шлюз, поэтому
|
||||
// её остановка обмену не мешает. Модуль приезжает с обновлениями — до них службы нет, это не ошибка.
|
||||
if _, err := os.Stat(cryptoJarPath); err == nil {
|
||||
out, _ := exec.CommandContext(cctx, "systemctl", "is-active", "bj-crypto.service").Output()
|
||||
if svc := strings.TrimSpace(string(out)); svc != "active" {
|
||||
if u := unitRunState("bj-crypto"); !(u.State == "active" && u.Up >= 5*time.Second) {
|
||||
c.State, c.Link = "warn", "/admin/setup#sec-crypto"
|
||||
c.Detail += "; служба пробной подписи bj-crypto не запущена (" + pickStr(svc, "нет") + ") — обмену не мешает: пакеты для НРД подписывает шлюз"
|
||||
c.Detail += "; служба пробной подписи bj-crypto не работает (" + pickStr(u.State, "нет") + ") — обмену не мешает: пакеты для НРД подписывает шлюз"
|
||||
if cc, ok := release.ComponentByName("crypto-service.jar"); ok && (u.Restarts >= 3 || u.State == "failed") {
|
||||
c.Detail += ". Причина: " + unitFailCause(cc)
|
||||
}
|
||||
} else {
|
||||
c.Detail += ", служба пробной подписи bj-crypto работает"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user