feat: add auto update

This commit is contained in:
2026-05-19 15:53:30 +02:00
parent bd3121d688
commit ba4de62a34
22 changed files with 3323 additions and 110 deletions

View File

@ -1,6 +1,8 @@
package grpc
import (
"context"
"fmt"
"sync"
"time"
@ -20,7 +22,10 @@ type AgentState struct {
Volumes []*agentv1.VolumeInfo
Networks []*agentv1.NetworkInfo
cmdCh chan *agentv1.ServerMessage
cmdCh chan *agentv1.ServerMessage
pendingFiles map[string]chan *agentv1.FileResult
pendingUpdates map[string]string // commandID → containerID
pendingMu sync.Mutex
}
type Registry struct {
@ -34,13 +39,15 @@ func NewRegistry() *Registry {
func (r *Registry) Register(id, hostname, alias, ipAddress, arch, os string) *AgentState {
state := &AgentState{
ID: id,
Hostname: hostname,
Alias: alias,
IPAddress: ipAddress,
Arch: arch,
OS: os,
cmdCh: make(chan *agentv1.ServerMessage, 16),
ID: id,
Hostname: hostname,
Alias: alias,
IPAddress: ipAddress,
Arch: arch,
OS: os,
cmdCh: make(chan *agentv1.ServerMessage, 16),
pendingFiles: make(map[string]chan *agentv1.FileResult),
pendingUpdates: make(map[string]string),
}
r.mu.Lock()
r.agents[id] = state
@ -118,3 +125,113 @@ func (r *Registry) Send(agentID string, msg *agentv1.ServerMessage) bool {
return false
}
}
// RegisterPending registers a channel waiting for a FileResult with the given cmdID.
func (r *Registry) RegisterPending(agentID, cmdID string) chan *agentv1.FileResult {
r.mu.RLock()
s, ok := r.agents[agentID]
r.mu.RUnlock()
if !ok {
return nil
}
ch := make(chan *agentv1.FileResult, 1)
s.pendingMu.Lock()
s.pendingFiles[cmdID] = ch
s.pendingMu.Unlock()
return ch
}
// ResolvePending sends the FileResult to the waiting channel identified by cmdID.
func (r *Registry) ResolvePending(agentID, cmdID string, result *agentv1.FileResult) {
r.mu.RLock()
s, ok := r.agents[agentID]
r.mu.RUnlock()
if !ok {
return
}
s.pendingMu.Lock()
ch, ok := s.pendingFiles[cmdID]
if ok {
delete(s.pendingFiles, cmdID)
}
s.pendingMu.Unlock()
if ok {
select {
case ch <- result:
default:
}
}
}
// CancelPending removes the pending channel for cmdID (cleanup on timeout).
func (r *Registry) CancelPending(agentID, cmdID string) {
r.mu.RLock()
s, ok := r.agents[agentID]
r.mu.RUnlock()
if !ok {
return
}
s.pendingMu.Lock()
delete(s.pendingFiles, cmdID)
s.pendingMu.Unlock()
}
// SendAndWait registers a pending channel, sends msg to the agent, and waits up
// to 30 seconds for the FileResult response identified by cmdID.
func (r *Registry) SendAndWait(agentID string, msg *agentv1.ServerMessage, cmdID string) (*agentv1.FileResult, error) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
return r.SendAndWaitCtx(ctx, agentID, msg, cmdID)
}
// RegisterPendingUpdate enregistre un commandID en attente de CommandResult pour un UpdateContainer.
func (r *Registry) RegisterPendingUpdate(agentID, cmdID, containerID string) {
r.mu.RLock()
s, ok := r.agents[agentID]
r.mu.RUnlock()
if !ok {
return
}
s.pendingMu.Lock()
s.pendingUpdates[cmdID] = containerID
s.pendingMu.Unlock()
}
// ResolvePendingUpdate retourne le containerID associé au commandID et le supprime de la map.
// Retourne ("", false) si le commandID n'est pas connu.
func (r *Registry) ResolvePendingUpdate(agentID, cmdID string) (string, bool) {
r.mu.RLock()
s, ok := r.agents[agentID]
r.mu.RUnlock()
if !ok {
return "", false
}
s.pendingMu.Lock()
containerID, found := s.pendingUpdates[cmdID]
if found {
delete(s.pendingUpdates, cmdID)
}
s.pendingMu.Unlock()
return containerID, found
}
// SendAndWaitCtx is like SendAndWait but uses the provided context for timeout control.
func (r *Registry) SendAndWaitCtx(ctx context.Context, agentID string, msg *agentv1.ServerMessage, cmdID string) (*agentv1.FileResult, error) {
ch := r.RegisterPending(agentID, cmdID)
if ch == nil {
return nil, fmt.Errorf("agent not connected")
}
if !r.Send(agentID, msg) {
r.CancelPending(agentID, cmdID)
return nil, fmt.Errorf("agent not connected")
}
select {
case result := <-ch:
return result, nil
case <-ctx.Done():
r.CancelPending(agentID, cmdID)
return nil, fmt.Errorf("timeout waiting for agent response")
}
}