mirror of
https://github.com/juanfont/headscale.git
synced 2026-10-21 05:56:34 +09:00
dns: add libdns provider registry
Curated cloudflare, gigahost, hetzner, powerdns and rfc2136. Settings decode via json tags; env per field, <field>_path for secrets. Updates #2137
This commit is contained in:
1 parent
a777969027
commit
24cf63294c
5 files changed
+320
-3
No files matched your search
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"vendor": {
|
||||
"goModSum": "sha256-aq3f+UFdqP7KURd92D2IvlViCkYouZtK/6UzRyNTBgE=",
|
||||
"sri": "sha256-EZ6+6LYzxcc48FwNddi46h6kaHur108O1qv1TIXfRzY="
|
||||
"goModSum": "sha256-oSB3v9cn+i6QxfOQGzsjCFjVgSOTrpp9mjR7mJmzQYs=",
|
||||
"sri": "sha256-KpiRQbHBAPp2BB+NodeSih2/WIlg52dfoOpYxbrVmBI="
|
||||
}
|
||||
}
|
||||
@@ -19,9 +19,16 @@ require (
|
||||
github.com/go-chi/metrics v0.1.1
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.7
|
||||
github.com/go-json-experiment/json v0.0.0-20260820222146-c27c302e5fc3
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/jagottsicher/termcolor v1.0.2
|
||||
github.com/kradalby/libdns-gigahost v0.0.0-20261014145029-4c03bc63b974
|
||||
github.com/libdns/cloudflare v0.2.2
|
||||
github.com/libdns/hetzner/v2 v2.0.1
|
||||
github.com/libdns/libdns v1.1.1
|
||||
github.com/libdns/powerdns v0.1.4
|
||||
github.com/libdns/rfc2136 v1.0.1
|
||||
github.com/oapi-codegen/runtime v1.7.0
|
||||
github.com/oauth2-proxy/mockoidc v0.0.0-20240214162133-caebfff84d25
|
||||
github.com/ory/dockertest/v3 v3.12.0
|
||||
@@ -135,7 +142,6 @@ require (
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
|
||||
github.com/go-logr/logr v1.4.4 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/go4org/hashtriemap v0.0.0-20260925222741-44e5305f85d9 // indirect
|
||||
github.com/godbus/dbus/v5 v5.2.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
|
||||
@@ -151,6 +157,7 @@ require (
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
|
||||
github.com/hashicorp/go-version v1.9.0 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
||||
github.com/hetznercloud/hcloud-go/v2 v2.27.0 // indirect
|
||||
github.com/huin/goupnp v1.3.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
@@ -163,6 +170,7 @@ require (
|
||||
github.com/kamstrup/intmap v0.5.2 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/klauspost/compress v1.20.1 // indirect
|
||||
github.com/kradalby/gigahost-go/client v0.0.0-20261014144355-025b38b667ed // indirect
|
||||
github.com/lib/pq v1.12.3 // indirect
|
||||
github.com/lithammer/fuzzysearch v1.1.8 // indirect
|
||||
github.com/mattn/go-colorable v0.1.15 // indirect
|
||||
@@ -170,7 +178,9 @@ require (
|
||||
github.com/mattn/go-runewidth v0.0.31 // indirect
|
||||
github.com/mdlayher/netlink v1.11.2 // indirect
|
||||
github.com/mdlayher/socket v0.7.0 // indirect
|
||||
github.com/miekg/dns v1.1.73 // indirect
|
||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
||||
github.com/mittwald/go-powerdns v0.6.6 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/moby/api v1.55.0 // indirect
|
||||
github.com/moby/moby/client v0.5.1 // indirect
|
||||
|
||||
@@ -259,12 +259,16 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA=
|
||||
github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU=
|
||||
github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
|
||||
github.com/hetznercloud/hcloud-go/v2 v2.27.0 h1:SOGpAP3kQ6+aevB4Hxr63ukNsdYJjHhuWNB1C3NsiJo=
|
||||
github.com/hetznercloud/hcloud-go/v2 v2.27.0/go.mod h1:OVlbjfoEuvNPI8ji3Sm/jPkjOxO7MKEiPyfctZ0R8jw=
|
||||
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
|
||||
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||
@@ -311,12 +315,27 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kradalby/gigahost-go/client v0.0.0-20261014144355-025b38b667ed h1:Jc+HE4/e+TK1H40RzQSa6ZR7Yu2Mx2brH+O4/uWsCgo=
|
||||
github.com/kradalby/gigahost-go/client v0.0.0-20261014144355-025b38b667ed/go.mod h1:sZmT6pTI0BarSbOc1CvUxwpxLl5SSIXhitioezuVmD0=
|
||||
github.com/kradalby/libdns-gigahost v0.0.0-20261014145029-4c03bc63b974 h1:G3F633EXxBI7ls+F8Jet7geawmCsqZMzyLEp+ZmS4aI=
|
||||
github.com/kradalby/libdns-gigahost v0.0.0-20261014145029-4c03bc63b974/go.mod h1:cTFSzETMrxz9nLduiEMrI1VuLLohAg8AA56kJjw25Vw=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
||||
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
|
||||
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
github.com/libdns/cloudflare v0.2.2 h1:XWHv+C1dDcApqazlh08Q6pjytYLgR2a+Y3xrXFu0vsI=
|
||||
github.com/libdns/cloudflare v0.2.2/go.mod h1:w9uTmRCDlAoafAsTPnn2nJ0XHK/eaUMh86DUk8BWi60=
|
||||
github.com/libdns/hetzner/v2 v2.0.1 h1:phsNc9er8JbVOCsytLG7VuMbCdTPsv+WWwNX1WCbk4Q=
|
||||
github.com/libdns/hetzner/v2 v2.0.1/go.mod h1:IkEcr62KG0rXYhs2K5VWCG4SFPUQWuI1SzlpXBz9amk=
|
||||
github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/libdns/libdns v1.1.1 h1:wPrHrXILoSHKWJKGd0EiAVmiJbFShguILTg9leS/P/U=
|
||||
github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/libdns/powerdns v0.1.4 h1:QdQ+FL2t5ky2UYiWSodbz2HOwQkCWpd+WGa4OoWGpBk=
|
||||
github.com/libdns/powerdns v0.1.4/go.mod h1:kTRi2e4sCcValWW6iWwyfcvFXvlxl1mn83vDAgq8bhA=
|
||||
github.com/libdns/rfc2136 v1.0.1 h1:aiztZgzI2cd9FAtBNPILz01mQcZs1jMqJ467KKI4UQ0=
|
||||
github.com/libdns/rfc2136 v1.0.1/go.mod h1:Uf4niCfXVgiMgwUrkPdIa5/sqLFdjVhkZj1ZfFAuSq4=
|
||||
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
|
||||
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
@@ -340,6 +359,8 @@ github.com/miekg/dns v1.1.73 h1:uhT8nJxmTrPJYClxVxTCX+CVn6qnzSiybRk72Z6DgrE=
|
||||
github.com/miekg/dns v1.1.73/go.mod h1:RW2Obtfd5NZHvOFe3zYG0W8koWOQtAzyHaLo8vASBuQ=
|
||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
||||
github.com/mittwald/go-powerdns v0.6.6 h1:yQcuszhl98+jJgELjD5ecfxCQWoshhnArexpwrwQxLY=
|
||||
github.com/mittwald/go-powerdns v0.6.6/go.mod h1:adWJ860laOgm14afg+7V0nCa5NQT37oEYe2HRhoS/CA=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/moby/api v1.55.0 h1:2/sexvQyqIWS8pRSCFddBfpW2qE7vR7FCL+vN8pxwMc=
|
||||
@@ -358,6 +379,7 @@ github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ=
|
||||
github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
@@ -627,6 +649,8 @@ google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/h2non/gock.v1 v1.0.14 h1:fTeu9fcUvSnLNacYvYI54h+1/XEteDyHvrVCZEEEYNM=
|
||||
gopkg.in/h2non/gock.v1 v1.0.14/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"os"
|
||||
"reflect"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/go-viper/mapstructure/v2"
|
||||
gigahost "github.com/kradalby/libdns-gigahost"
|
||||
"github.com/libdns/cloudflare"
|
||||
"github.com/libdns/hetzner/v2"
|
||||
"github.com/libdns/libdns"
|
||||
"github.com/libdns/powerdns"
|
||||
"github.com/libdns/rfc2136"
|
||||
)
|
||||
|
||||
// Provider writes ACME DNS-01 challenge records into a zone.
|
||||
//
|
||||
// It is append and delete only: a wildcard order puts two values on the
|
||||
// same name, so SetRecords would replace the value the CA is validating.
|
||||
type Provider interface {
|
||||
libdns.RecordAppender
|
||||
libdns.RecordDeleter
|
||||
}
|
||||
|
||||
// providers is the compiled-in provider registry. Providers are decoded
|
||||
// from configuration through their json tags, so adding one is one line
|
||||
// plus its module; the set is curated because each provider is a
|
||||
// dependency to keep current and its quirks become ours.
|
||||
var providers = map[string]func() Provider{
|
||||
"cloudflare": func() Provider { return new(cloudflare.Provider) },
|
||||
"gigahost": func() Provider { return new(gigahost.Provider) },
|
||||
"hetzner": func() Provider { return new(hetzner.Provider) },
|
||||
"powerdns": func() Provider { return new(powerdns.Provider) },
|
||||
"rfc2136": func() Provider { return new(rfc2136.Provider) },
|
||||
}
|
||||
|
||||
var (
|
||||
ErrUnknownProvider = errors.New("unknown DNS provider")
|
||||
ErrProviderConfig = errors.New("invalid DNS provider configuration")
|
||||
)
|
||||
|
||||
// pathSuffix marks a setting whose value is read from a file, keeping
|
||||
// secrets out of the configuration file.
|
||||
const pathSuffix = "_path"
|
||||
|
||||
// ProviderNames returns the names of the compiled-in providers.
|
||||
func ProviderNames() []string {
|
||||
return slices.Sorted(maps.Keys(providers))
|
||||
}
|
||||
|
||||
// NewProvider builds the named provider from its settings.
|
||||
//
|
||||
// settings holds the provider's keys as written in the configuration
|
||||
// file. get looks up a single key and is consulted for every field of
|
||||
// the provider, because environment variables only reach individual
|
||||
// keys, never a map. Any string field can instead be read from a file
|
||||
// named by "<field>_path". Unknown keys are an error so a typo does not
|
||||
// silently leave a credential empty.
|
||||
func NewProvider(
|
||||
name string,
|
||||
settings map[string]any,
|
||||
get func(key string) (any, bool),
|
||||
) (Provider, error) {
|
||||
newProvider, ok := providers[strings.ToLower(name)]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%w: %q", ErrUnknownProvider, name)
|
||||
}
|
||||
|
||||
provider := newProvider()
|
||||
|
||||
input := make(map[string]any, len(settings))
|
||||
for k, v := range settings {
|
||||
input[strings.ToLower(k)] = v
|
||||
}
|
||||
|
||||
delete(input, "name")
|
||||
|
||||
fields := jsonFields(provider)
|
||||
for tag, kind := range fields {
|
||||
if v, ok := get(tag); ok {
|
||||
input[tag] = v
|
||||
}
|
||||
|
||||
pathKey := tag + pathSuffix
|
||||
if _, isField := fields[pathKey]; isField || kind != reflect.String {
|
||||
continue
|
||||
}
|
||||
|
||||
if v, ok := get(pathKey); ok {
|
||||
input[pathKey] = v
|
||||
}
|
||||
|
||||
path, ok := input[pathKey]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, both := input[tag]; both {
|
||||
return nil, fmt.Errorf("%w: %s and %s are mutually exclusive", ErrProviderConfig, tag, pathKey)
|
||||
}
|
||||
|
||||
value, err := os.ReadFile(os.ExpandEnv(fmt.Sprint(path)))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: reading %s: %w", ErrProviderConfig, pathKey, err)
|
||||
}
|
||||
|
||||
input[tag] = strings.TrimSpace(string(value))
|
||||
delete(input, pathKey)
|
||||
}
|
||||
|
||||
// Every provider needs a credential or a server: a block with only a
|
||||
// name is a secret that was not set, and would fail at the first
|
||||
// certificate instead of at startup.
|
||||
if len(input) == 0 {
|
||||
return nil, fmt.Errorf("%w for %s: no settings", ErrProviderConfig, name)
|
||||
}
|
||||
|
||||
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
TagName: "json",
|
||||
WeaklyTypedInput: true,
|
||||
ErrorUnused: true,
|
||||
Result: provider,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating decoder: %w", err)
|
||||
}
|
||||
|
||||
err = decoder.Decode(input)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w for %s: %w", ErrProviderConfig, name, err)
|
||||
}
|
||||
|
||||
return provider, nil
|
||||
}
|
||||
|
||||
// jsonFields returns the json key and kind of each configurable field of
|
||||
// the provider struct.
|
||||
func jsonFields(provider Provider) map[string]reflect.Kind {
|
||||
t := reflect.TypeOf(provider).Elem()
|
||||
fields := make(map[string]reflect.Kind, t.NumField())
|
||||
|
||||
for f := range t.Fields() {
|
||||
name, _, _ := strings.Cut(f.Tag.Get("json"), ",")
|
||||
if !f.IsExported() || name == "" || name == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
fields[name] = f.Type.Kind()
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
gigahost "github.com/kradalby/libdns-gigahost"
|
||||
"github.com/libdns/cloudflare"
|
||||
"github.com/libdns/hetzner/v2"
|
||||
"github.com/libdns/powerdns"
|
||||
"github.com/libdns/rfc2136"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func noEnv(string) (any, bool) { return nil, false }
|
||||
|
||||
func TestNewProvider(t *testing.T) {
|
||||
secret := filepath.Join(t.TempDir(), "token")
|
||||
require.NoError(t, os.WriteFile(secret, []byte("from-file\n"), 0o600))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
provider string
|
||||
settings map[string]any
|
||||
want Provider
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "cloudflare from settings",
|
||||
provider: "cloudflare",
|
||||
settings: map[string]any{"name": "cloudflare", "api_token": "tok"},
|
||||
want: &cloudflare.Provider{APIToken: "tok"},
|
||||
},
|
||||
{
|
||||
name: "name is case insensitive",
|
||||
provider: "CloudFlare",
|
||||
settings: map[string]any{"api_token": "tok"},
|
||||
want: &cloudflare.Provider{APIToken: "tok"},
|
||||
},
|
||||
{
|
||||
name: "rfc2136",
|
||||
provider: "rfc2136",
|
||||
settings: map[string]any{
|
||||
"server": "ns1.example.com:53",
|
||||
"key_name": "headscale",
|
||||
"key_alg": "hmac-sha256",
|
||||
"key": "c2VjcmV0",
|
||||
},
|
||||
want: &rfc2136.Provider{
|
||||
Server: "ns1.example.com:53",
|
||||
KeyName: "headscale",
|
||||
KeyAlg: "hmac-sha256",
|
||||
Key: "c2VjcmV0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "secret from path",
|
||||
provider: "cloudflare",
|
||||
settings: map[string]any{"api_token_path": secret},
|
||||
want: &cloudflare.Provider{APIToken: "from-file"},
|
||||
},
|
||||
{
|
||||
name: "value and path both set",
|
||||
provider: "cloudflare",
|
||||
settings: map[string]any{"api_token": "tok", "api_token_path": secret},
|
||||
wantErr: ErrProviderConfig,
|
||||
},
|
||||
{
|
||||
name: "unreadable path",
|
||||
provider: "cloudflare",
|
||||
settings: map[string]any{"api_token_path": filepath.Join(t.TempDir(), "missing")},
|
||||
wantErr: ErrProviderConfig,
|
||||
},
|
||||
{
|
||||
name: "unknown key",
|
||||
provider: "cloudflare",
|
||||
settings: map[string]any{"auth_token": "tok"},
|
||||
wantErr: ErrProviderConfig,
|
||||
},
|
||||
{
|
||||
name: "unknown provider",
|
||||
provider: "route53",
|
||||
wantErr: ErrUnknownProvider,
|
||||
},
|
||||
{
|
||||
name: "gigahost",
|
||||
provider: "gigahost",
|
||||
settings: map[string]any{"api_key_path": secret},
|
||||
want: &gigahost.Provider{APIKey: "from-file"},
|
||||
},
|
||||
{
|
||||
name: "hetzner",
|
||||
provider: "hetzner",
|
||||
settings: map[string]any{"api_token_path": secret},
|
||||
want: &hetzner.Provider{APIToken: "from-file"},
|
||||
},
|
||||
{
|
||||
name: "powerdns",
|
||||
provider: "powerdns",
|
||||
settings: map[string]any{"server_url": "https://pdns.example.com", "api_token": "tok"},
|
||||
want: &powerdns.Provider{ServerURL: "https://pdns.example.com", APIToken: "tok"},
|
||||
},
|
||||
{
|
||||
name: "only a name",
|
||||
provider: "gigahost",
|
||||
settings: map[string]any{"name": "gigahost"},
|
||||
wantErr: ErrProviderConfig,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := NewProvider(tt.provider, tt.settings, noEnv)
|
||||
if tt.wantErr != nil {
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user