agent (unreleased): fixes after an independent review — one lock for the writers of agent.json, the trust run serialised and bounded, the CA copy keeps its previous root, the profile stanza from the wrapper's answer, a stale give-up ends, both boot tokens from one read
gates / gates (push) Successful in 48s
gates / gates (push) Successful in 48s
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
1 parent
61feb95c06
commit
ec7e699fd7
18 files changed
+333
-59
No files matched your search
@@ -25,6 +25,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gitea.dooplex.hu/admin/felhom-agent/internal/config"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -81,12 +82,16 @@ type Manager struct {
|
||||
// MinGap between two runs that a failed call asked for (default 10 minutes).
|
||||
MinGap time.Duration
|
||||
|
||||
runMu sync.Mutex // serialises Ensure
|
||||
mu sync.Mutex
|
||||
cur proxmox.TLSConfig
|
||||
status Status
|
||||
lastRun time.Time
|
||||
}
|
||||
|
||||
// copyStepTimeout bounds the root step that makes the CA copy.
|
||||
const copyStepTimeout = 90 * time.Second
|
||||
|
||||
func (m *Manager) now() time.Time {
|
||||
if m.Now != nil {
|
||||
return m.Now()
|
||||
@@ -207,6 +212,11 @@ func causeText(cause string) string {
|
||||
// Ensure runs the order of the package comment once. why is a log word ("start", "daily", a trust cause). It returns
|
||||
// true when the running trust changed.
|
||||
func (m *Manager) Ensure(ctx context.Context, why string) bool {
|
||||
// One run at a time. Ensure is called from the start, the timer, a failed collect and after a bundle install; two
|
||||
// overlapping runs would both read the config, and the second would park the ALREADY-MOVED file as the "before"
|
||||
// copy (losing the fingerprint from the recovery copy). Found by review, 2026-11-14.
|
||||
m.runMu.Lock()
|
||||
defer m.runMu.Unlock()
|
||||
m.mu.Lock()
|
||||
cur := m.cur
|
||||
m.lastRun = m.now()
|
||||
@@ -230,8 +240,11 @@ func (m *Manager) Ensure(ctx context.Context, why string) bool {
|
||||
if m.EnsureCopy == nil || m.Probe == nil {
|
||||
return false
|
||||
}
|
||||
// 1. the copy
|
||||
c, err := m.EnsureCopy(ctx)
|
||||
// 1. the copy. The root step gets a deadline of its own: Ensure runs on the daemon's start path and inside the
|
||||
// report loop, and a wrapper that hangs (sudo, a stuck read of /etc/pve) must not stop either.
|
||||
cctx, ccancel := context.WithTimeout(ctx, copyStepTimeout)
|
||||
c, err := m.EnsureCopy(cctx)
|
||||
ccancel()
|
||||
if err != nil {
|
||||
lg.Warn("pvetrust: the root step gave no CA copy — the trust stays as it is", "err", err)
|
||||
set(modeOf(cur), ProblemCopyUnavailable, truncate(err.Error(), 240), nil)
|
||||
@@ -341,6 +354,8 @@ func truncate(s string, n int) string {
|
||||
// writeConfig sets proxmox.tls.ca_file and clears proxmox.tls.fingerprint in ONE write of agent.json. Every other key is
|
||||
// kept byte for byte. A recovery copy is parked in the state folder first; the result is read back and checked.
|
||||
func (m *Manager) writeConfig(caPath string) error {
|
||||
config.FileMu.Lock() // one writer of agent.json at a time in this process (config.FileMu)
|
||||
defer config.FileMu.Unlock()
|
||||
raw, err := os.ReadFile(m.ConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
Reference in new issue
Block a user