fix: Fixed agent collect packages
Chart Release / chart (push) Successful in 11s
Server Deploy / deploy (push) Successful in 21s
Agent Release / build (push) Successful in 58s
Agent Release / msi (push) Successful in 1m34s

This commit is contained in:
2026-08-06 16:20:34 +01:00
parent 5cee53dc5f
commit 6dced22499
2 changed files with 40 additions and 0 deletions
+35
View File
@@ -1,9 +1,12 @@
package agentsync
import (
"context"
"log"
"runtime"
"sync"
"sync/atomic"
"time"
"gitea.hostxtra.co.uk/mrhid6/vantage/agent/internal/config"
grpcclient "gitea.hostxtra.co.uk/mrhid6/vantage/agent/internal/grpc"
@@ -20,6 +23,38 @@ import (
// are not paying for.
var collectPackagesFlag atomic.Bool
// firstPoll closes once a SyncKeys response has set the flag above.
//
// Without it the boot-time package report loses a race it can only lose: the
// hourly loop starts before the first poll, reads a flag that is still false by
// construction, and skips — so a freshly installed agent reports no packages for
// an hour and the server shows nothing to scan.
// How long the boot package report waits for that first poll. Two poll
// intervals plus slack: long enough to cover one failed attempt, short enough
// that a dead control plane does not hold the OS-update report hostage.
const firstPollWait = 90 * time.Second
var (
firstPoll = make(chan struct{})
firstPollOnce sync.Once
)
func markFirstPoll() { firstPollOnce.Do(func() { close(firstPoll) }) }
// waitFirstPoll blocks until the flag is known, or gives up. The wait is
// bounded because this loop also reports OS updates, which do not depend on the
// flag at all — a control plane that cannot be polled must not silence those too.
func waitFirstPoll(ctx context.Context, limit time.Duration) {
t := time.NewTimer(limit)
defer t.Stop()
select {
case <-firstPoll:
case <-t.C:
log.Printf("package collection: no SyncKeys response within %s, collecting nothing this round", limit)
case <-ctx.Done():
}
}
func collectPackagesEnabled() bool { return collectPackagesFlag.Load() }
// reportPackages offers a hash of the installed package set and sends the full
+5
View File
@@ -101,6 +101,7 @@ func poll(client *grpcclient.Client, cfg *config.Config, version string) error {
// goroutine. Absent on the wire decodes as false, so an older server leaves
// collection off rather than on.
collectPackagesFlag.Store(resp.CollectPackages)
markFirstPoll()
desired := resp.PublicKeys
@@ -409,6 +410,10 @@ func runUpdateCheck(ctx context.Context, cfg *config.Config) {
reportPackages(client, cfg)
}
// The boot round only: after this the flag has long been set, and every
// later tick is an hour past a poll that runs every 30s.
waitFirstPoll(ctx, firstPollWait)
doCheck()
ticker := time.NewTicker(interval)
defer ticker.Stop()