Alejandro Mery
1 year ago
4 changed files with 290 additions and 1 deletions
@ -1,2 +1,38 @@
|
||||
// Package dns manages DNS entries for the cluster
|
||||
package dns |
||||
|
||||
import ( |
||||
"fmt" |
||||
"net/netip" |
||||
) |
||||
|
||||
// Zone represents a set of hosts with high affinity
|
||||
type Zone struct { |
||||
Name string |
||||
|
||||
Hosts map[int]*Host |
||||
} |
||||
|
||||
func (z *Zone) String() string { |
||||
if z == nil { |
||||
return "undetermined" |
||||
} |
||||
return z.Name |
||||
} |
||||
|
||||
// Host represents a member of the cluster
|
||||
type Host struct { |
||||
zone *Zone |
||||
|
||||
ID int |
||||
Active bool |
||||
Addrs []netip.Addr |
||||
} |
||||
|
||||
func (p *Host) String() string { |
||||
if p == nil { |
||||
return "undetermined" |
||||
} |
||||
|
||||
return fmt.Sprintf("%s-%v", p.zone, p.ID) |
||||
} |
||||
|
@ -0,0 +1,173 @@
|
||||
package dns |
||||
|
||||
import ( |
||||
"context" |
||||
"errors" |
||||
"io/fs" |
||||
"net/netip" |
||||
"strings" |
||||
|
||||
"darvaza.org/core" |
||||
"darvaza.org/slog" |
||||
"git.jpi.io/amery/jpictl/pkg/cluster" |
||||
"golang.org/x/net/publicsuffix" |
||||
) |
||||
|
||||
// Manager is a DNS Manager instance
|
||||
type Manager struct { |
||||
domain string |
||||
suffix string |
||||
zones map[string]*Zone |
||||
regions map[string][]string |
||||
|
||||
p Provider |
||||
l slog.Logger |
||||
} |
||||
|
||||
// ManagerOption configures a Manager
|
||||
type ManagerOption func(*Manager) error |
||||
|
||||
func newErrorManagerOption(err error, hint string) ManagerOption { |
||||
return func(*Manager) error { |
||||
return core.Wrap(err, hint) |
||||
} |
||||
} |
||||
|
||||
// WithProvider attaches a libdns Provider to the Manager
|
||||
func WithProvider(p Provider) ManagerOption { |
||||
var err error |
||||
|
||||
if p == nil { |
||||
p, err = DefaultDNSProvider() |
||||
} |
||||
|
||||
if err != nil { |
||||
return newErrorManagerOption(err, "WithProvider") |
||||
} |
||||
|
||||
return func(mgr *Manager) error { |
||||
mgr.p = p |
||||
return nil |
||||
} |
||||
} |
||||
|
||||
// WithLogger attaches a logger to the Manager
|
||||
func WithLogger(log slog.Logger) ManagerOption { |
||||
if log == nil { |
||||
log = cluster.DefaultLogger() |
||||
} |
||||
|
||||
return func(mgr *Manager) error { |
||||
mgr.l = log |
||||
return nil |
||||
} |
||||
} |
||||
|
||||
func (mgr *Manager) setDefaults() error { |
||||
var opts []ManagerOption |
||||
|
||||
if mgr.l == nil { |
||||
opts = append(opts, WithLogger(nil)) |
||||
} |
||||
|
||||
if mgr.domain == "" || mgr.suffix == "" { |
||||
return errors.New("domain not specified") |
||||
} |
||||
|
||||
for _, opt := range opts { |
||||
if err := opt(mgr); err != nil { |
||||
return err |
||||
} |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// WithDomain specifies where the manager operates
|
||||
func WithDomain(domain string) ManagerOption { |
||||
base, err := publicsuffix.EffectiveTLDPlusOne(domain) |
||||
if err != nil { |
||||
return newErrorManagerOption(err, "publicsuffix") |
||||
} |
||||
|
||||
suffix := strings.TrimSuffix(domain, base) |
||||
if suffix != "" { |
||||
suffix = "." + suffix[:len(suffix)-1] |
||||
} |
||||
|
||||
return func(mgr *Manager) error { |
||||
mgr.domain = base |
||||
mgr.suffix = suffix |
||||
return nil |
||||
} |
||||
} |
||||
|
||||
// NewManager creates a DNS manager with the
|
||||
func NewManager(opts ...ManagerOption) (*Manager, error) { |
||||
mgr := &Manager{ |
||||
zones: make(map[string]*Zone), |
||||
regions: make(map[string][]string), |
||||
} |
||||
|
||||
for _, opt := range opts { |
||||
if err := opt(mgr); err != nil { |
||||
return nil, err |
||||
} |
||||
} |
||||
|
||||
if err := mgr.setDefaults(); err != nil { |
||||
return nil, err |
||||
} |
||||
return mgr, nil |
||||
} |
||||
|
||||
// AddHost registers a host
|
||||
func (mgr *Manager) AddHost(_ context.Context, zone string, id int, |
||||
active bool, addrs ...netip.Addr) error { |
||||
//
|
||||
if zone == "" || id <= 0 { |
||||
return fs.ErrInvalid |
||||
} |
||||
|
||||
z, ok := mgr.zones[zone] |
||||
if !ok { |
||||
z = &Zone{ |
||||
Name: zone, |
||||
Hosts: make(map[int]*Host), |
||||
} |
||||
|
||||
mgr.zones[zone] = z |
||||
} |
||||
|
||||
p := &Host{ |
||||
zone: z, |
||||
ID: id, |
||||
Active: active, |
||||
Addrs: SortAddrSlice(addrs), |
||||
} |
||||
z.Hosts[id] = p |
||||
|
||||
if log, ok := mgr.l.Debug().WithEnabled(); ok { |
||||
log.WithField("subsystem", "dns"). |
||||
WithField("zone", zone). |
||||
WithField("host", p.String()). |
||||
WithField("active", active). |
||||
Print() |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
// AddRegion specifies a new region and the zones it contains
|
||||
func (mgr *Manager) AddRegion(_ context.Context, region string, zones ...string) error { |
||||
mgr.regions[region] = append(mgr.regions[region], zones...) |
||||
|
||||
if log, ok := mgr.l.Debug().WithEnabled(); ok { |
||||
for _, zoneName := range zones { |
||||
log.WithField("subsystem", "dns"). |
||||
WithField("region", region). |
||||
WithField("zone", zoneName).Print() |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
Loading…
Reference in new issue