1
0
Fork 0

feat: initial release

This commit is contained in:
Arthur K. 2026-01-17 18:14:50 +03:00
parent a3cf21f5bd
commit 1e0ee5bffe
Signed by: wzray
GPG key ID: B97F30FDC4636357
40 changed files with 2007 additions and 217 deletions

View file

@ -0,0 +1,7 @@
package main
import "fmt"
func main() {
fmt.Println("hivemind lite")
}

169
cmd/hivemind/main.go Normal file
View file

@ -0,0 +1,169 @@
package main
import (
"context"
"fmt"
"os"
"os/signal"
"path/filepath"
"strconv"
"syscall"
"git.wzray.com/homelab/mastermind/internal/config"
"git.wzray.com/homelab/mastermind/internal/registry"
"git.wzray.com/homelab/mastermind/internal/roles"
"git.wzray.com/homelab/mastermind/internal/roles/dns"
"git.wzray.com/homelab/mastermind/internal/roles/host"
"git.wzray.com/homelab/mastermind/internal/roles/master"
"git.wzray.com/homelab/mastermind/internal/roles/node"
"git.wzray.com/homelab/mastermind/internal/state"
"git.wzray.com/homelab/mastermind/internal/types"
"git.wzray.com/homelab/mastermind/internal/web/client"
"git.wzray.com/homelab/mastermind/internal/web/middleware"
"git.wzray.com/homelab/mastermind/internal/web/server"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/rs/zerolog/pkgerrors"
)
var (
configFile = "/etc/hivemind/config.toml"
registryFile = "/var/lib/hivemind/registry"
)
func levelToZerolog(l config.LogLevel) zerolog.Level {
switch l {
case config.LogLevelDebug:
return zerolog.DebugLevel
case config.LogLevelInfo:
return zerolog.InfoLevel
case config.LogLevelWarn:
return zerolog.WarnLevel
case config.LogLevelError:
return zerolog.ErrorLevel
default:
panic("unreachable")
}
}
func init() {
zerolog.CallerMarshalFunc = func(pc uintptr, file string, line int) string {
return filepath.Base(file) + ":" + strconv.Itoa(line)
}
log.Logger = log.With().Caller().Stack().Logger()
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
if e := os.Getenv("HIVEMIND_CONFIG_FILE"); e != "" {
configFile = e
}
if e := os.Getenv("HIVEMIND_REGISTRY_FILE"); e != "" {
registryFile = e
}
}
func main() {
config, err := config.FromFile(configFile)
if err != nil {
log.Fatal().Err(err).Msg("unable to read config file")
}
if err := config.Validate(); err != nil {
log.Fatal().Err(err).Msg("invalid configuration")
}
zerolog.SetGlobalLevel(levelToZerolog(config.Node.LogLevel))
self := types.NewNode(
fmt.Sprintf("%v:%v", config.Node.Endpoint, config.Node.Port),
config.Node.Hostname,
config.Roles,
)
filestore := registry.NewFileStorage(registryFile)
filestore.EnsureExists()
registry := registry.New(filestore, self)
state := state.New(registry, self)
nodeRole := node.New(state, config.Node)
var builder middleware.MiddlewareBuilder
middlewares := builder.Prepare()
client.Init(middlewares)
listenAddr := fmt.Sprintf("%v:%v", config.Node.ListenOn, config.Node.Port)
server := server.NewServer(listenAddr, middlewares)
roles := make([]roles.Role, 0)
roles = append(roles, nodeRole)
for _, role := range config.Roles {
switch role {
case types.MasterRole:
role := master.New(state, config.Configs.Master)
roles = append(roles, role)
case types.DnsRole:
role := dns.New(state, config.Configs.Dns)
roles = append(roles, role)
case types.HostRole:
role := host.New(state, config.Configs.Host)
roles = append(roles, role)
}
}
for _, role := range roles {
role.RegisterHandlers(server)
}
serverError := make(chan error)
go func() {
log.Info().Str("addr", listenAddr).Msg("started listening")
serverError <- server.Listen()
}()
if err := nodeRole.Join(config.Node.BootstrapMaster); err != nil {
log.Warn().Err(err).Msg("unable to join")
} else {
log.Info().Msg("joined")
}
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
for _, role := range roles {
logger := log.With().Str("role", fmt.Sprintf("%T", role)).Logger()
logger.Debug().Msg("running OnStartup handler")
if err := role.OnStartup(ctx); err != nil {
logger.Err(err).Msg("failed to initialize role")
}
}
log.Debug().Msg("finished role startup")
select {
case err := <-serverError:
log.Err(err).Send()
case <-ctx.Done():
log.Info().Msg("got stop signal")
}
cancel()
if err := server.Shutdown(context.Background()); err != nil {
log.Err(err).Msg("error while shutting down the server")
}
if err := nodeRole.Leave(); err != nil {
log.Info().Err(err).Msg("error while sending shutdown packet")
}
for _, role := range roles {
if err := role.OnShutdown(); err != nil {
log.Err(err).Interface("role", role).Msg("failed to shutdown role")
}
}
registry.Save()
}