feat: initial release
This commit is contained in:
parent
a3cf21f5bd
commit
761174d035
41 changed files with 2008 additions and 217 deletions
122
internal/roles/dns/dns.go
Normal file
122
internal/roles/dns/dns.go
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.wzray.com/homelab/mastermind/internal/config"
|
||||
"git.wzray.com/homelab/mastermind/internal/state"
|
||||
"git.wzray.com/homelab/mastermind/internal/types"
|
||||
"git.wzray.com/homelab/mastermind/internal/web/client"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const hostsDir = "/etc/hosts.d/"
|
||||
|
||||
type Role struct {
|
||||
state *state.RuntimeState
|
||||
config config.DnsConfig
|
||||
group sync.WaitGroup
|
||||
}
|
||||
|
||||
func New(state *state.RuntimeState, config config.DnsConfig) *Role {
|
||||
r := &Role{
|
||||
state: state,
|
||||
config: config,
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
func (r *Role) updateDnsmasq(filename string, data []byte) error {
|
||||
if err := os.WriteFile(filename, data, 0644); err != nil {
|
||||
return fmt.Errorf("write endpoint file %q: %w", filename, err)
|
||||
}
|
||||
|
||||
if err := r.reload(); err != nil {
|
||||
return fmt.Errorf("reload dnsmasq: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseState(state types.HostState) (string, []byte) {
|
||||
var builder strings.Builder
|
||||
|
||||
for _, d := range state.Domains {
|
||||
builder.WriteString(fmt.Sprintf("%s %s\n", state.Name, d))
|
||||
}
|
||||
|
||||
return hostsDir + state.Endpoint, []byte(builder.String())
|
||||
}
|
||||
|
||||
func (r *Role) OnStartup(ctx context.Context) error {
|
||||
r.group.Go(func() {
|
||||
r.syncFromRegistry()
|
||||
})
|
||||
|
||||
c := r.state.Registry.OnChanged()
|
||||
r.group.Go(func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-c:
|
||||
r.syncFromRegistry()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Role) syncFromRegistry() {
|
||||
for _, n := range r.state.Registry.ByRole(types.HostRole) {
|
||||
state, err := client.Get[types.HostState](n.Address, types.PathHostDns)
|
||||
if err != nil {
|
||||
log.Warn().Str("name", n.Name).Err(err).Msg("unable to get host config")
|
||||
continue
|
||||
}
|
||||
|
||||
filename, data := parseState(*state)
|
||||
if err := r.updateDnsmasq(filename, data); err != nil {
|
||||
log.Warn().Str("name", n.Name).Err(err).Msg("unable to update dnsmasq")
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Role) OnShutdown() error {
|
||||
r.group.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Role) reload() error {
|
||||
var err error
|
||||
|
||||
if r.config.UseSystemd {
|
||||
err = exec.Command("systemctl", "reload", "dnsmasq").Run()
|
||||
} else {
|
||||
err = exec.Command("/etc/init.d/dnsmasq", "reload").Run()
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Role) onCallback(state types.HostState) (bool, error) {
|
||||
filename, data := parseState(state)
|
||||
|
||||
if err := r.updateDnsmasq(filename, data); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *Role) RegisterHandlers(rg types.Registrator) {
|
||||
rg.Register(types.PostEndpoint(types.PathDnsCallback, r.onCallback))
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue