Skip to content
This repository was archived by the owner on Aug 24, 2024. It is now read-only.

Commit

Permalink
WIP status updates
Browse files Browse the repository at this point in the history
  • Loading branch information
nint8835 committed Feb 25, 2024
1 parent d6bd057 commit 350bf63
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 0 deletions.
7 changes: 7 additions & 0 deletions pkg/config/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@ package config

import (
"fmt"
"time"
)

type ServerConfig struct {
SharedConfig

// ListenAddress is the address to listen on
ListenAddress string `split_words:"true" default:":8080"`

// UnhealthyTime is the amount of time since last ping before a host is marked unhealthy
UnhealthyTime time.Duration `split_words:"true" default:"5m"`

// UpdateFrequency is the amount of time between status updates
UpdateFrequency time.Duration `split_words:"true" default:"1m"`
}

func LoadServerConfig() (*ServerConfig, error) {
Expand Down
82 changes: 82 additions & 0 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package server

import (
"context"
"net/http"
"sync"
"time"

"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
"github.com/rs/zerolog/log"

"github.com/nint8835/instatus-cluster-monitor/pkg/config"
)
Expand All @@ -18,6 +21,21 @@ func (v *Validator) Validate(i any) error {
return v.validator.Struct(i)
}

type Status string

const (
StatusNone Status = ""
StatusHealthy Status = "healthy"
StatusUnhealthy Status = "unhealthy"
)

type HostStatus struct {
Status Status `json:"status"`
LastStatus Status `json:"-"`

ReportedAt time.Time `json:"reported_at"`
}

type Server struct {
config *config.ServerConfig
echoInst *echo.Echo
Expand All @@ -26,9 +44,62 @@ type Server struct {
}

func (s *Server) Start() error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

go s.monitorStatuses(ctx)

return s.echoInst.Start(s.config.ListenAddress)
}

func (s *Server) monitorStatuses(ctx context.Context) {
ticker := time.NewTicker(s.config.UpdateFrequency)
defer ticker.Stop()

for {
select {
case <-ctx.Done():
log.Debug().Msg("Stopping status monitor")
return
case <-ticker.C:
log.Debug().Msg("Updating statuses")

s.statuses.Range(func(key any, value any) bool {
identifier := key.(string)
status := value.(*HostStatus)

if time.Since(status.ReportedAt) >= s.config.UnhealthyTime {
status.Status = StatusUnhealthy
}

if status.Status == status.LastStatus {
return true
}

log.Info().
Str("identifier", identifier).
Str("last_status", string(status.LastStatus)).
Str("status", string(status.Status)).
Msg("Host status changed")

status.LastStatus = status.Status
return true
})
}
}
}

func (s *Server) getStatuses(c echo.Context) error {
statuses := map[string]*HostStatus{}

s.statuses.Range(func(key any, value any) bool {
statuses[key.(string)] = value.(*HostStatus)
return true
})

return c.JSON(http.StatusOK, statuses)
}

type PingBody struct {
Identifier string `json:"identifier" validate:"required"`
}
Expand All @@ -48,6 +119,16 @@ func (s *Server) handlePing(c echo.Context) error {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}

existingStatus, hasStatus := s.statuses.Load(body.Identifier)
if !hasStatus {
s.statuses.Store(body.Identifier, &HostStatus{ReportedAt: time.Now(), Status: StatusHealthy})
return c.JSON(http.StatusOK, map[string]any{})
}

status := existingStatus.(*HostStatus)
status.ReportedAt = time.Now()
status.Status = StatusHealthy

return c.JSON(http.StatusOK, map[string]any{})
}

Expand All @@ -61,6 +142,7 @@ func New(c *config.ServerConfig) *Server {
echoInst.HideBanner = true
echoInst.Validator = &Validator{validator: validator.New()}

echoInst.GET("/statuses", serverInst.getStatuses)
echoInst.POST("/ping", serverInst.requireAuth(serverInst.handlePing))

return serverInst
Expand Down

0 comments on commit 350bf63

Please sign in to comment.