feat: add auto update
This commit is contained in:
39
server/internal/scheduler/adapter.go
Normal file
39
server/internal/scheduler/adapter.go
Normal file
@ -0,0 +1,39 @@
|
||||
package scheduler
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/containarr/server/internal/store"
|
||||
)
|
||||
|
||||
// StoreAdapter wraps *store.Store so it satisfies StoreInterface.
|
||||
type StoreAdapter struct {
|
||||
s *store.Store
|
||||
}
|
||||
|
||||
// NewStoreAdapter creates a StoreAdapter wrapping the given *store.Store.
|
||||
func NewStoreAdapter(s *store.Store) *StoreAdapter {
|
||||
return &StoreAdapter{s: s}
|
||||
}
|
||||
|
||||
// ListDueAutoUpdatePolicies implements StoreInterface by converting
|
||||
// *store.AutoUpdatePolicy to DuePolicy.
|
||||
func (a *StoreAdapter) ListDueAutoUpdatePolicies(now time.Time) ([]DuePolicy, error) {
|
||||
policies, err := a.s.ListDueAutoUpdatePolicies(now)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make([]DuePolicy, 0, len(policies))
|
||||
for _, p := range policies {
|
||||
out = append(out, DuePolicy{
|
||||
AgentID: p.AgentID,
|
||||
ContainerID: p.ContainerID,
|
||||
})
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// UpdateAutoUpdateChecked implements StoreInterface.
|
||||
func (a *StoreAdapter) UpdateAutoUpdateChecked(agentID, containerID string, at time.Time) error {
|
||||
return a.s.UpdateAutoUpdateChecked(agentID, containerID, at)
|
||||
}
|
||||
86
server/internal/scheduler/scheduler.go
Normal file
86
server/internal/scheduler/scheduler.go
Normal file
@ -0,0 +1,86 @@
|
||||
package scheduler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
agentv1 "github.com/containarr/server/internal/proto/agentv1"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// DuePolicy is a minimal view of an auto-update policy returned by the store.
|
||||
type DuePolicy struct {
|
||||
AgentID string
|
||||
ContainerID string
|
||||
}
|
||||
|
||||
// StoreInterface defines the minimal store methods used by the scheduler.
|
||||
// Implementations must convert their internal policy type to DuePolicy when
|
||||
// implementing ListDueAutoUpdatePolicies, or use StoreAdapter provided below.
|
||||
type StoreInterface interface {
|
||||
ListDueAutoUpdatePolicies(now time.Time) ([]DuePolicy, error)
|
||||
UpdateAutoUpdateChecked(agentID, containerID string, at time.Time) error
|
||||
}
|
||||
|
||||
// RegistryInterface defines the minimal registry methods used by the scheduler.
|
||||
type RegistryInterface interface {
|
||||
Send(agentID string, msg *agentv1.ServerMessage) bool
|
||||
}
|
||||
|
||||
// Scheduler sends CheckUpdateCommand to agents every 60 seconds for containers
|
||||
// with an active and due auto-update policy.
|
||||
type Scheduler struct {
|
||||
store StoreInterface
|
||||
registry RegistryInterface
|
||||
}
|
||||
|
||||
// New creates a new Scheduler.
|
||||
func New(store StoreInterface, registry RegistryInterface) *Scheduler {
|
||||
return &Scheduler{store: store, registry: registry}
|
||||
}
|
||||
|
||||
// Start runs the scheduler loop until ctx is cancelled.
|
||||
func (s *Scheduler) Start(ctx context.Context) {
|
||||
ticker := time.NewTicker(60 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
slog.Info("scheduler stopped")
|
||||
return
|
||||
case t := <-ticker.C:
|
||||
s.tick(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Scheduler) tick(now time.Time) {
|
||||
policies, err := s.store.ListDueAutoUpdatePolicies(now)
|
||||
if err != nil {
|
||||
slog.Error("scheduler: list due policies", "err", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, p := range policies {
|
||||
cmdID := uuid.NewString()
|
||||
msg := &agentv1.ServerMessage{
|
||||
Payload: &agentv1.ServerMessage_CheckUpdate{
|
||||
CheckUpdate: &agentv1.CheckUpdateCommand{
|
||||
CommandId: cmdID,
|
||||
ContainerId: p.ContainerID,
|
||||
},
|
||||
},
|
||||
}
|
||||
sent := s.registry.Send(p.AgentID, msg)
|
||||
if !sent {
|
||||
slog.Debug("scheduler: agent not connected, skipping", "agent_id", p.AgentID, "container_id", p.ContainerID)
|
||||
continue
|
||||
}
|
||||
if err := s.store.UpdateAutoUpdateChecked(p.AgentID, p.ContainerID, now); err != nil {
|
||||
slog.Error("scheduler: update last_checked_at", "agent_id", p.AgentID, "container_id", p.ContainerID, "err", err)
|
||||
}
|
||||
slog.Info("scheduler: sent CheckUpdateCommand", "agent_id", p.AgentID, "container_id", p.ContainerID, "command_id", cmdID)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user