1
0
Fork 0
hivemind/internal/roles/host/host.go

125 lines
2.9 KiB
Go

package host
import (
"context"
"fmt"
"slices"
"sync"
"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/host"
"git.wzray.com/homelab/hivemind/internal/types"
"github.com/rs/zerolog/log"
)
type Role struct {
state *app.State
config config.HostConfig
gateway *TraefikGateway
tasksGroup sync.WaitGroup
externalDomains []string // TODO: i don't like hardcoding external/internal logic here
internalDomains []string
}
func New(state *app.State, config config.HostConfig) *Role {
r := &Role{
state: state,
config: config,
}
r.gateway = NewTraefikGateway(config, r)
return r
}
func (r *Role) sendUpdate(domains []string, role types.Role) {
state := types.HostState{
Domains: domains,
Address: r.config.IpAddress,
Hostname: r.state.Self.Hostname,
}
for _, node := range r.state.Registry.ByRole(role) {
r.tasksGroup.Go(func() {
logger := log.With().Str("name", node.Hostname).Logger()
logger.Debug().Msg("sending update")
if _, err := r.state.Clients.DNS.Callback(node.Endpoint, state); err != nil {
logger.Warn().Err(err).Msg("unable to send dns info")
} else {
logger.Debug().Msg("update sent")
}
})
}
}
func (r *Role) OnTraefikUpdate(resp traefikResponse) {
newInternal := resp.Domains(r.config.InternalEntrypoint)
newExternal := resp.Domains(r.config.ExternalEntrypoint)
if !slices.Equal(newInternal, r.internalDomains) {
log.Info().Msg("internal domains updated, propogating")
r.internalDomains = newInternal
r.sendUpdate(newInternal, types.DnsRole)
}
if !slices.Equal(newExternal, r.externalDomains) {
log.Info().Msg("internal domains updated, propogating")
r.externalDomains = newExternal
r.sendUpdate(newExternal, types.NameserverRole)
}
}
func (r *Role) Dns() (types.HostState, error) {
return types.HostState{
Domains: r.internalDomains,
Address: r.config.IpAddress,
Hostname: r.state.Self.Hostname,
}, nil
}
func (r *Role) Nameserver() (types.HostState, error) {
return types.HostState{
Domains: r.externalDomains,
Address: r.config.IpAddress,
Hostname: r.state.Self.Hostname,
}, nil
}
func (r *Role) RegisterHandlers(rg transport.Registrator) {
host.Register(rg, r)
}
func (r *Role) OnStartup(ctx context.Context) error {
r.tasksGroup.Go(func() {
if err := r.gateway.Listen(); err != nil {
log.Err(err).Msg("traefik gateway stopped")
}
})
r.tasksGroup.Go(func() {
<-ctx.Done()
if err := r.gateway.Shutdown(context.Background()); err != nil {
log.Err(err).Msg("failed to shutdown traefik gateway")
}
})
resp, err := r.gateway.GetRawData()
if err != nil {
return fmt.Errorf("get traefik state: %w", err)
}
log.Info().Msg("got raw data from traefik")
log.Debug().Interface("response", resp).Send()
r.OnTraefikUpdate(*resp)
return nil
}
func (r *Role) OnShutdown() error {
r.tasksGroup.Wait()
return nil
}