|
|
|
package cluster
|
|
|
|
|
|
|
|
import (
|
|
|
|
"io/fs"
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"darvaza.org/resolver"
|
|
|
|
"darvaza.org/slog"
|
|
|
|
)
|
|
|
|
|
|
|
|
// A ScanOption pre-configures the Zones before scanning
|
|
|
|
type ScanOption func(*Cluster, *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(_ *Cluster, 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 *Cluster, _ *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 *Cluster, _ *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 *Cluster, opt *ScanOptions) error {
|
|
|
|
if log == nil {
|
|
|
|
log = DefaultLogger()
|
|
|
|
}
|
|
|
|
|
|
|
|
opt.Logger = log
|
|
|
|
m.log = log
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Cluster) setScanDefaults(opt *ScanOptions) error {
|
|
|
|
if m.resolver == nil {
|
|
|
|
h := DefaultLookuper()
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewFromDirectory builds a [Cluster] tree using the given directory
|
|
|
|
func NewFromDirectory(dir, domain string, opts ...ScanOption) (*Cluster, error) {
|
|
|
|
var scanOptions ScanOptions
|
|
|
|
|
|
|
|
dir = filepath.Clean(dir)
|
|
|
|
fullPath, err := filepath.Abs(dir)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
sub, err := DirFS(dir)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
m := &Cluster{
|
|
|
|
dir: sub,
|
|
|
|
BaseDir: dir,
|
|
|
|
Name: filepath.Base(fullPath),
|
|
|
|
Domain: domain,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, opt := range opts {
|
|
|
|
if err := opt(m, &scanOptions); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := m.setScanDefaults(&scanOptions); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := m.scan(&scanOptions); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return m, nil
|
|
|
|
}
|