Files

94 lines
2.7 KiB
Go

// Package dnsproviders is a registry of DNS providers usable for ACME DNS-01
// challenges. Each provider is implemented as a separate file that registers
// itself in init(); to add a new provider, drop a file in this package — no
// changes are needed elsewhere.
//
// All providers wrap the libdns ecosystem (https://github.com/libdns) so they
// plug directly into certmagic's DNSManager.
package dnsproviders
import (
"fmt"
"sort"
"strings"
"sync"
"github.com/caddyserver/certmagic"
)
// Builder constructs a certmagic.DNSProvider from a flat env-style map.
// The map mirrors the cert_config.dns_env block from config.yml.
type Builder func(env map[string]string) (certmagic.DNSProvider, error)
// Provider describes a single DNS provider entry in the registry.
type Provider struct {
// Names is the list of accepted dns_provider values; the first is canonical,
// the rest are aliases (case-insensitive match).
Names []string
// EnvVars lists the environment variable names this provider may consume.
// Used for documentation and error messages only.
EnvVars []string
// Build constructs the underlying libdns provider.
Build Builder
}
var (
mu sync.RWMutex
registry = map[string]*Provider{}
ordered []*Provider
)
// Register adds a provider to the global registry. It panics on duplicate
// names so that registration errors surface at process start.
func Register(p *Provider) {
if p == nil || len(p.Names) == 0 || p.Build == nil {
panic("dnsproviders: invalid provider registration")
}
mu.Lock()
defer mu.Unlock()
for _, n := range p.Names {
key := strings.ToLower(strings.TrimSpace(n))
if key == "" {
panic("dnsproviders: empty provider name")
}
if _, exists := registry[key]; exists {
panic(fmt.Sprintf("dnsproviders: duplicate name %q", key))
}
registry[key] = p
}
ordered = append(ordered, p)
}
// Get returns the provider registered under the given (case-insensitive) name
// or alias.
func Get(name string) (*Provider, bool) {
mu.RLock()
defer mu.RUnlock()
p, ok := registry[strings.ToLower(strings.TrimSpace(name))]
return p, ok
}
// CanonicalNames returns the canonical (first-listed) name of every registered
// provider, sorted alphabetically. Useful for error messages.
func CanonicalNames() []string {
mu.RLock()
defer mu.RUnlock()
names := make([]string, 0, len(ordered))
for _, p := range ordered {
names = append(names, p.Names[0])
}
sort.Strings(names)
return names
}
// List returns all registered providers (canonical entries only), sorted by
// canonical name.
func List() []*Provider {
mu.RLock()
defer mu.RUnlock()
out := make([]*Provider, len(ordered))
copy(out, ordered)
sort.Slice(out, func(i, j int) bool { return out[i].Names[0] < out[j].Names[0] })
return out
}