93 lines
2.0 KiB
Go
93 lines
2.0 KiB
Go
package agent
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/hexahost/gamecloud/node-agent/internal/config"
|
|
"github.com/hexahost/gamecloud/node-agent/internal/health"
|
|
)
|
|
|
|
const Version = "0.1.0-phase0"
|
|
|
|
// Agent coordinates the node agent lifecycle until shutdown.
|
|
type Agent struct {
|
|
cfg *config.Config
|
|
log *slog.Logger
|
|
health *health.Server
|
|
}
|
|
|
|
// New creates an agent instance from configuration.
|
|
func New(cfg *config.Config, log *slog.Logger) *Agent {
|
|
return &Agent{
|
|
cfg: cfg,
|
|
log: log,
|
|
health: health.NewServer(cfg.HealthListenAddr, Version),
|
|
}
|
|
}
|
|
|
|
// Run starts the agent loop and blocks until context cancellation or signal.
|
|
func (a *Agent) Run(ctx context.Context) error {
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
defer cancel()
|
|
|
|
sigCh := make(chan os.Signal, 1)
|
|
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
|
defer signal.Stop(sigCh)
|
|
|
|
errCh := make(chan error, 1)
|
|
go func() {
|
|
a.log.Info("health server listening", "addr", a.cfg.HealthListenAddr)
|
|
if err := a.health.ListenAndServe(); err != nil {
|
|
errCh <- err
|
|
}
|
|
}()
|
|
|
|
a.health.SetReady(true)
|
|
a.log.Info("agent started",
|
|
"node_id", a.cfg.NodeID,
|
|
"control_plane", a.cfg.ControlPlaneURL,
|
|
"version", Version,
|
|
)
|
|
|
|
ticker := time.NewTicker(a.cfg.HeartbeatInterval)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return a.shutdown(ctx.Err())
|
|
case sig := <-sigCh:
|
|
a.log.Info("shutdown signal received", "signal", sig.String())
|
|
return a.shutdown(nil)
|
|
case err := <-errCh:
|
|
return err
|
|
case <-ticker.C:
|
|
a.log.Debug("heartbeat tick",
|
|
"node_id", a.cfg.NodeID,
|
|
"interval", a.cfg.HeartbeatInterval.String(),
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (a *Agent) shutdown(reason error) error {
|
|
a.health.SetReady(false)
|
|
a.log.Info("agent shutting down", "grace", a.cfg.ShutdownGracePeriod.String())
|
|
|
|
timer := time.NewTimer(a.cfg.ShutdownGracePeriod)
|
|
defer timer.Stop()
|
|
<-timer.C
|
|
|
|
if reason != nil && reason != context.Canceled {
|
|
a.log.Warn("shutdown with error", "error", reason)
|
|
return reason
|
|
}
|
|
a.log.Info("agent stopped")
|
|
return nil
|
|
}
|