You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

135 lines
2.8 KiB

package zones
import (
"io/fs"
"path/filepath"
"darvaza.org/resolver"
"darvaza.org/slog"
"darvaza.org/slog/handlers/discard"
"github.com/hack-pad/hackpadfs/os"
)
// A ScanOption pre-configures the Zones before scanning
type ScanOption func(*Zones, *ScanOptions) error
// ScanOptions contains flags used by the initial scan
type ScanOptions struct {
// DontResolvePublicAddresses indicates we shouldn't
// pre-populate Machine.PublicAddresses during the
// initial scan
DontResolvePublicAddresses bool
// Logger specifies the logger to be used. otherwise
// the scanner will be mute
slog.Logger
}
// ResolvePublicAddresses instructs the scanner to use
// the DNS resolver to get PublicAddresses of nodes.
// Default is true
func ResolvePublicAddresses(resolve bool) ScanOption {
return func(m *Zones, opt *ScanOptions) error {
opt.DontResolvePublicAddresses = !resolve
return nil
}
}
// WithLookuper specifies what resolver.Lookuper to use to
// find public addresses
func WithLookuper(h resolver.Lookuper) ScanOption {
return func(m *Zones, opt *ScanOptions) error {
if h == nil {
return fs.ErrInvalid
}
m.resolver = resolver.NewResolver(h)
return nil
}
}
// WithResolver specifies what resolver to use to find
// public addresses. if nil is passed, the [net.Resolver] will be used.
// The default is using Cloudflare's 1.1.1.1.
func WithResolver(h resolver.Resolver) ScanOption {
return func(m *Zones, opt *ScanOptions) error {
if h == nil {
h = resolver.SystemResolver(true)
}
m.resolver = h
return nil
}
}
// WithLogger specifies what to use for logging
func WithLogger(log slog.Logger) ScanOption {
return func(m *Zones, opt *ScanOptions) error {
if log == nil {
log = discard.New()
}
opt.Logger = log
m.log = log
return nil
}
}
func (m *Zones) setDefaults(opt *ScanOptions) error {
if m.resolver == nil {
h := resolver.NewCloudflareLookuper()
if err := WithLookuper(h)(m, opt); err != nil {
return err
}
}
if opt.Logger == nil {
if err := WithLogger(nil)(m, opt); err != nil {
return err
}
}
return nil
}
// NewFS builds a [Zones] tree using the given directory
func NewFS(dir fs.FS, domain string, opts ...ScanOption) (*Zones, error) {
var scanOptions ScanOptions
z := &Zones{
dir: dir,
domain: domain,
}
for _, opt := range opts {
if err := opt(z, &scanOptions); err != nil {
return nil, err
}
}
if err := z.setDefaults(&scanOptions); err != nil {
return nil, err
}
if err := z.scan(&scanOptions); err != nil {
return nil, err
}
return z, nil
}
// New builds a [Zones] tree using the given directory
func New(dir, domain string, opts ...ScanOption) (*Zones, error) {
dir, err := filepath.Abs(dir)
if err != nil {
return nil, err
}
base, err := os.NewFS().Sub(dir[1:])
if err != nil {
return nil, err
}
return NewFS(base, domain, opts...)
}