From db3c47693089b31be3ef7c20bad71427c29dff97 Mon Sep 17 00:00:00 2001 From: Alejandro Mery Date: Sun, 10 Sep 2023 18:40:13 +0000 Subject: [PATCH] DNS [WIP] Signed-off-by: Alejandro Mery --- cmd/jpictl/dns.go | 48 ++++++++++++++++ go.mod | 2 + go.sum | 5 ++ pkg/cluster/defaults.go | 5 ++ pkg/dns/dns.go | 12 ++++ pkg/dns/manager.go | 121 ++++++++++++++++++++++++++++++++++++++++ pkg/dns/provider.go | 36 ++++++++++++ 7 files changed, 229 insertions(+) create mode 100644 cmd/jpictl/dns.go create mode 100644 pkg/dns/dns.go create mode 100644 pkg/dns/manager.go create mode 100644 pkg/dns/provider.go diff --git a/cmd/jpictl/dns.go b/cmd/jpictl/dns.go new file mode 100644 index 0000000..d478059 --- /dev/null +++ b/cmd/jpictl/dns.go @@ -0,0 +1,48 @@ +package main + +import ( + "github.com/spf13/cobra" +) + +// Command +var dnsCmd = &cobra.Command{ + Use: "dns", +} + +var dnsSyncCmd = &cobra.Command{ + Use: "sync", + RunE: func(_ *cobra.Command, _ []string) error { + _, err := cfg.LoadZones(true) + return err + }, +} + +var dnsWriteCmd = &cobra.Command{ + Use: "write", + RunE: func(_ *cobra.Command, _ []string) error { + return nil + }, +} + +var dnsAddCmd = &cobra.Command{ + Use: "add", + RunE: func(_ *cobra.Command, _ []string) error { + return nil + }, +} + +var dnsRemoveCmd = &cobra.Command{ + Use: "remove", + RunE: func(_ *cobra.Command, _ []string) error { + return nil + }, +} + +func init() { + rootCmd.AddCommand(dnsCmd) + + dnsCmd.AddCommand(dnsSyncCmd) + dnsCmd.AddCommand(dnsWriteCmd) + dnsCmd.AddCommand(dnsAddCmd) + dnsCmd.AddCommand(dnsRemoveCmd) +} diff --git a/go.mod b/go.mod index d132e34..888ec0d 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,8 @@ require ( darvaza.org/slog/handlers/discard v0.4.5 github.com/gofrs/uuid/v5 v5.0.0 github.com/hack-pad/hackpadfs v0.2.1 + github.com/libdns/cloudflare v0.1.0 + github.com/libdns/libdns v0.2.1 github.com/mgechev/revive v1.3.3 github.com/spf13/cobra v1.7.0 golang.org/x/crypto v0.12.0 diff --git a/go.sum b/go.sum index c320b57..8aed74f 100644 --- a/go.sum +++ b/go.sum @@ -36,6 +36,11 @@ github.com/hack-pad/hackpadfs v0.2.1 h1:FelFhIhv26gyjujoA/yeFO+6YGlqzmc9la/6iKMI github.com/hack-pad/hackpadfs v0.2.1/go.mod h1:khQBuCEwGXWakkmq8ZiFUvUZz84ZkJ2KNwKvChs4OrU= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/libdns/cloudflare v0.1.0 h1:93WkJaGaiXCe353LHEP36kAWCUw0YjFqwhkBkU2/iic= +github.com/libdns/cloudflare v0.1.0/go.mod h1:a44IP6J1YH6nvcNl1PverfJviADgXUnsozR3a7vBKN8= +github.com/libdns/libdns v0.2.0/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= +github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis= +github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= diff --git a/pkg/cluster/defaults.go b/pkg/cluster/defaults.go index 925b3d5..fbc2140 100644 --- a/pkg/cluster/defaults.go +++ b/pkg/cluster/defaults.go @@ -15,3 +15,8 @@ func DefaultLogger() slog.Logger { func DefaultLookuper() resolver.Lookuper { return resolver.NewCloudflareLookuper() } + +// DefaultResolver returns a [resolver.Resolver] using Cloudflare's 1.1.1.1 +func DefaultResolver() resolver.Resolver { + return resolver.NewResolver(DefaultLookuper()) +} diff --git a/pkg/dns/dns.go b/pkg/dns/dns.go new file mode 100644 index 0000000..2a31127 --- /dev/null +++ b/pkg/dns/dns.go @@ -0,0 +1,12 @@ +// Package dns manages DNS entries for the cluster +package dns + +// A Region describes where a Zone is +type Region struct { + // Name is the identifier of this Region + Name string + // Regions are a list of (sub)regions that belong to this Region + Regions []string + // Zones are a list of Zones that directly belong to this Region + Zones []string +} diff --git a/pkg/dns/manager.go b/pkg/dns/manager.go new file mode 100644 index 0000000..9ef8093 --- /dev/null +++ b/pkg/dns/manager.go @@ -0,0 +1,121 @@ +package dns + +import ( + "darvaza.org/core" + "darvaza.org/resolver" + "darvaza.org/slog" + + "git.jpi.io/amery/jpictl/pkg/cluster" +) + +// Manager is a DNS Manager instance +type Manager struct { + p Provider + r resolver.Resolver + l slog.Logger +} + +// ManagerOption configures a Manager +type ManagerOption func(*Manager) error + +func newErrorManagerOption(err error, hint string) ManagerOption { + return func(*Manager) error { + if hint != "" { + err = core.Wrap(err, hint) + } + return err + } +} + +// 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 + } +} + +// WithLookuper attaches a resolver.Lookuper to the Manager +func WithLookuper(h resolver.Lookuper) ManagerOption { + if h == nil { + h = cluster.DefaultLookuper() + } + + return func(mgr *Manager) error { + mgr.r = resolver.NewResolver(h) + return nil + } +} + +// WithResolver attaches a resolver.Resolver to the Manager +func WithResolver(r resolver.Resolver) ManagerOption { + if r == nil { + r = cluster.DefaultResolver() + } + + return func(mgr *Manager) error { + mgr.r = r + 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.p == nil { + opts = append(opts, WithProvider(nil)) + } + + if mgr.r == nil { + opts = append(opts, WithResolver(nil)) + } + + if mgr.l == nil { + opts = append(opts, WithLogger(nil)) + } + + for _, opt := range opts { + if err := opt(mgr); err != nil { + return err + } + } + return nil +} + +// NewManager creates a DNS manager with the +func NewManager(opts ...ManagerOption) (*Manager, error) { + mgr := new(Manager) + + 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 +} diff --git a/pkg/dns/provider.go b/pkg/dns/provider.go new file mode 100644 index 0000000..6a53a24 --- /dev/null +++ b/pkg/dns/provider.go @@ -0,0 +1,36 @@ +package dns + +import ( + "fmt" + "os" + + "github.com/libdns/cloudflare" + "github.com/libdns/libdns" +) + +const ( + // CloudflareAPIToken is the environment variable + // containing the API Token + CloudflareAPIToken = "CLOUDFLARE_DNS_API_TOKEN" +) + +// Provider manages DNS entries +type Provider interface { + libdns.RecordGetter + libdns.RecordDeleter +} + +// DefaultDNSProvider returns a cloudflare DNS provider +// using an API Token from env [CloudflareAPIToken] +func DefaultDNSProvider() (*cloudflare.Provider, error) { + s := os.Getenv(CloudflareAPIToken) + if s == "" { + return nil, fmt.Errorf("%q: %s", CloudflareAPIToken, "not found") + } + + p := &cloudflare.Provider{ + APIToken: s, + } + + return p, nil +}