144 lines
3.3 KiB
Go
144 lines
3.3 KiB
Go
package node
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"git.wzray.com/homelab/hivemind/internal/app"
|
|
"git.wzray.com/homelab/hivemind/internal/config"
|
|
"git.wzray.com/homelab/hivemind/internal/transport"
|
|
"git.wzray.com/homelab/hivemind/internal/transport/node"
|
|
"git.wzray.com/homelab/hivemind/internal/types"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
type Role struct {
|
|
state *app.State
|
|
keepaliveGroup sync.WaitGroup
|
|
config config.NodeConfig
|
|
}
|
|
|
|
func New(state *app.State, config config.NodeConfig) *Role {
|
|
return &Role{
|
|
state: state,
|
|
config: config,
|
|
}
|
|
}
|
|
|
|
func (r *Role) OnStartup(ctx context.Context) error {
|
|
r.keepaliveGroup.Go(r.keepaliveFunc(ctx))
|
|
return nil
|
|
}
|
|
|
|
func (r *Role) OnShutdown() error {
|
|
r.keepaliveGroup.Wait()
|
|
return nil
|
|
}
|
|
|
|
func (n *Role) RegisterHandlers(r transport.Registrator) {
|
|
node.Register(r, n)
|
|
}
|
|
|
|
func (r *Role) Join(bootstrap string) error {
|
|
masters := make(map[string]struct{})
|
|
for _, node := range r.state.Registry.ByRole(types.MasterRole) {
|
|
if node.Hostname == r.state.Self.Hostname {
|
|
continue
|
|
}
|
|
masters[node.Endpoint] = struct{}{}
|
|
}
|
|
if bootstrap != "" {
|
|
masters[bootstrap] = struct{}{}
|
|
} else if len(masters) == 0 {
|
|
return errors.New("no masters configured")
|
|
}
|
|
|
|
var errs []error
|
|
for m := range masters {
|
|
logger := log.With().Str("host", m).Logger()
|
|
logger.Debug().Msg("trying to join via master")
|
|
|
|
nodes, err := r.state.Clients.Master.Join(m, r.state.Self)
|
|
if err != nil {
|
|
errs = append(errs, err)
|
|
logger.Debug().Err(err).Msg("unable to join")
|
|
continue
|
|
}
|
|
|
|
if err := r.state.Registry.Set(nodes); err != nil {
|
|
logger.Debug().Err(err).Msg("unable to set master's nodes")
|
|
errs = append(errs, err)
|
|
continue
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("unable to join with any master: %w", errors.Join(errs...))
|
|
}
|
|
|
|
func (r *Role) Leave() error {
|
|
masters := r.state.Registry.ByRole(types.MasterRole)
|
|
if len(masters) == 0 {
|
|
return nil
|
|
}
|
|
|
|
var errs []error
|
|
for _, m := range masters {
|
|
logger := log.With().Str("name", m.Hostname).Logger()
|
|
logger.Debug().Msg("sending leave message")
|
|
|
|
if err := r.state.Clients.Master.Leave(m.Endpoint, r.state.Self); err != nil {
|
|
logger.Debug().Err(err).Msg("unable to send leave message")
|
|
errs = append(errs, err)
|
|
continue
|
|
} else {
|
|
logger.Debug().Msg("leave message sent")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("unable to send leave message to any master: %w", errors.Join(errs...))
|
|
}
|
|
|
|
func (r *Role) Healthcheck() (string, error) {
|
|
return "OK", nil
|
|
}
|
|
|
|
func (r *Role) keepaliveFunc(ctx context.Context) func() {
|
|
sendKeepalive := func() {
|
|
for _, m := range r.state.Registry.ByRole(types.MasterRole) {
|
|
logger := log.With().Str("name", m.Hostname).Logger()
|
|
logger.Debug().Msg("sending keepalive packet")
|
|
|
|
nodes, err := r.state.Clients.Master.Heartbeat(m.Endpoint, r.state.Self)
|
|
if err != nil {
|
|
logger.Info().Err(err).Msg("unable to send keepalive packet")
|
|
continue
|
|
}
|
|
|
|
logger.Debug().Msg("keepalive packet sent")
|
|
|
|
if err := r.state.Registry.Set(nodes); err != nil {
|
|
logger.Warn().Err(err).Msg("unable to set masters nodes")
|
|
continue
|
|
}
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
return func() {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-time.After(time.Duration(r.config.KeepaliveInterval) * time.Second):
|
|
sendKeepalive()
|
|
}
|
|
}
|
|
}
|
|
}
|