SyncKeys now returns the whole response so the poll can carry CollectPackages; a separate RPC for one boolean would be a message every 30 seconds for a value that changes when a licence does. The flag is an atomic: the 30s poll writes it, the hourly package loop reads it, and they are different goroutines.
91 lines
2.5 KiB
Go
91 lines
2.5 KiB
Go
package agentsync
|
|
|
|
import (
|
|
"log"
|
|
"runtime"
|
|
"sync/atomic"
|
|
|
|
"gitea.hostxtra.co.uk/mrhid6/vantage/agent/internal/config"
|
|
grpcclient "gitea.hostxtra.co.uk/mrhid6/vantage/agent/internal/grpc"
|
|
"gitea.hostxtra.co.uk/mrhid6/vantage/agent/internal/grpc/pb"
|
|
"gitea.hostxtra.co.uk/mrhid6/vantage/agent/internal/packages"
|
|
)
|
|
|
|
// collectPackagesFlag is written by the 30s key poll and read by the hourly
|
|
// package loop — two different goroutines, hence the atomic.
|
|
//
|
|
// It defaults to false, so an agent that has not yet completed a poll, or is
|
|
// talking to a server too old to send the field, collects nothing. Off is the
|
|
// safe default: collecting without a licence costs the customer storage they
|
|
// are not paying for.
|
|
var collectPackagesFlag atomic.Bool
|
|
|
|
func collectPackagesEnabled() bool { return collectPackagesFlag.Load() }
|
|
|
|
// reportPackages offers a hash of the installed package set and sends the full
|
|
// list only if the server does not already hold it.
|
|
//
|
|
// It runs on the same hourly cadence as the update check because a package set
|
|
// changes on roughly the same schedule, and reusing that loop means one timer
|
|
// rather than two.
|
|
func reportPackages(client *grpcclient.Client, cfg *config.Config) {
|
|
if runtime.GOOS != "linux" {
|
|
return
|
|
}
|
|
if !collectPackagesEnabled() {
|
|
return
|
|
}
|
|
|
|
osrel, pkgs, err := packages.Collect()
|
|
if err != nil {
|
|
log.Printf("package collection error: %v", err)
|
|
return
|
|
}
|
|
|
|
pbOS := pb.OSRelease{
|
|
Family: osrel.Family,
|
|
VersionId: osrel.VersionID,
|
|
Arch: osrel.Arch,
|
|
}
|
|
hash := packages.Hash(pkgs)
|
|
|
|
// The offer: hash only, no body. On an unchanged host this is the whole
|
|
// exchange, which is the point of the handshake.
|
|
needFull, err := client.ReportPackages(&pb.ReportPackagesRequest{
|
|
ServerId: cfg.ServerID,
|
|
AgentToken: cfg.AgentToken,
|
|
Hash: hash,
|
|
Os: pbOS,
|
|
})
|
|
if err != nil {
|
|
log.Printf("ReportPackages offer error: %v", err)
|
|
return
|
|
}
|
|
if !needFull {
|
|
return
|
|
}
|
|
|
|
pbPkgs := make([]pb.InstalledPackage, len(pkgs))
|
|
for i, p := range pkgs {
|
|
pbPkgs[i] = pb.InstalledPackage{
|
|
Name: p.Name,
|
|
Version: p.Version,
|
|
Epoch: int32(p.Epoch),
|
|
Arch: p.Arch,
|
|
SourceName: p.SourceName,
|
|
}
|
|
}
|
|
|
|
if _, err := client.ReportPackages(&pb.ReportPackagesRequest{
|
|
ServerId: cfg.ServerID,
|
|
AgentToken: cfg.AgentToken,
|
|
Hash: hash,
|
|
Os: pbOS,
|
|
Packages: pbPkgs,
|
|
}); err != nil {
|
|
log.Printf("ReportPackages full error: %v", err)
|
|
return
|
|
}
|
|
log.Printf("reported %d installed packages", len(pkgs))
|
|
}
|