package status import ( "context" "fmt" "net/http" "sync/atomic" "forge.cadoles.com/Cadoles/emissary/internal/agent" "forge.cadoles.com/Cadoles/emissary/internal/datastore" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/api" "gitlab.com/wpetit/goweb/logger" ) type Status struct { Agent *datastore.Agent Connected bool Claimed bool Thumbprint string ServerURL string ClaimURL string AgentURL string AgentVersion string } type Controller struct { status *atomic.Value server *atomic.Value addr string claimURL string agentURL string agentVersion string } // Name implements node.Controller. func (c *Controller) Name() string { return "status-controller" } // Reconcile implements node.Controller. func (c *Controller) Reconcile(ctx context.Context, state *agent.State) error { cl := agent.Client(ctx) thumbprint := agent.Thumbprint(ctx) connected := true agent, err := cl.GetAgent(ctx, state.AgentID()) if err != nil { logger.Error(ctx, "could not get agent", logger.E(errors.WithStack(err))) var apiErr *api.Error if errors.As(err, &apiErr) { switch apiErr.Code { case api.ErrCodeForbidden: // Contact is ok but agent may be not claimed yet default: connected = false } } else { connected = false } } claimed := agent != nil && agent.TenantID != nil var agentID datastore.AgentID if agent != nil { agentID = agent.ID } c.status.Store(Status{ Agent: agent, Connected: connected, Claimed: claimed, Thumbprint: thumbprint, ServerURL: cl.ServerURL(), ClaimURL: fmt.Sprintf(c.claimURL, thumbprint), AgentURL: fmt.Sprintf(c.agentURL, agentID), AgentVersion: c.agentVersion, }) if err := c.startServer(ctx); err != nil { return errors.WithStack(err) } return nil } func (c *Controller) startServer(ctx context.Context) error { server := c.getServer() if server != nil { return nil } server = &http.Server{ Addr: c.addr, Handler: &Handler{ status: c.status, }, } go func() { defer c.setServer(nil) if err := server.ListenAndServe(); err != nil { logger.Error(ctx, "could not start server", logger.E(errors.WithStack(err))) } }() c.setServer(server) return nil } func (c *Controller) setServer(s *http.Server) { c.server.Store(s) } func (c *Controller) getServer() *http.Server { server, ok := c.server.Load().(*http.Server) if !ok { return nil } return server } func NewController(addr string, claimURL string, agentURL string, agentVersion string) *Controller { return &Controller{ addr: addr, claimURL: claimURL, agentURL: agentURL, agentVersion: agentVersion, status: &atomic.Value{}, server: &atomic.Value{}, } } var _ agent.Controller = &Controller{}