Default to upstream to self (#2436)
* Default to upstream to self This is a backwards incompatible change. This is a massive (cleanup) PR where we default to resolving external names by the coredns process itself, instead of directly forwarding them to some upstream. This ignores any arguments `upstream` may have had and makes it depend on proxy/forward configuration in the Corefile. This allows resolved upstream names to be cached and we have better healthchecking of the upstreams. It also means there is only one way to resolve names, by either using the proxy or forward plugin. The proxy/forward lookup.go functions have been removed. This also lessen the dependency on proxy, meaning deprecating proxy will become easier. Some tests have been removed as well, or moved to the top-level test directory as they now require a full coredns process instead of just the plugin. For the etcd plugin, the entire StubZone resolving is *dropped*! This was a hacky (but working) solution to say the least. If someone cares deeply it can be brought back (maybe)? The pkg/upstream is now very small and almost does nothing. Also the New() function was changed to return a pointer to upstream.Upstream. It also returns only one parameter, so any stragglers using it will encounter a compile error. All documentation has been adapted. This affected the following plugins: * etcd * file * auto * secondary * federation * template * route53 A followup PR will make any upstream directives with arguments an error, right now they are ignored. Signed-off-by: Miek Gieben <miek@miek.nl> * Fix etcd build - probably still fails unit test Signed-off-by: Miek Gieben <miek@miek.nl> * Slightly smarter lookup check in upstream Signed-off-by: Miek Gieben <miek@miek.nl> * Compilez Signed-off-by: Miek Gieben <miek@miek.nl>
This commit is contained in:
parent
6b56a9c921
commit
9c16ed1d14
55 changed files with 184 additions and 1349 deletions
|
@ -18,7 +18,7 @@ auto [ZONES...] {
|
|||
directory DIR [REGEXP ORIGIN_TEMPLATE [TIMEOUT]]
|
||||
reload DURATION
|
||||
no_reload
|
||||
upstream [ADDRESS...]
|
||||
upstream
|
||||
}
|
||||
~~~
|
||||
|
||||
|
@ -37,9 +37,7 @@ are used.
|
|||
and reloads zone when serial changes.
|
||||
* `no_reload` deprecated. Sets reload to 0.
|
||||
* `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs)
|
||||
pointing to external names. **ADDRESS** can be an IP address, an IP:port or a string pointing to
|
||||
a file that is structured as /etc/resolv.conf. If no **ADDRESS** is given, CoreDNS will resolve CNAMEs
|
||||
against itself.
|
||||
pointing to external names. CoreDNS will resolve CNAMEs against itself.
|
||||
|
||||
All directives from the *file* plugin are supported. Note that *auto* will load all zones found,
|
||||
even though the directive might only receive queries for a specific zone. I.e:
|
||||
|
|
|
@ -33,7 +33,7 @@ type (
|
|||
// In the future this should be something like ZoneMeta that contains all this stuff.
|
||||
transferTo []string
|
||||
ReloadInterval time.Duration
|
||||
upstream upstream.Upstream // Upstream for looking up names during the resolution process.
|
||||
upstream *upstream.Upstream // Upstream for looking up names during the resolution process.
|
||||
|
||||
duration time.Duration
|
||||
}
|
||||
|
|
|
@ -155,15 +155,8 @@ func autoParse(c *caddy.Controller) (Auto, error) {
|
|||
a.loader.ReloadInterval = 0
|
||||
|
||||
case "upstream":
|
||||
args := c.RemainingArgs()
|
||||
if len(args) == 0 {
|
||||
return a, c.ArgErr()
|
||||
}
|
||||
var err error
|
||||
a.loader.upstream, err = upstream.New(args)
|
||||
if err != nil {
|
||||
return a, err
|
||||
}
|
||||
c.RemainingArgs() // eat remaining args
|
||||
a.loader.upstream = upstream.New()
|
||||
|
||||
default:
|
||||
t, _, e := parse.Transfer(c, false)
|
||||
|
|
|
@ -28,28 +28,22 @@ If you want to `round robin` A and AAAA responses look at the `loadbalance` plug
|
|||
|
||||
~~~
|
||||
etcd [ZONES...] {
|
||||
stubzones
|
||||
fallthrough [ZONES...]
|
||||
path PATH
|
||||
endpoint ENDPOINT...
|
||||
upstream [ADDRESS...]
|
||||
upstream
|
||||
tls CERT KEY CACERT
|
||||
}
|
||||
~~~
|
||||
|
||||
* `stubzones` enables the stub zones feature. The stubzone is *only* done in the etcd tree located
|
||||
under the *first* zone specified.
|
||||
* `fallthrough` If zone matches but no record can be generated, pass request to the next plugin.
|
||||
If **[ZONES...]** is omitted, then fallthrough happens for all zones for which the plugin
|
||||
is authoritative. If specific zones are listed (for example `in-addr.arpa` and `ip6.arpa`), then only
|
||||
queries for those zones will be subject to fallthrough.
|
||||
* **PATH** the path inside etcd. Defaults to "/skydns".
|
||||
* **ENDPOINT** the etcd endpoints. Defaults to "http://localhost:2379".
|
||||
* `upstream` upstream resolvers to be used resolve external names found in etcd (think CNAMEs)
|
||||
pointing to external names. If you want CoreDNS to act as a proxy for clients, you'll need to add
|
||||
the proxy plugin. If no **ADDRESS** is given, CoreDNS will resolve CNAMEs against itself.
|
||||
**ADDRESS** can be an IP address, and IP:port or a string pointing to a file that is structured
|
||||
as /etc/resolv.conf.
|
||||
* `upstream` resolve names found in etcd (think CNAMEs) If you want CoreDNS to act as a proxy for clients,
|
||||
you'll need to add the forward plugin. CoreDNS will resolve CNAMEs against itself.
|
||||
* `tls` followed by:
|
||||
|
||||
* no arguments, if the server certificate is signed by a system-installed CA and no client cert is needed
|
||||
|
@ -79,10 +73,9 @@ This is the default SkyDNS setup, with everything specified in full:
|
|||
~~~ corefile
|
||||
. {
|
||||
etcd skydns.local {
|
||||
stubzones
|
||||
path /skydns
|
||||
endpoint http://localhost:2379
|
||||
upstream 8.8.8.8:53 8.8.4.4:53
|
||||
upstream
|
||||
}
|
||||
prometheus
|
||||
cache 160 skydns.local
|
||||
|
@ -98,7 +91,7 @@ when resolving external pointing CNAMEs.
|
|||
. {
|
||||
etcd skydns.local {
|
||||
path /skydns
|
||||
upstream /etc/resolv.conf
|
||||
upstream
|
||||
}
|
||||
cache 160 skydns.local
|
||||
proxy . /etc/resolv.conf
|
||||
|
@ -125,7 +118,6 @@ need to add the zone `0.0.10.in-addr.arpa` to the list of zones. Showing a snipp
|
|||
|
||||
~~~
|
||||
etcd skydns.local 10.0.0.0/24 {
|
||||
stubzones
|
||||
...
|
||||
~~~
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
"github.com/coredns/coredns/plugin"
|
||||
"github.com/coredns/coredns/plugin/etcd/msg"
|
||||
"github.com/coredns/coredns/plugin/pkg/fall"
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/coredns/coredns/plugin/pkg/upstream"
|
||||
|
@ -35,10 +34,9 @@ type Etcd struct {
|
|||
Fall fall.F
|
||||
Zones []string
|
||||
PathPrefix string
|
||||
Upstream upstream.Upstream // Proxy for looking up names during the resolution process
|
||||
Upstream *upstream.Upstream
|
||||
Client *etcdcv3.Client
|
||||
Ctx context.Context
|
||||
Stubmap *map[string]proxy.Proxy // list of proxies for stub resolving.
|
||||
|
||||
endpoints []string // Stored here as well, to aid in testing.
|
||||
}
|
||||
|
|
|
@ -14,20 +14,6 @@ func (e *Etcd) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg) (
|
|||
opt := plugin.Options{}
|
||||
state := request.Request{W: w, Req: r, Context: ctx}
|
||||
|
||||
name := state.Name()
|
||||
|
||||
// We need to check stubzones first, because we may get a request for a zone we
|
||||
// are not auth. for *but* do have a stubzone forward for. If we do the stubzone
|
||||
// handler will handle the request.
|
||||
if e.Stubmap != nil && len(*e.Stubmap) > 0 {
|
||||
for zone := range *e.Stubmap {
|
||||
if plugin.Name(zone).Matches(name) {
|
||||
stub := Stub{Etcd: e, Zone: zone}
|
||||
return stub.ServeDNS(ctx, w, r)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zone := plugin.Zones(e.Zones).Matches(state.Name())
|
||||
if zone == "" {
|
||||
return plugin.NextOrFailure(e.Name(), e.Next, ctx, w, r)
|
||||
|
|
|
@ -12,7 +12,6 @@ import (
|
|||
"github.com/coredns/coredns/plugin/pkg/dnstest"
|
||||
"github.com/coredns/coredns/plugin/pkg/tls"
|
||||
"github.com/coredns/coredns/plugin/pkg/upstream"
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
|
@ -283,9 +282,8 @@ func newEtcdPlugin() *Etcd {
|
|||
tlsc, _ := tls.NewTLSConfigFromArgs()
|
||||
client, _ := newEtcdClient(endpoints, tlsc)
|
||||
|
||||
p := proxy.NewLookup([]string{"8.8.8.8:53"})
|
||||
return &Etcd{
|
||||
Upstream: upstream.Upstream{Forward: &p},
|
||||
Upstream: upstream.New(),
|
||||
PathPrefix: "skydns",
|
||||
Ctx: context.Background(),
|
||||
Zones: []string{"skydns.test.", "skydns_extra.test.", "skydns_zonea.test.", "skydns_zoneb.test.", "skydns_zonec.test.", "skydns_zoned.test.", "in-addr.arpa."},
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
clog "github.com/coredns/coredns/plugin/pkg/log"
|
||||
mwtls "github.com/coredns/coredns/plugin/pkg/tls"
|
||||
"github.com/coredns/coredns/plugin/pkg/upstream"
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
|
||||
etcdcv3 "github.com/coreos/etcd/clientv3"
|
||||
"github.com/mholt/caddy"
|
||||
|
@ -25,18 +24,11 @@ func init() {
|
|||
}
|
||||
|
||||
func setup(c *caddy.Controller) error {
|
||||
e, stubzones, err := etcdParse(c)
|
||||
e, err := etcdParse(c)
|
||||
if err != nil {
|
||||
return plugin.Error("etcd", err)
|
||||
}
|
||||
|
||||
if stubzones {
|
||||
c.OnStartup(func() error {
|
||||
e.UpdateStubZones()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
dnsserver.GetConfig(c).AddPlugin(func(next plugin.Handler) plugin.Handler {
|
||||
e.Next = next
|
||||
return e
|
||||
|
@ -45,20 +37,17 @@ func setup(c *caddy.Controller) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func etcdParse(c *caddy.Controller) (*Etcd, bool, error) {
|
||||
stub := make(map[string]proxy.Proxy)
|
||||
func etcdParse(c *caddy.Controller) (*Etcd, error) {
|
||||
etc := Etcd{
|
||||
// Don't default to a proxy for lookups.
|
||||
// Proxy: proxy.NewLookup([]string{"8.8.8.8:53", "8.8.4.4:53"}),
|
||||
PathPrefix: "skydns",
|
||||
Ctx: context.Background(),
|
||||
Stubmap: &stub,
|
||||
}
|
||||
var (
|
||||
tlsConfig *tls.Config
|
||||
err error
|
||||
endpoints = []string{defaultEndpoint}
|
||||
stubzones = false
|
||||
)
|
||||
for c.Next() {
|
||||
etc.Zones = c.RemainingArgs()
|
||||
|
@ -74,38 +63,35 @@ func etcdParse(c *caddy.Controller) (*Etcd, bool, error) {
|
|||
for {
|
||||
switch c.Val() {
|
||||
case "stubzones":
|
||||
stubzones = true
|
||||
// ignored, remove later.
|
||||
case "fallthrough":
|
||||
etc.Fall.SetZonesFromArgs(c.RemainingArgs())
|
||||
case "debug":
|
||||
/* it is a noop now */
|
||||
case "path":
|
||||
if !c.NextArg() {
|
||||
return &Etcd{}, false, c.ArgErr()
|
||||
return &Etcd{}, c.ArgErr()
|
||||
}
|
||||
etc.PathPrefix = c.Val()
|
||||
case "endpoint":
|
||||
args := c.RemainingArgs()
|
||||
if len(args) == 0 {
|
||||
return &Etcd{}, false, c.ArgErr()
|
||||
return &Etcd{}, c.ArgErr()
|
||||
}
|
||||
endpoints = args
|
||||
case "upstream":
|
||||
args := c.RemainingArgs()
|
||||
u, err := upstream.New(args)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
etc.Upstream = u
|
||||
// check args != 0 and error in the future
|
||||
c.RemainingArgs() // clear buffer
|
||||
etc.Upstream = upstream.New()
|
||||
case "tls": // cert key cacertfile
|
||||
args := c.RemainingArgs()
|
||||
tlsConfig, err = mwtls.NewTLSConfigFromArgs(args...)
|
||||
if err != nil {
|
||||
return &Etcd{}, false, err
|
||||
return &Etcd{}, err
|
||||
}
|
||||
default:
|
||||
if c.Val() != "}" {
|
||||
return &Etcd{}, false, c.Errf("unknown property '%s'", c.Val())
|
||||
return &Etcd{}, c.Errf("unknown property '%s'", c.Val())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,14 +103,14 @@ func etcdParse(c *caddy.Controller) (*Etcd, bool, error) {
|
|||
}
|
||||
client, err := newEtcdClient(endpoints, tlsConfig)
|
||||
if err != nil {
|
||||
return &Etcd{}, false, err
|
||||
return &Etcd{}, err
|
||||
}
|
||||
etc.Client = client
|
||||
etc.endpoints = endpoints
|
||||
|
||||
return &etc, stubzones, nil
|
||||
return &etc, nil
|
||||
}
|
||||
return &Etcd{}, false, nil
|
||||
return &Etcd{}, nil
|
||||
}
|
||||
|
||||
func newEtcdClient(endpoints []string, cc *tls.Config) (*etcdcv3.Client, error) {
|
||||
|
|
|
@ -56,7 +56,7 @@ func TestSetupEtcd(t *testing.T) {
|
|||
|
||||
for i, test := range tests {
|
||||
c := caddy.NewTestController("dns", test.input)
|
||||
etcd, _ /*stubzones*/, err := etcdParse(c)
|
||||
etcd, err := etcdParse(c)
|
||||
|
||||
if test.shouldErr && err == nil {
|
||||
t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input)
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
package etcd
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/coredns/coredns/plugin/etcd/msg"
|
||||
"github.com/coredns/coredns/plugin/pkg/dnsutil"
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// UpdateStubZones checks etcd for an update on the stubzones.
|
||||
func (e *Etcd) UpdateStubZones() {
|
||||
go func() {
|
||||
for {
|
||||
e.updateStubZones()
|
||||
time.Sleep(15 * time.Second)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Look in .../dns/stub/<zone>/xx for msg.Services. Loop through them
|
||||
// extract <zone> and add them as forwarders (ip:port-combos) for
|
||||
// the stub zones. Only numeric (i.e. IP address) hosts are used.
|
||||
// Only the first zone configured on e is used for the lookup.
|
||||
func (e *Etcd) updateStubZones() {
|
||||
zone := e.Zones[0]
|
||||
|
||||
fakeState := request.Request{W: nil, Req: new(dns.Msg)}
|
||||
fakeState.Req.SetQuestion(stubDomain+"."+zone, dns.TypeA)
|
||||
|
||||
services, err := e.Records(fakeState, false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
stubmap := make(map[string]proxy.Proxy)
|
||||
// track the nameservers on a per domain basis, but allow a list on the domain.
|
||||
nameservers := map[string][]string{}
|
||||
|
||||
Services:
|
||||
for _, serv := range services {
|
||||
if serv.Port == 0 {
|
||||
serv.Port = 53
|
||||
}
|
||||
ip := net.ParseIP(serv.Host)
|
||||
if ip == nil {
|
||||
log.Warningf("Non IP address stub nameserver: %s", serv.Host)
|
||||
continue
|
||||
}
|
||||
|
||||
domain := msg.Domain(serv.Key)
|
||||
labels := dns.SplitDomainName(domain)
|
||||
|
||||
// If the remaining name equals any of the zones we have, we ignore it.
|
||||
for _, z := range e.Zones {
|
||||
// Chop of left most label, because that is used as the nameserver place holder
|
||||
// and drop the right most labels that belong to zone.
|
||||
// We must *also* chop of dns.stub. which means cutting two more labels.
|
||||
domain = dnsutil.Join(labels[1 : len(labels)-dns.CountLabel(z)-2]...)
|
||||
if domain == z {
|
||||
log.Warningf("Skipping nameserver for domain we are authoritative for: %s", domain)
|
||||
continue Services
|
||||
}
|
||||
}
|
||||
nameservers[domain] = append(nameservers[domain], net.JoinHostPort(serv.Host, strconv.Itoa(serv.Port)))
|
||||
}
|
||||
|
||||
for domain, nss := range nameservers {
|
||||
stubmap[domain] = proxy.NewLookup(nss)
|
||||
}
|
||||
// atomic swap (at least that's what we hope it is)
|
||||
if len(stubmap) > 0 {
|
||||
e.Stubmap = &stubmap
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
package etcd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Stub wraps an Etcd. We have this type so that it can have a ServeDNS method.
|
||||
type Stub struct {
|
||||
*Etcd
|
||||
Zone string // for what zone (and thus what nameservers are we called)
|
||||
}
|
||||
|
||||
// ServeDNS implements the plugin.Handler interface.
|
||||
func (s Stub) ServeDNS(ctx context.Context, w dns.ResponseWriter, req *dns.Msg) (int, error) {
|
||||
if hasStubEdns0(req) {
|
||||
log.Warningf("Forwarding cycle detected, refusing msg: %s", req.Question[0].Name)
|
||||
return dns.RcodeRefused, errors.New("stub forward cycle")
|
||||
}
|
||||
req = addStubEdns0(req)
|
||||
proxy, ok := (*s.Etcd.Stubmap)[s.Zone]
|
||||
if !ok { // somebody made a mistake..
|
||||
return dns.RcodeServerFailure, nil
|
||||
}
|
||||
|
||||
state := request.Request{W: w, Req: req}
|
||||
m, e := proxy.Forward(state)
|
||||
if e != nil {
|
||||
return dns.RcodeServerFailure, e
|
||||
}
|
||||
w.WriteMsg(m)
|
||||
return dns.RcodeSuccess, nil
|
||||
}
|
||||
|
||||
// hasStubEdns0 checks if the message is carrying our special edns0 zero option.
|
||||
func hasStubEdns0(m *dns.Msg) bool {
|
||||
option := m.IsEdns0()
|
||||
if option == nil {
|
||||
return false
|
||||
}
|
||||
for _, o := range option.Option {
|
||||
if o.Option() == ednsStubCode && len(o.(*dns.EDNS0_LOCAL).Data) == 1 &&
|
||||
o.(*dns.EDNS0_LOCAL).Data[0] == 1 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// addStubEdns0 adds our special option to the message's OPT record.
|
||||
func addStubEdns0(m *dns.Msg) *dns.Msg {
|
||||
option := m.IsEdns0()
|
||||
// Add a custom EDNS0 option to the packet, so we can detect loops when 2 stubs are forwarding to each other.
|
||||
if option != nil {
|
||||
option.Option = append(option.Option, &dns.EDNS0_LOCAL{Code: ednsStubCode, Data: []byte{1}})
|
||||
return m
|
||||
}
|
||||
|
||||
m.Extra = append(m.Extra, ednsStub)
|
||||
return m
|
||||
}
|
||||
|
||||
const (
|
||||
ednsStubCode = dns.EDNS0LOCALSTART + 10
|
||||
stubDomain = "stub.dns"
|
||||
)
|
||||
|
||||
var ednsStub = func() *dns.OPT {
|
||||
o := new(dns.OPT)
|
||||
o.Hdr.Name = "."
|
||||
o.Hdr.Rrtype = dns.TypeOPT
|
||||
o.SetUDPSize(4096)
|
||||
|
||||
e := new(dns.EDNS0_LOCAL)
|
||||
e.Code = ednsStubCode
|
||||
e.Data = []byte{1}
|
||||
o.Option = append(o.Option, e)
|
||||
return o
|
||||
}()
|
|
@ -1,87 +0,0 @@
|
|||
// +build etcd
|
||||
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/etcd/msg"
|
||||
"github.com/coredns/coredns/plugin/pkg/dnstest"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func fakeStubServerExampleNet(t *testing.T) (*dns.Server, string) {
|
||||
server, addr, err := test.UDPServer("127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create a UDP server: %s", err)
|
||||
}
|
||||
// add handler for example.net
|
||||
dns.HandleFunc("example.net.", func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
m := new(dns.Msg)
|
||||
m.SetReply(r)
|
||||
m.Answer = []dns.RR{test.A("example.net. 86400 IN A 93.184.216.34")}
|
||||
w.WriteMsg(m)
|
||||
})
|
||||
|
||||
return server, addr
|
||||
}
|
||||
|
||||
func TestStubLookup(t *testing.T) {
|
||||
server, addr := fakeStubServerExampleNet(t)
|
||||
defer server.Shutdown()
|
||||
|
||||
host, p, _ := net.SplitHostPort(addr)
|
||||
port, _ := strconv.Atoi(p)
|
||||
exampleNetStub := &msg.Service{Host: host, Port: port, Key: "a.example.net.stub.dns.skydns.test."}
|
||||
servicesStub = append(servicesStub, exampleNetStub)
|
||||
|
||||
etc := newEtcdPlugin()
|
||||
|
||||
for _, serv := range servicesStub {
|
||||
set(t, etc, serv.Key, 0, serv)
|
||||
defer delete(t, etc, serv.Key)
|
||||
}
|
||||
|
||||
etc.updateStubZones()
|
||||
|
||||
for _, tc := range dnsTestCasesStub {
|
||||
m := tc.Msg()
|
||||
|
||||
rec := dnstest.NewRecorder(&test.ResponseWriter{})
|
||||
_, err := etc.ServeDNS(ctxt, rec, m)
|
||||
if err != nil && m.Question[0].Name == "example.org." {
|
||||
// This is OK, we expect this backend to *not* work.
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %v for %s\n", err, m.Question[0].Name)
|
||||
}
|
||||
resp := rec.Msg
|
||||
if resp == nil {
|
||||
// etcd not running?
|
||||
continue
|
||||
}
|
||||
|
||||
test.SortAndCheck(t, resp, tc)
|
||||
}
|
||||
}
|
||||
|
||||
var servicesStub = []*msg.Service{
|
||||
// Two tests, ask a question that should return servfail because remote it no accessible
|
||||
// and one with edns0 option added, that should return refused.
|
||||
{Host: "127.0.0.1", Port: 666, Key: "b.example.org.stub.dns.skydns.test."},
|
||||
}
|
||||
|
||||
var dnsTestCasesStub = []test.Case{
|
||||
{
|
||||
Qname: "example.org.", Qtype: dns.TypeA, Rcode: dns.RcodeServerFailure,
|
||||
},
|
||||
{
|
||||
Qname: "example.net.", Qtype: dns.TypeA,
|
||||
Answer: []dns.RR{test.A("example.net. 86400 IN A 93.184.216.34")},
|
||||
},
|
||||
}
|
|
@ -17,16 +17,14 @@ Enabling *federation* without also having *kubernetes* is a noop.
|
|||
~~~
|
||||
federation [ZONES...] {
|
||||
NAME DOMAIN
|
||||
upstream [ADDRESS...]
|
||||
upstream
|
||||
}
|
||||
~~~
|
||||
|
||||
* Each **NAME** and **DOMAIN** defines federation membership. One entry for each. A duplicate
|
||||
**NAME** will silently overwrite any previous value.
|
||||
* `upstream` [**ADDRESS**...] defines the upstream resolvers used for resolving the `CNAME` target
|
||||
produced by this plugin. If no **ADDRESS** is given, CoreDNS
|
||||
will resolve External Services against itself. **ADDRESS** can be an IP, an IP:port, or a path
|
||||
to a file structured like resolv.conf.
|
||||
* `upstream` [**ADDRESS**...] resolve the `CNAME` target produced by this plugin. CoreDNS
|
||||
will resolve External Services against itself.
|
||||
|
||||
## Examples
|
||||
|
||||
|
|
|
@ -64,12 +64,8 @@ func federationParse(c *caddy.Controller) (*Federation, error) {
|
|||
x := c.Val()
|
||||
switch x {
|
||||
case "upstream":
|
||||
args := c.RemainingArgs()
|
||||
u, err := upstream.New(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fed.Upstream = &u
|
||||
c.RemainingArgs()
|
||||
fed.Upstream = upstream.New()
|
||||
default:
|
||||
args := c.RemainingArgs()
|
||||
if x := len(args); x != 1 {
|
||||
|
|
|
@ -29,7 +29,7 @@ file DBFILE [ZONES... ] {
|
|||
transfer to ADDRESS...
|
||||
reload DURATION
|
||||
no_reload
|
||||
upstream [ADDRESS...]
|
||||
upstream
|
||||
}
|
||||
~~~
|
||||
|
||||
|
@ -41,11 +41,9 @@ file DBFILE [ZONES... ] {
|
|||
Value of `0` means to not scan for changes and reload. For example, `30s` checks the zonefile every 30 seconds
|
||||
and reloads the zone when serial changes.
|
||||
* `no_reload` deprecated. Sets reload to 0.
|
||||
* `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs)
|
||||
pointing to external names. This is only really useful when CoreDNS is configured as a proxy; for
|
||||
normal authoritative serving you don't need *or* want to use this. **ADDRESS** can be an IP
|
||||
address, an IP:port or a string pointing to a file that is structured as /etc/resolv.conf.
|
||||
If no **ADDRESS** is given, CoreDNS will resolve CNAMEs against itself.
|
||||
* `upstream` resolve external names found (think CNAMEs) pointing to external names. This is only
|
||||
really useful when CoreDNS is configured as a proxy; for normal authoritative serving you don't
|
||||
need *or* want to use this. CoreDNS will resolve CNAMEs against itself.
|
||||
|
||||
## Examples
|
||||
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/pkg/dnstest"
|
||||
"github.com/coredns/coredns/plugin/pkg/upstream"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func TestLookupCNAMEChain(t *testing.T) {
|
||||
name := "example.org."
|
||||
zone, err := Parse(strings.NewReader(dbExampleCNAME), name, "stdin", 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error when reading zone, got %q", err)
|
||||
}
|
||||
|
||||
fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{name: zone}, Names: []string{name}}}
|
||||
ctx := context.TODO()
|
||||
|
||||
for _, tc := range cnameTestCases {
|
||||
m := tc.Msg()
|
||||
|
||||
rec := dnstest.NewRecorder(&test.ResponseWriter{})
|
||||
_, err := fm.ServeDNS(ctx, rec, m)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
resp := rec.Msg
|
||||
test.SortAndCheck(t, resp, tc)
|
||||
}
|
||||
}
|
||||
|
||||
var cnameTestCases = []test.Case{
|
||||
{
|
||||
Qname: "a.example.org.", Qtype: dns.TypeA,
|
||||
Answer: []dns.RR{
|
||||
test.A("a.example.org. 1800 IN A 127.0.0.1"),
|
||||
},
|
||||
},
|
||||
{
|
||||
Qname: "www3.example.org.", Qtype: dns.TypeCNAME,
|
||||
Answer: []dns.RR{
|
||||
test.CNAME("www3.example.org. 1800 IN CNAME www2.example.org."),
|
||||
},
|
||||
},
|
||||
{
|
||||
Qname: "dangling.example.org.", Qtype: dns.TypeA,
|
||||
Answer: []dns.RR{
|
||||
test.CNAME("dangling.example.org. 1800 IN CNAME foo.example.org."),
|
||||
},
|
||||
},
|
||||
{
|
||||
Qname: "www3.example.org.", Qtype: dns.TypeA,
|
||||
Answer: []dns.RR{
|
||||
test.A("a.example.org. 1800 IN A 127.0.0.1"),
|
||||
test.CNAME("www.example.org. 1800 IN CNAME a.example.org."),
|
||||
test.CNAME("www1.example.org. 1800 IN CNAME www.example.org."),
|
||||
test.CNAME("www2.example.org. 1800 IN CNAME www1.example.org."),
|
||||
test.CNAME("www3.example.org. 1800 IN CNAME www2.example.org."),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestLookupCNAMEExternal(t *testing.T) {
|
||||
name := "example.org."
|
||||
zone, err := Parse(strings.NewReader(dbExampleCNAME), name, "stdin", 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error when reading zone, got %q", err)
|
||||
}
|
||||
zone.Upstream, _ = upstream.New([]string{"8.8.8.8:53"}) // TODO(miek): point to local instance
|
||||
|
||||
fm := File{Next: test.ErrorHandler(), Zones: Zones{Z: map[string]*Zone{name: zone}, Names: []string{name}}}
|
||||
ctx := context.TODO()
|
||||
|
||||
for _, tc := range exernalTestCases {
|
||||
m := tc.Msg()
|
||||
|
||||
rec := dnstest.NewRecorder(&test.ResponseWriter{})
|
||||
_, err := fm.ServeDNS(ctx, rec, m)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
resp := rec.Msg
|
||||
test.SortAndCheck(t, resp, tc)
|
||||
}
|
||||
}
|
||||
|
||||
var exernalTestCases = []test.Case{
|
||||
{
|
||||
Qname: "external.example.org.", Qtype: dns.TypeA,
|
||||
Answer: []dns.RR{
|
||||
test.CNAME("external.example.org. 1800 CNAME www.example.net."),
|
||||
// magic 303 TTL that says: don't check TTL.
|
||||
test.A("www.example.net. 303 IN A 93.184.216.34"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const dbExampleCNAME = `
|
||||
$TTL 30M
|
||||
$ORIGIN example.org.
|
||||
@ IN SOA linode.atoom.net. miek.miek.nl. (
|
||||
1282630057 ; Serial
|
||||
4H ; Refresh
|
||||
1H ; Retry
|
||||
7D ; Expire
|
||||
4H ) ; Negative Cache TTL
|
||||
|
||||
a IN A 127.0.0.1
|
||||
www3 IN CNAME www2
|
||||
www2 IN CNAME www1
|
||||
www1 IN CNAME www
|
||||
www IN CNAME a
|
||||
dangling IN CNAME foo
|
||||
external IN CNAME www.example.net.`
|
|
@ -1,5 +1,8 @@
|
|||
package file
|
||||
|
||||
/*
|
||||
TODO(miek): move to test/ for full server testing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
@ -294,3 +297,4 @@ ns.example.org. 1800 IN A 127.0.0.1
|
|||
RXpMdvaE6ZDwalWldLjC3h8QDywDoFdndoRY
|
||||
eHOsmTvvtWWqtO6Fa5A8gmHT5HA= )
|
||||
`
|
||||
*/
|
||||
|
|
|
@ -374,7 +374,6 @@ func cnameForType(targets []dns.RR, origQtype uint16) []dns.RR {
|
|||
func (z *Zone) externalLookup(state request.Request, target string, qtype uint16) []dns.RR {
|
||||
m, e := z.Upstream.Lookup(state, target, qtype)
|
||||
if e != nil {
|
||||
// TODO(miek): Log, or return error here?
|
||||
return nil
|
||||
}
|
||||
if m == nil {
|
||||
|
|
|
@ -93,7 +93,7 @@ func fileParse(c *caddy.Controller) (Zones, error) {
|
|||
}
|
||||
|
||||
reload := 1 * time.Minute
|
||||
upstr := upstream.Upstream{}
|
||||
upstr := upstream.New()
|
||||
t := []string{}
|
||||
var e error
|
||||
|
||||
|
@ -116,11 +116,8 @@ func fileParse(c *caddy.Controller) (Zones, error) {
|
|||
reload = 0
|
||||
|
||||
case "upstream":
|
||||
args := c.RemainingArgs()
|
||||
upstr, err = upstream.New(args)
|
||||
if err != nil {
|
||||
return Zones{}, err
|
||||
}
|
||||
// ignore args, will be error later.
|
||||
c.RemainingArgs() // clear buffer
|
||||
|
||||
default:
|
||||
return Zones{}, c.Errf("unknown property '%s'", c.Val())
|
||||
|
|
|
@ -57,8 +57,8 @@ func TestFileParse(t *testing.T) {
|
|||
`file ` + zoneFileName1 + ` example.net. {
|
||||
upstream a
|
||||
}`,
|
||||
true,
|
||||
Zones{Names: []string{}},
|
||||
false, // OK for now as we disregard any options for the `upstream`.
|
||||
Zones{Names: []string{"example.net."}},
|
||||
},
|
||||
{
|
||||
`file ` + zoneFileName1 + ` example.net. {
|
||||
|
|
|
@ -32,7 +32,7 @@ type Zone struct {
|
|||
LastReloaded time.Time
|
||||
reloadMu sync.RWMutex
|
||||
reloadShutdown chan bool
|
||||
Upstream upstream.Upstream // Upstream for looking up names during the resolution process
|
||||
Upstream *upstream.Upstream // Upstream for looking up external names during the resolution process
|
||||
}
|
||||
|
||||
// Apex contains the apex records of a zone: SOA, NS and their potential signatures.
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
package forward
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/pkg/dnstest"
|
||||
"github.com/coredns/coredns/plugin/pkg/transport"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func TestForward(t *testing.T) {
|
||||
s := dnstest.NewServer(func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
ret := new(dns.Msg)
|
||||
ret.SetReply(r)
|
||||
ret.Answer = append(ret.Answer, test.A("example.org. IN A 127.0.0.1"))
|
||||
w.WriteMsg(ret)
|
||||
})
|
||||
defer s.Close()
|
||||
|
||||
p := NewProxy(s.Addr, transport.DNS)
|
||||
f := New()
|
||||
f.SetProxy(p)
|
||||
defer f.Close()
|
||||
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
state.Req.SetQuestion("example.org.", dns.TypeA)
|
||||
resp, err := f.Forward(state)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
// expect answer section with A record in it
|
||||
if len(resp.Answer) == 0 {
|
||||
t.Fatalf("Expected to at least one RR in the answer section, got none: %s", resp)
|
||||
}
|
||||
if resp.Answer[0].Header().Rrtype != dns.TypeA {
|
||||
t.Errorf("Expected RR to A, got: %d", resp.Answer[0].Header().Rrtype)
|
||||
}
|
||||
if resp.Answer[0].(*dns.A).A.String() != "127.0.0.1" {
|
||||
t.Errorf("Expected 127.0.0.1, got: %s", resp.Answer[0].(*dns.A).A.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestForwardRefused(t *testing.T) {
|
||||
s := dnstest.NewServer(func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
ret := new(dns.Msg)
|
||||
ret.SetReply(r)
|
||||
ret.Rcode = dns.RcodeRefused
|
||||
w.WriteMsg(ret)
|
||||
})
|
||||
defer s.Close()
|
||||
|
||||
p := NewProxy(s.Addr, transport.DNS)
|
||||
f := New()
|
||||
f.SetProxy(p)
|
||||
defer f.Close()
|
||||
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
state.Req.SetQuestion("example.org.", dns.TypeA)
|
||||
resp, err := f.Forward(state)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
if resp.Rcode != dns.RcodeRefused {
|
||||
t.Errorf("Expected rcode to be %d, got %d", dns.RcodeRefused, resp.Rcode)
|
||||
}
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
// Package forward implements a forwarding proxy. It caches an upstream net.Conn for some time, so if the same
|
||||
// client returns the upstream's Conn will be precached. Depending on how you benchmark this looks to be
|
||||
// 50% faster than just opening a new connection for every client. It works with UDP and TCP and uses
|
||||
// inband healthchecking.
|
||||
package forward
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/coredns/coredns/plugin/pkg/transport"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// Forward forward the request in state as-is. Unlike Lookup that adds EDNS0 suffix to the message.
|
||||
// Forward may be called with a nil f, an error is returned in that case.
|
||||
func (f *Forward) Forward(state request.Request) (*dns.Msg, error) {
|
||||
if f == nil {
|
||||
return nil, ErrNoForward
|
||||
}
|
||||
|
||||
fails := 0
|
||||
var upstreamErr error
|
||||
for _, proxy := range f.List() {
|
||||
if proxy.Down(f.maxfails) {
|
||||
fails++
|
||||
if fails < len(f.proxies) {
|
||||
continue
|
||||
}
|
||||
// All upstream proxies are dead, assume healtcheck is complete broken and randomly
|
||||
// select an upstream to connect to.
|
||||
proxy = f.List()[0]
|
||||
}
|
||||
|
||||
ret, err := proxy.Connect(context.Background(), state, f.opts)
|
||||
|
||||
upstreamErr = err
|
||||
|
||||
if err != nil {
|
||||
if fails < len(f.proxies) {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// Check if the reply is correct; if not return FormErr.
|
||||
if !state.Match(ret) {
|
||||
return state.ErrorMessage(dns.RcodeFormatError), nil
|
||||
}
|
||||
|
||||
ret = state.Scrub(ret)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
if upstreamErr != nil {
|
||||
return nil, upstreamErr
|
||||
}
|
||||
|
||||
return nil, ErrNoHealthy
|
||||
}
|
||||
|
||||
// Lookup will use name and type to forge a new message and will send that upstream. It will
|
||||
// set any EDNS0 options correctly so that downstream will be able to process the reply.
|
||||
// Lookup may be called with a nil f, an error is returned in that case.
|
||||
func (f *Forward) Lookup(state request.Request, name string, typ uint16) (*dns.Msg, error) {
|
||||
if f == nil {
|
||||
return nil, ErrNoForward
|
||||
}
|
||||
|
||||
req := new(dns.Msg)
|
||||
req.SetQuestion(name, typ)
|
||||
state.SizeAndDo(req)
|
||||
|
||||
state2 := request.Request{W: state.W, Req: req}
|
||||
|
||||
return f.Forward(state2)
|
||||
}
|
||||
|
||||
// NewLookup returns a Forward that can be used for plugin that need an upstream to resolve external names.
|
||||
// Note that the caller MUST run Close on the forward to stop the health checking goroutines.
|
||||
func NewLookup(addr []string) *Forward {
|
||||
f := New()
|
||||
for i := range addr {
|
||||
p := NewProxy(addr[i], transport.DNS)
|
||||
f.SetProxy(p)
|
||||
}
|
||||
return f
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
package forward
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/pkg/dnstest"
|
||||
"github.com/coredns/coredns/plugin/pkg/transport"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func TestLookup(t *testing.T) {
|
||||
s := dnstest.NewServer(func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
ret := new(dns.Msg)
|
||||
ret.SetReply(r)
|
||||
ret.Answer = append(ret.Answer, test.A("example.org. IN A 127.0.0.1"))
|
||||
w.WriteMsg(ret)
|
||||
})
|
||||
defer s.Close()
|
||||
|
||||
p := NewProxy(s.Addr, transport.DNS)
|
||||
f := New()
|
||||
f.SetProxy(p)
|
||||
defer f.Close()
|
||||
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
resp, err := f.Lookup(state, "example.org.", dns.TypeA)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
// expect answer section with A record in it
|
||||
if len(resp.Answer) == 0 {
|
||||
t.Fatalf("Expected to at least one RR in the answer section, got none: %s", resp)
|
||||
}
|
||||
if resp.Answer[0].Header().Rrtype != dns.TypeA {
|
||||
t.Errorf("Expected RR to A, got: %d", resp.Answer[0].Header().Rrtype)
|
||||
}
|
||||
if resp.Answer[0].(*dns.A).A.String() != "127.0.0.1" {
|
||||
t.Errorf("Expected 127.0.0.1, got: %s", resp.Answer[0].(*dns.A).A.String())
|
||||
}
|
||||
}
|
|
@ -32,7 +32,7 @@ import (
|
|||
type Kubernetes struct {
|
||||
Next plugin.Handler
|
||||
Zones []string
|
||||
Upstream upstream.Upstream
|
||||
Upstream *upstream.Upstream
|
||||
APIServerList []string
|
||||
APIProxy *apiProxy
|
||||
APICertAuth string
|
||||
|
|
|
@ -241,12 +241,8 @@ func ParseStanza(c *caddy.Controller) (*Kubernetes, error) {
|
|||
case "fallthrough":
|
||||
k8s.Fall.SetZonesFromArgs(c.RemainingArgs())
|
||||
case "upstream":
|
||||
args := c.RemainingArgs()
|
||||
u, err := upstream.New(args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k8s.Upstream = u
|
||||
c.RemainingArgs() // eat remaining args
|
||||
k8s.Upstream = upstream.New()
|
||||
case "ttl":
|
||||
args := c.RemainingArgs()
|
||||
if len(args) == 0 {
|
||||
|
|
|
@ -7,7 +7,6 @@ import (
|
|||
|
||||
"github.com/coredns/coredns/plugin/pkg/fall"
|
||||
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/mholt/caddy"
|
||||
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
@ -23,7 +22,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
expectedLabelSelector string // expected label selector value
|
||||
expectedPodMode string
|
||||
expectedFallthrough fall.F
|
||||
expectedUpstreams []string
|
||||
}{
|
||||
// positive
|
||||
{
|
||||
|
@ -36,7 +34,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local test.local`,
|
||||
|
@ -48,7 +45,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -61,7 +57,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -75,7 +70,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -89,7 +83,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -103,7 +96,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -117,7 +109,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -131,7 +122,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -145,7 +135,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"environment=prod",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -159,7 +148,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"application=nginx,environment in (production,qa,staging)",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local test.local {
|
||||
|
@ -177,7 +165,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"application=nginx,environment in (production,qa,staging)",
|
||||
podModeDisabled,
|
||||
fall.Root,
|
||||
nil,
|
||||
},
|
||||
// negative
|
||||
{
|
||||
|
@ -192,7 +179,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -206,7 +192,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -220,7 +205,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -234,7 +218,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -248,7 +231,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -262,7 +244,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -276,7 +257,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
// pods disabled
|
||||
{
|
||||
|
@ -291,7 +271,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
// pods insecure
|
||||
{
|
||||
|
@ -306,7 +285,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeInsecure,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
// pods verified
|
||||
{
|
||||
|
@ -321,7 +299,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeVerified,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
// pods invalid
|
||||
{
|
||||
|
@ -336,7 +313,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeVerified,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
// fallthrough with zones
|
||||
{
|
||||
|
@ -351,12 +327,11 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.F{Zones: []string{"ip6.arpa.", "inaddr.arpa.", "foo.com."}},
|
||||
nil,
|
||||
},
|
||||
// Valid upstream
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
upstream 13.14.15.16:53
|
||||
upstream
|
||||
}`,
|
||||
false,
|
||||
"",
|
||||
|
@ -366,22 +341,6 @@ func TestKubernetesParse(t *testing.T) {
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
[]string{"13.14.15.16:53"},
|
||||
},
|
||||
// Invalid upstream
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
upstream 13.14.15.16orange
|
||||
}`,
|
||||
true,
|
||||
"not an IP address or file: \"13.14.15.16orange\"",
|
||||
-1,
|
||||
0,
|
||||
defaultResyncPeriod,
|
||||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
// More than one Kubernetes not allowed
|
||||
{
|
||||
|
@ -395,7 +354,6 @@ kubernetes cluster.local`,
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -409,7 +367,6 @@ kubernetes cluster.local`,
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -423,7 +380,6 @@ kubernetes cluster.local`,
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -437,7 +393,6 @@ kubernetes cluster.local`,
|
|||
"",
|
||||
podModeDisabled,
|
||||
fall.Zero,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`kubernetes coredns.local {
|
||||
|
@ -519,31 +474,6 @@ kubernetes cluster.local`,
|
|||
if !k8sController.Fall.Equal(test.expectedFallthrough) {
|
||||
t.Errorf("Test %d: Expected kubernetes controller to be initialized with fallthrough '%v'. Instead found fallthrough '%v' for input '%s'", i, test.expectedFallthrough, k8sController.Fall, test.input)
|
||||
}
|
||||
// upstream
|
||||
var foundUpstreams *[]proxy.Upstream
|
||||
if k8sController.Upstream.Forward != nil {
|
||||
foundUpstreams = k8sController.Upstream.Forward.Upstreams
|
||||
}
|
||||
if test.expectedUpstreams == nil {
|
||||
if foundUpstreams != nil {
|
||||
t.Errorf("Test %d: Expected kubernetes controller to not be initialized with upstreams for input '%s'", i, test.input)
|
||||
}
|
||||
} else {
|
||||
if foundUpstreams == nil {
|
||||
t.Errorf("Test %d: Expected kubernetes controller to be initialized with upstreams for input '%s'", i, test.input)
|
||||
} else {
|
||||
if len(*foundUpstreams) != len(test.expectedUpstreams) {
|
||||
t.Errorf("Test %d: Expected kubernetes controller to be initialized with %d upstreams. Instead found %d upstreams for input '%s'", i, len(test.expectedUpstreams), len(*foundUpstreams), test.input)
|
||||
}
|
||||
for j, want := range test.expectedUpstreams {
|
||||
got := (*foundUpstreams)[j].Select().Name
|
||||
if got != want {
|
||||
t.Errorf("Test %d: Expected kubernetes controller to be initialized with upstream '%s'. Instead found upstream '%s' for input '%s'", i, want, got, test.input)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,58 +1,35 @@
|
|||
// Package upstream abstracts a upstream lookups so that plugins
|
||||
// can handle them in an unified way.
|
||||
// Package upstream abstracts a upstream lookups so that plugins can handle them in an unified way.
|
||||
package upstream
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"github.com/coredns/coredns/core/dnsserver"
|
||||
"github.com/coredns/coredns/plugin/pkg/nonwriter"
|
||||
"github.com/coredns/coredns/plugin/pkg/parse"
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/request"
|
||||
)
|
||||
|
||||
// Upstream is used to resolve CNAME targets
|
||||
type Upstream struct {
|
||||
self bool
|
||||
Forward *proxy.Proxy
|
||||
}
|
||||
// Upstream is used to resolve CNAME or other external targets via CoreDNS itself.
|
||||
type Upstream struct{}
|
||||
|
||||
// New creates a new Upstream for given destination(s). If dests is empty it default to upstreaming to
|
||||
// the coredns process.
|
||||
func New(dests []string) (Upstream, error) {
|
||||
u := Upstream{}
|
||||
if len(dests) == 0 {
|
||||
u.self = true
|
||||
return u, nil
|
||||
}
|
||||
u.self = false
|
||||
ups, err := parse.HostPortOrFile(dests...)
|
||||
if err != nil {
|
||||
return u, err
|
||||
}
|
||||
p := proxy.NewLookup(ups)
|
||||
u.Forward = &p
|
||||
return u, nil
|
||||
}
|
||||
// New creates a new Upstream to resolve names using the the coredns process.
|
||||
func New() *Upstream { return &Upstream{} }
|
||||
|
||||
// Lookup routes lookups to our selves or forward to a remote.
|
||||
func (u Upstream) Lookup(state request.Request, name string, typ uint16) (*dns.Msg, error) {
|
||||
if u.self {
|
||||
func (u *Upstream) Lookup(state request.Request, name string, typ uint16) (*dns.Msg, error) {
|
||||
server, ok := state.Context.Value(dnsserver.Key{}).(*dnsserver.Server)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no full server is running")
|
||||
}
|
||||
|
||||
req := new(dns.Msg)
|
||||
req.SetQuestion(name, typ)
|
||||
|
||||
nw := nonwriter.New(state.W)
|
||||
server := state.Context.Value(dnsserver.Key{}).(*dnsserver.Server)
|
||||
|
||||
server.ServeDNS(state.Context, nw, req)
|
||||
|
||||
return nw.Msg, nil
|
||||
}
|
||||
|
||||
if u.Forward != nil {
|
||||
return u.Forward.Lookup(state, name, typ)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
package proxy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/mholt/caddy/caddyfile"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func TestUnhealthy(t *testing.T) {
|
||||
// High HC interval, we want to test the HC after failed queries.
|
||||
config := "proxy . %s {\n health_check /healthcheck:%s 10s \nfail_timeout 100ms\n}"
|
||||
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body.Close()
|
||||
w.Write([]byte("OK"))
|
||||
}))
|
||||
defer backend.Close()
|
||||
|
||||
port := backend.URL[17:] // Remove all crap up to the port
|
||||
back := backend.URL[7:] // Remove http://
|
||||
|
||||
c := caddyfile.NewDispenser("testfile", strings.NewReader(fmt.Sprintf(config, back, port)))
|
||||
upstreams, err := NewStaticUpstreams(&c)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error. Got: %s", err)
|
||||
}
|
||||
p := &Proxy{Upstreams: &upstreams}
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("example.org.", dns.TypeA)
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: m}
|
||||
|
||||
// Should all fail.
|
||||
for j := 0; j < failureCheck; j++ {
|
||||
if _, err := p.Forward(state); err == nil {
|
||||
t.Errorf("Expected error. Got: nil")
|
||||
}
|
||||
}
|
||||
|
||||
fails := atomic.LoadInt32(&upstreams[0].(*staticUpstream).Hosts[0].Fails)
|
||||
if fails != 3 {
|
||||
t.Errorf("Expected %d fails, got %d", 3, fails)
|
||||
}
|
||||
// HC should be kicked off, and reset the counter to 0
|
||||
i := 0
|
||||
for fails != 0 {
|
||||
fails = atomic.LoadInt32(&upstreams[0].(*staticUpstream).Hosts[0].Fails)
|
||||
time.Sleep(100 * time.Microsecond)
|
||||
i++
|
||||
}
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
package proxy
|
||||
|
||||
// functions other plugin might want to use to do lookup in the same style as the proxy.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/coredns/coredns/plugin/pkg/healthcheck"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
// NewLookup create a new proxy with the hosts in host and a Random policy.
|
||||
func NewLookup(hosts []string) Proxy { return NewLookupWithOption(hosts, Options{}) }
|
||||
|
||||
// NewLookupWithOption process creates a simple round robin forward with potentially forced proto for upstream.
|
||||
func NewLookupWithOption(hosts []string, opts Options) Proxy {
|
||||
p := Proxy{Next: nil}
|
||||
|
||||
// TODO(miek): this needs to be unified with upstream.go's NewStaticUpstreams, caddy uses NewHost
|
||||
// we should copy/make something similar.
|
||||
upstream := &staticUpstream{
|
||||
from: ".",
|
||||
HealthCheck: healthcheck.HealthCheck{
|
||||
FailTimeout: 5 * time.Second,
|
||||
MaxFails: 3,
|
||||
},
|
||||
ex: newDNSExWithOption(opts),
|
||||
}
|
||||
upstream.Hosts = make([]*healthcheck.UpstreamHost, len(hosts))
|
||||
|
||||
for i, host := range hosts {
|
||||
uh := &healthcheck.UpstreamHost{
|
||||
Name: host,
|
||||
FailTimeout: upstream.FailTimeout,
|
||||
CheckDown: checkDownFunc(upstream),
|
||||
}
|
||||
|
||||
upstream.Hosts[i] = uh
|
||||
}
|
||||
p.Upstreams = &[]Upstream{upstream}
|
||||
return p
|
||||
}
|
||||
|
||||
// Lookup will use name and type to forge a new message and will send that upstream. It will
|
||||
// set any EDNS0 options correctly so that downstream will be able to process the reply.
|
||||
func (p Proxy) Lookup(state request.Request, name string, typ uint16) (*dns.Msg, error) {
|
||||
req := new(dns.Msg)
|
||||
req.SetQuestion(name, typ)
|
||||
state.SizeAndDo(req)
|
||||
|
||||
state2 := request.Request{W: state.W, Req: req}
|
||||
|
||||
return p.lookup(state2)
|
||||
}
|
||||
|
||||
// Forward forward the request in state as-is. Unlike Lookup that adds EDNS0 suffix to the message.
|
||||
func (p Proxy) Forward(state request.Request) (*dns.Msg, error) {
|
||||
return p.lookup(state)
|
||||
}
|
||||
|
||||
func (p Proxy) lookup(state request.Request) (*dns.Msg, error) {
|
||||
upstream := p.match(state)
|
||||
if upstream == nil {
|
||||
return nil, errInvalidDomain
|
||||
}
|
||||
for {
|
||||
start := time.Now()
|
||||
var reply *dns.Msg
|
||||
var backendErr error
|
||||
|
||||
// Since Select() should give us "up" hosts, keep retrying
|
||||
// hosts until timeout (or until we get a nil host).
|
||||
for time.Since(start) < tryDuration {
|
||||
host := upstream.Select()
|
||||
if host == nil {
|
||||
return nil, fmt.Errorf("%s: %s", errUnreachable, "no upstream host")
|
||||
}
|
||||
|
||||
// duplicated from proxy.go, but with a twist, we don't write the
|
||||
// reply back to the client, we return it and there is no monitoring to update here.
|
||||
|
||||
atomic.AddInt64(&host.Conns, 1)
|
||||
|
||||
reply, backendErr = upstream.Exchanger().Exchange(context.TODO(), host.Name, state)
|
||||
|
||||
atomic.AddInt64(&host.Conns, -1)
|
||||
|
||||
if backendErr == nil {
|
||||
|
||||
if !state.Match(reply) {
|
||||
return state.ErrorMessage(dns.RcodeFormatError), nil
|
||||
}
|
||||
|
||||
return reply, nil
|
||||
}
|
||||
|
||||
if oe, ok := backendErr.(*net.OpError); ok {
|
||||
if oe.Timeout() { // see proxy.go for docs.
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
timeout := host.FailTimeout
|
||||
if timeout == 0 {
|
||||
timeout = defaultFailTimeout
|
||||
}
|
||||
|
||||
atomic.AddInt32(&host.Fails, 1)
|
||||
fails := atomic.LoadInt32(&host.Fails)
|
||||
|
||||
go func(host *healthcheck.UpstreamHost, timeout time.Duration) {
|
||||
time.Sleep(timeout)
|
||||
atomic.AddInt32(&host.Fails, -1)
|
||||
if fails%failureCheck == 0 { // Kick off healthcheck on eveyry third failure.
|
||||
host.HealthCheckURL()
|
||||
}
|
||||
}(host, timeout)
|
||||
}
|
||||
return nil, fmt.Errorf("%s: %s", errUnreachable, backendErr)
|
||||
}
|
||||
}
|
|
@ -9,12 +9,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coredns/coredns/plugin/pkg/dnstest"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/mholt/caddy/caddyfile"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func TestStop(t *testing.T) {
|
||||
|
@ -75,25 +70,3 @@ func TestStop(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProxyRefused(t *testing.T) {
|
||||
s := dnstest.NewServer(func(w dns.ResponseWriter, r *dns.Msg) {
|
||||
ret := new(dns.Msg)
|
||||
ret.SetReply(r)
|
||||
ret.Rcode = dns.RcodeRefused
|
||||
w.WriteMsg(ret)
|
||||
})
|
||||
defer s.Close()
|
||||
|
||||
p := NewLookup([]string{s.Addr})
|
||||
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
state.Req.SetQuestion("example.org.", dns.TypeA)
|
||||
resp, err := p.Forward(state)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
if resp.Rcode != dns.RcodeRefused {
|
||||
t.Errorf("Expected rcode to be %d, got %d", dns.RcodeRefused, resp.Rcode)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
|
||||
## Description
|
||||
|
||||
The route53 plugin is useful for serving zones from resource record sets in AWS route53. This plugin
|
||||
supports all Amazon Route 53 records (https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/ResourceRecordTypes.html).
|
||||
The route53 plugin is useful for serving zones from resource record
|
||||
sets in AWS route53. This plugin supports all Amazon Route 53 records
|
||||
([https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/ResourceRecordTypes.html](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/ResourceRecordTypes.html)).
|
||||
The route53 plugin can be used when coredns is deployed on AWS or elsewhere.
|
||||
|
||||
## Syntax
|
||||
|
@ -15,32 +16,39 @@ The route53 plugin can be used when coredns is deployed on AWS or elsewhere.
|
|||
~~~ txt
|
||||
route53 [ZONE:HOSTED_ZONE_ID...] {
|
||||
[aws_access_key AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY]
|
||||
upstream [ADDRESS...]
|
||||
upstream
|
||||
credentials PROFILE [FILENAME]
|
||||
fallthrough [ZONES...]
|
||||
}
|
||||
~~~
|
||||
|
||||
* **ZONE** the name of the domain to be accessed. When there are multiple zones with overlapping domains
|
||||
(private vs. public hosted zone), CoreDNS does the lookup in the given order here. Therefore, for a
|
||||
non-existing resource record, SOA response will be from the rightmost zone.
|
||||
* **HOSTED_ZONE_ID** the ID of the hosted zone that contains the resource record sets to be accessed.
|
||||
* **AWS_ACCESS_KEY_ID** and **AWS_SECRET_ACCESS_KEY** the AWS access key ID and secret access key
|
||||
* **ZONE** the name of the domain to be accessed. When there are multiple zones with overlapping
|
||||
domains (private vs. public hosted zone), CoreDNS does the lookup in the given order here.
|
||||
Therefore, for a non-existing resource record, SOA response will be from the rightmost zone.
|
||||
|
||||
* **HOSTED*ZONE*ID** the ID of the hosted zone that contains the resource record sets to be
|
||||
accessed.
|
||||
|
||||
* **AWS*ACCESS*KEY_ID** and **AWS*SECRET*ACCESS_KEY** the AWS access key ID and secret access key
|
||||
to be used when query AWS (optional). If they are not provided, then coredns tries to access
|
||||
AWS credentials the same way as AWS CLI, e.g., environmental variables, AWS credentials file,
|
||||
instance profile credentials, etc.
|
||||
* `upstream` [**ADDRESS**...] specifies upstream resolver(s) used for resolving services that point
|
||||
to external hosts (eg. used to resolve CNAMEs). If no **ADDRESS** is given, CoreDNS will resolve
|
||||
against itself. **ADDRESS** can be an IP, an IP:port or a path to a file structured like
|
||||
resolv.conf.
|
||||
* `credentials` used for reading the credential file and setting the profile name for a given zone.
|
||||
|
||||
* `upstream`is used for resolving services that point to external hosts (eg. used to resolve
|
||||
CNAMEs). CoreDNS will resolve against itself.
|
||||
|
||||
* `credentials` used for reading the credential file and setting the profile name for a given
|
||||
zone.
|
||||
|
||||
* **PROFILE** AWS account profile name. Defaults to `default`.
|
||||
* **FILENAME** AWS credentials filename. Defaults to `~/.aws/credentials`
|
||||
are used.
|
||||
|
||||
* **FILENAME** AWS credentials filename. Defaults to `~/.aws/credentials` are used.
|
||||
|
||||
* `fallthrough` If zone matches and no record can be generated, pass request to the next plugin.
|
||||
If **[ZONES...]** is omitted, then fallthrough happens for all zones for which the plugin
|
||||
is authoritative. If specific zones are listed (for example `in-addr.arpa` and `ip6.arpa`), then only
|
||||
queries for those zones will be subject to fallthrough.
|
||||
If **[ZONES...]** is omitted, then fallthrough happens for all zones for which the plugin is
|
||||
authoritative. If specific zones are listed (for example `in-addr.arpa` and `ip6.arpa`), then
|
||||
only queries for those zones will be subject to fallthrough.
|
||||
|
||||
* **ZONES** zones it should be authoritative for. If empty, the zones from the configuration block
|
||||
|
||||
## Examples
|
||||
|
|
|
@ -170,7 +170,7 @@ func (h *Route53) updateZones(ctx context.Context) error {
|
|||
|
||||
for i, hostedZone := range z {
|
||||
newZ := file.NewZone(zName, "")
|
||||
newZ.Upstream = *h.upstream
|
||||
newZ.Upstream = h.upstream
|
||||
in := &route53.ListResourceRecordSetsInput{
|
||||
HostedZoneId: aws.String(hostedZone.id),
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ func setup(c *caddy.Controller, f func(*credentials.Credentials) route53iface.Ro
|
|||
var providers []credentials.Provider
|
||||
var fall fall.F
|
||||
|
||||
up, _ := upstream.New(nil)
|
||||
up := upstream.New()
|
||||
for c.Next() {
|
||||
args := c.RemainingArgs()
|
||||
|
||||
|
@ -83,12 +83,7 @@ func setup(c *caddy.Controller, f func(*credentials.Credentials) route53iface.Ro
|
|||
},
|
||||
})
|
||||
case "upstream":
|
||||
args := c.RemainingArgs()
|
||||
var err error
|
||||
up, err = upstream.New(args)
|
||||
if err != nil {
|
||||
return c.Errf("invalid upstream: %v", err)
|
||||
}
|
||||
c.RemainingArgs() // eats args
|
||||
case "credentials":
|
||||
if c.NextArg() {
|
||||
sharedProvider.Profile = c.Val()
|
||||
|
@ -109,7 +104,7 @@ func setup(c *caddy.Controller, f func(*credentials.Credentials) route53iface.Ro
|
|||
|
||||
client := f(credentials.NewChainCredentials(providers))
|
||||
ctx := context.Background()
|
||||
h, err := New(ctx, client, keys, &up)
|
||||
h, err := New(ctx, client, keys, up)
|
||||
if err != nil {
|
||||
return c.Errf("failed to create Route53 plugin: %v", err)
|
||||
}
|
||||
|
|
|
@ -23,18 +23,16 @@ A working syntax would be:
|
|||
secondary [zones...] {
|
||||
transfer from ADDRESS
|
||||
transfer to ADDRESS
|
||||
upstream [ADDRESS...]
|
||||
upstream
|
||||
}
|
||||
~~~
|
||||
|
||||
* `transfer from` specifies from which address to fetch the zone. It can be specified multiple times;
|
||||
if one does not work, another will be tried.
|
||||
* `transfer to` can be enabled to allow this secondary zone to be transferred again.
|
||||
* `upstream` defines upstream resolvers to be used resolve external names found (think CNAMEs)
|
||||
pointing to external names. This is only really useful when CoreDNS is configured as a proxy, for
|
||||
normal authoritative serving you don't need *or* want to use this. **ADDRESS** can be an IP
|
||||
address, and IP:port or a string pointing to a file that is structured as /etc/resolv.conf.
|
||||
If no **ADDRESS** is given, CoreDNS will resolve CNAMEs against itself.
|
||||
* `upstream` resolve external names found (think CNAMEs) pointing to external names. This is only
|
||||
really useful when CoreDNS is configured as a proxy; for normal authoritative serving you don't
|
||||
need *or* want to use this. CoreDNS will resolve CNAMEs against itself.
|
||||
|
||||
When a zone is due to be refreshed (Refresh timer fires) a random jitter of 5 seconds is
|
||||
applied, before fetching. In the case of retry this will be 2 seconds. If there are any errors
|
||||
|
|
|
@ -49,7 +49,7 @@ func setup(c *caddy.Controller) error {
|
|||
func secondaryParse(c *caddy.Controller) (file.Zones, error) {
|
||||
z := make(map[string]*file.Zone)
|
||||
names := []string{}
|
||||
upstr := upstream.Upstream{}
|
||||
upstr := upstream.New()
|
||||
for c.Next() {
|
||||
|
||||
if c.Val() == "secondary" {
|
||||
|
@ -78,12 +78,7 @@ func secondaryParse(c *caddy.Controller) (file.Zones, error) {
|
|||
return file.Zones{}, e
|
||||
}
|
||||
case "upstream":
|
||||
args := c.RemainingArgs()
|
||||
var err error
|
||||
upstr, err = upstream.New(args)
|
||||
if err != nil {
|
||||
return file.Zones{}, err
|
||||
}
|
||||
c.RemainingArgs() // eat args
|
||||
default:
|
||||
return file.Zones{}, c.Errf("unknown property '%s'", c.Val())
|
||||
}
|
||||
|
|
|
@ -12,14 +12,13 @@ The *template* plugin allows you to dynamically respond to queries by just writi
|
|||
|
||||
~~~
|
||||
template CLASS TYPE [ZONE...] {
|
||||
[match REGEX...]
|
||||
[answer RR]
|
||||
[additional RR]
|
||||
[authority RR]
|
||||
[...]
|
||||
[rcode CODE]
|
||||
[upstream [ADDRESS...]]
|
||||
[fallthrough [ZONE...]]
|
||||
match REGEX...
|
||||
answer RR
|
||||
additional RR
|
||||
authority RR
|
||||
rcode CODE
|
||||
upstream
|
||||
fallthrough [ZONE...]
|
||||
}
|
||||
~~~
|
||||
|
||||
|
@ -30,9 +29,7 @@ template CLASS TYPE [ZONE...] {
|
|||
* `answer|additional|authority` **RR** A [RFC 1035](https://tools.ietf.org/html/rfc1035#section-5) style resource record fragment
|
||||
built by a [Go template](https://golang.org/pkg/text/template/) that contains the reply.
|
||||
* `rcode` **CODE** A response code (`NXDOMAIN, SERVFAIL, ...`). The default is `SUCCESS`.
|
||||
* `upstream` [**ADDRESS**...] defines the upstream resolvers used for resolving CNAME.
|
||||
If no **ADDRESS** is given, CoreDNS will resolve CNAMEs against itself. **ADDRESS**
|
||||
can be an IP, an IP:port, or a path to a file structured like resolv.conf.
|
||||
* `upstream` defines the upstream resolvers used for resolving CNAMEs. CoreDNS will resolve CNAMEs against itself.
|
||||
* `fallthrough` Continue with the next plugin if the zone matched but no regex matched.
|
||||
If specific zones are listed (for example `in-addr.arpa` and `ip6.arpa`), then only queries for
|
||||
those zones will be subject to fallthrough.
|
||||
|
|
|
@ -144,12 +144,8 @@ func templateParse(c *caddy.Controller) (handler Handler, err error) {
|
|||
t.fall.SetZonesFromArgs(c.RemainingArgs())
|
||||
|
||||
case "upstream":
|
||||
args := c.RemainingArgs()
|
||||
u, err := upstream.New(args)
|
||||
if err != nil {
|
||||
return handler, err
|
||||
}
|
||||
t.upstream = &u
|
||||
c.RemainingArgs() // eat remaining args
|
||||
t.upstream = upstream.New()
|
||||
default:
|
||||
return handler, c.ArgErr()
|
||||
}
|
||||
|
|
|
@ -148,13 +148,6 @@ func TestSetupParse(t *testing.T) {
|
|||
}`,
|
||||
false,
|
||||
},
|
||||
{
|
||||
`template ANY ANY up.stream.local {
|
||||
answer "up.stream.local 5 IN CNAME up.river.local"
|
||||
upstream invalid-upstream-argument
|
||||
}`,
|
||||
true,
|
||||
},
|
||||
}
|
||||
for i, test := range tests {
|
||||
c := caddy.NewTestController("dns", test.inputFileRules)
|
||||
|
|
|
@ -7,10 +7,6 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
|
@ -34,10 +30,9 @@ func TestAuto(t *testing.T) {
|
|||
}
|
||||
defer i.Stop()
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
|
||||
resp, err := p.Lookup(state, "www.example.org.", dns.TypeA)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("www.example.org.", dns.TypeA)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
|
@ -52,7 +47,7 @@ func TestAuto(t *testing.T) {
|
|||
|
||||
time.Sleep(1500 * time.Millisecond) // wait for it to be picked up
|
||||
|
||||
resp, err = p.Lookup(state, "www.example.org.", dns.TypeA)
|
||||
resp, err = dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
|
@ -64,7 +59,7 @@ func TestAuto(t *testing.T) {
|
|||
os.Remove(filepath.Join(tmpdir, "db.example.org"))
|
||||
|
||||
time.Sleep(1100 * time.Millisecond) // wait for it to be picked up
|
||||
resp, err = p.Lookup(state, "www.example.org.", dns.TypeA)
|
||||
resp, err = dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
|
@ -99,10 +94,9 @@ func TestAutoNonExistentZone(t *testing.T) {
|
|||
}
|
||||
defer i.Stop()
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
|
||||
resp, err := p.Lookup(state, "example.org.", dns.TypeA)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("example.org.", dns.TypeA)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
|
@ -145,12 +139,9 @@ func TestAutoAXFR(t *testing.T) {
|
|||
|
||||
time.Sleep(1100 * time.Millisecond) // wait for it to be picked up
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
m := new(dns.Msg)
|
||||
m.SetAxfr("example.org.")
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: m}
|
||||
|
||||
resp, err := p.Lookup(state, "example.org.", dns.TypeAXFR)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
|
|
|
@ -3,9 +3,7 @@ package test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
@ -40,21 +38,20 @@ func TestLookupCache(t *testing.T) {
|
|||
}
|
||||
defer i.Stop()
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
|
||||
t.Run("Long TTL", func(t *testing.T) {
|
||||
testCase(t, state, p, "example.org.", 2, 10)
|
||||
testCase(t, "example.org.", udp, 2, 10)
|
||||
})
|
||||
|
||||
t.Run("Short TTL", func(t *testing.T) {
|
||||
testCase(t, state, p, "short.example.org.", 1, 5)
|
||||
testCase(t, "short.example.org.", udp, 1, 5)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func testCase(t *testing.T, state request.Request, p proxy.Proxy, name string, expectAnsLen int, expectTTL uint32) {
|
||||
resp, err := p.Lookup(state, name, dns.TypeA)
|
||||
func testCase(t *testing.T, name, addr string, expectAnsLen int, expectTTL uint32) {
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(name, dns.TypeA)
|
||||
resp, err := dns.Exchange(m, addr)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
|
|
|
@ -3,9 +3,7 @@ package test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
mtest "github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
@ -47,11 +45,10 @@ func TestLookupDS(t *testing.T) {
|
|||
}
|
||||
defer i.Stop()
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &mtest.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
|
||||
for _, tc := range dsTestCases {
|
||||
resp, err := p.Lookup(state, tc.Qname, tc.Qtype)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(tc.Qname, tc.Qtype)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil || resp == nil {
|
||||
t.Fatalf("Expected to receive reply, but didn't for %s %d", tc.Qname, tc.Qtype)
|
||||
}
|
||||
|
|
|
@ -7,9 +7,6 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/etcd/msg"
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
@ -38,16 +35,15 @@ func TestEtcdCache(t *testing.T) {
|
|||
defer delete(ctx, t, etc, serv.Key)
|
||||
}
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
|
||||
resp, err := p.Lookup(state, "b.example.skydns.test.", dns.TypeA)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("b.example.skydns.test.", dns.TypeA)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Errorf("Expected to receive reply, but didn't: %s", err)
|
||||
}
|
||||
checkResponse(t, resp)
|
||||
|
||||
resp, err = p.Lookup(state, "b.example.skydns.test.", dns.TypeA)
|
||||
resp, err = dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Errorf("Expected to receive reply, but didn't: %s", err)
|
||||
}
|
||||
|
|
|
@ -10,9 +10,6 @@ import (
|
|||
|
||||
"github.com/coredns/coredns/plugin/etcd"
|
||||
"github.com/coredns/coredns/plugin/etcd/msg"
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
etcdcv3 "github.com/coreos/etcd/clientv3"
|
||||
"github.com/miekg/dns"
|
||||
|
@ -38,7 +35,7 @@ func TestEtcdStubAndProxyLookup(t *testing.T) {
|
|||
stubzones
|
||||
path /skydns
|
||||
endpoint http://localhost:2379
|
||||
upstream 8.8.8.8:53 8.8.4.4:53
|
||||
upstream
|
||||
fallthrough
|
||||
}
|
||||
proxy . 8.8.8.8:53
|
||||
|
@ -58,9 +55,9 @@ func TestEtcdStubAndProxyLookup(t *testing.T) {
|
|||
defer delete(ctx, t, etc, serv.Key)
|
||||
}
|
||||
|
||||
p := proxy.NewLookup([]string{udp}) // use udp port from the server
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
resp, err := p.Lookup(state, "example.com.", dns.TypeA)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("example.com.", dns.TypeA)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to receive reply, but didn't: %v", err)
|
||||
}
|
||||
|
|
|
@ -11,6 +11,5 @@ short.example.org. 1 IN A 127.0.0.3
|
|||
*.w.example.org. IN TXT "Wildcard"
|
||||
a.b.c.w.example.org. IN TXT "Not a wildcard"
|
||||
cname.example.org. IN CNAME www.example.net.
|
||||
|
||||
service.example.org. IN SRV 8080 10 10 example.org.
|
||||
`
|
||||
|
|
|
@ -3,10 +3,6 @@ package test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
|
@ -30,10 +26,9 @@ func TestZoneExternalCNAMELookupWithoutProxy(t *testing.T) {
|
|||
}
|
||||
defer i.Stop()
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
|
||||
resp, err := p.Lookup(state, "cname.example.org.", dns.TypeA)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("cname.example.org.", dns.TypeA)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to receive reply, but didn't: %s", err)
|
||||
}
|
||||
|
@ -52,11 +47,12 @@ func TestZoneExternalCNAMELookupWithProxy(t *testing.T) {
|
|||
}
|
||||
defer rm()
|
||||
|
||||
// Corefile with for example without proxy section.
|
||||
corefile := `example.org:0 {
|
||||
file ` + name + ` {
|
||||
upstream 8.8.8.8
|
||||
// Corefile with for example proxy section.
|
||||
corefile := `.:0 {
|
||||
file ` + name + ` example.org {
|
||||
upstream
|
||||
}
|
||||
proxy . 8.8.8.8 8.8.4.4
|
||||
}
|
||||
`
|
||||
i, udp, _, err := CoreDNSServerAndPorts(corefile)
|
||||
|
@ -65,10 +61,9 @@ func TestZoneExternalCNAMELookupWithProxy(t *testing.T) {
|
|||
}
|
||||
defer i.Stop()
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
|
||||
resp, err := p.Lookup(state, "cname.example.org.", dns.TypeA)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("cname.example.org.", dns.TypeA)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to receive reply, but didn't: %s", err)
|
||||
}
|
||||
|
|
|
@ -6,9 +6,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/coredns/coredns/plugin/file"
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
@ -39,10 +36,9 @@ example.net:0 {
|
|||
}
|
||||
defer i.Stop()
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
|
||||
resp, err := p.Lookup(state, "example.org.", dns.TypeA)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("example.org.", dns.TypeA)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to receive reply, but didn't: %s", err)
|
||||
}
|
||||
|
@ -55,7 +51,7 @@ example.net:0 {
|
|||
|
||||
time.Sleep(2 * time.Second) // reload time
|
||||
|
||||
resp, err = p.Lookup(state, "example.org.", dns.TypeA)
|
||||
resp, err = dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
|
|
|
@ -3,10 +3,6 @@ package test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
|
@ -30,10 +26,9 @@ func TestZoneSRVAdditional(t *testing.T) {
|
|||
}
|
||||
defer i.Stop()
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
|
||||
resp, err := p.Lookup(state, "service.example.org.", dns.TypeSRV)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("service.example.org.", dns.TypeSRV)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected to receive reply, but didn't: %s", err)
|
||||
}
|
||||
|
|
|
@ -3,10 +3,6 @@ package test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
|
@ -24,10 +20,9 @@ func TestHostsInlineLookup(t *testing.T) {
|
|||
}
|
||||
defer i.Stop()
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
|
||||
resp, err := p.Lookup(state, "example.org.", dns.TypeA)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("example.org.", dns.TypeA)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
|
|
|
@ -3,36 +3,9 @@ package test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func TestProxyErratic(t *testing.T) {
|
||||
corefile := `example.org:0 {
|
||||
erratic {
|
||||
drop 2
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
backend, udp, _, err := CoreDNSServerAndPorts(corefile)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get CoreDNS serving instance: %s", err)
|
||||
}
|
||||
defer backend.Stop()
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
|
||||
// We do one lookup that should not time out.
|
||||
// After this the backend is marked unhealthy anyway. So basically this
|
||||
// tests that it times out.
|
||||
p.Lookup(state, "example.org.", dns.TypeA)
|
||||
}
|
||||
|
||||
func TestProxyThreeWay(t *testing.T) {
|
||||
// Run 3 CoreDNS server, 2 upstream ones and a proxy. 1 Upstream is unhealthy after 1 query, but after
|
||||
// that we should still be able to send to the other one.
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
func TestProxyWithHTTPCheckOK(t *testing.T) {
|
||||
healthCheckServer := httptest.NewServer(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
io.WriteString(w, "OK\n")
|
||||
}))
|
||||
defer healthCheckServer.Close()
|
||||
|
||||
healthCheckURL, err := url.Parse(healthCheckServer.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// TODO: use URL.Port() (Go 1.8+) once we've deprecated Go 1.7 support
|
||||
var healthCheckPort string
|
||||
if _, healthCheckPort, err = net.SplitHostPort(healthCheckURL.Host); err != nil {
|
||||
healthCheckPort = "80"
|
||||
}
|
||||
|
||||
name, rm, err := test.TempFile(".", exampleOrg)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create zone: %s", err)
|
||||
}
|
||||
defer rm()
|
||||
|
||||
// We have to bind to 127.0.0.1 because the server started by
|
||||
// httptest.NewServer does, and the IP addresses of the backend
|
||||
// DNS and HTTP servers must match.
|
||||
authoritativeCorefile := `example.org:0 {
|
||||
bind 127.0.0.1
|
||||
file ` + name + `
|
||||
}
|
||||
`
|
||||
|
||||
authoritativeInstance, authoritativeAddr, _, err := CoreDNSServerAndPorts(authoritativeCorefile)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get CoreDNS authoritative instance: %s", err)
|
||||
}
|
||||
defer authoritativeInstance.Stop()
|
||||
|
||||
proxyCorefile := `example.org:0 {
|
||||
proxy . ` + authoritativeAddr + ` {
|
||||
health_check /health:` + healthCheckPort + ` 1s
|
||||
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
proxyInstance, proxyAddr, _, err := CoreDNSServerAndPorts(proxyCorefile)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get CoreDNS proxy instance: %s", err)
|
||||
}
|
||||
defer proxyInstance.Stop()
|
||||
|
||||
p := proxy.NewLookup([]string{proxyAddr})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
resp, err := p.Lookup(state, "example.org.", dns.TypeA)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
// expect answer section with A record in it
|
||||
if len(resp.Answer) == 0 {
|
||||
t.Fatalf("Expected to at least one RR in the answer section, got none: %s", resp)
|
||||
}
|
||||
if resp.Answer[0].Header().Rrtype != dns.TypeA {
|
||||
t.Errorf("Expected RR to A, got: %d", resp.Answer[0].Header().Rrtype)
|
||||
}
|
||||
if resp.Answer[0].(*dns.A).A.String() != "127.0.0.1" {
|
||||
t.Errorf("Expected 127.0.0.1, got: %s", resp.Answer[0].(*dns.A).A.String())
|
||||
}
|
||||
}
|
|
@ -3,9 +3,7 @@ package test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
@ -29,46 +27,9 @@ func TestLookupProxy(t *testing.T) {
|
|||
}
|
||||
defer i.Stop()
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
resp, err := p.Lookup(state, "example.org.", dns.TypeA)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
// expect answer section with A record in it
|
||||
if len(resp.Answer) == 0 {
|
||||
t.Fatalf("Expected to at least one RR in the answer section, got none: %s", resp)
|
||||
}
|
||||
if resp.Answer[0].Header().Rrtype != dns.TypeA {
|
||||
t.Errorf("Expected RR to A, got: %d", resp.Answer[0].Header().Rrtype)
|
||||
}
|
||||
if resp.Answer[0].(*dns.A).A.String() != "127.0.0.1" {
|
||||
t.Errorf("Expected 127.0.0.1, got: %s", resp.Answer[0].(*dns.A).A.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLookupDnsWithForcedTcp(t *testing.T) {
|
||||
t.Parallel()
|
||||
name, rm, err := test.TempFile(".", exampleOrg)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create zone: %s", err)
|
||||
}
|
||||
defer rm()
|
||||
|
||||
corefile := `example.org:0 {
|
||||
file ` + name + `
|
||||
}
|
||||
`
|
||||
|
||||
i, _, tcp, err := CoreDNSServerAndPorts(corefile)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not get CoreDNS serving instance: %s", err)
|
||||
}
|
||||
defer i.Stop()
|
||||
|
||||
p := proxy.NewLookupWithOption([]string{tcp}, proxy.Options{ForceTCP: true})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
resp, err := p.Lookup(state, "example.org.", dns.TypeA)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("example.org.", dns.TypeA)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
|
@ -108,13 +69,12 @@ func BenchmarkProxyLookup(b *testing.B) {
|
|||
}
|
||||
defer i.Stop()
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("example.org.", dns.TypeA)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := p.Lookup(state, "example.org.", dns.TypeA)
|
||||
if err != nil {
|
||||
if _, err := dns.Exchange(m, udp); err != nil {
|
||||
b.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,6 @@ package test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
|
@ -26,9 +22,9 @@ func TestReverseCorefile(t *testing.T) {
|
|||
t.Fatalf("Could not get UDP listening port")
|
||||
}
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
resp, err := p.Lookup(state, "17.0.0.10.in-addr.arpa.", dns.TypePTR)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("17.0.0.10.in-addr.arpa.", dns.TypePTR)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
|
|
|
@ -3,9 +3,7 @@ package test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
@ -25,10 +23,9 @@ func TestEmptySecondaryZone(t *testing.T) {
|
|||
}
|
||||
defer i.Stop()
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
|
||||
resp, err := p.Lookup(state, "www.example.org.", dns.TypeA)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion("www.example.org.", dns.TypeA)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil {
|
||||
t.Fatal("Expected to receive reply, but didn't")
|
||||
}
|
||||
|
|
|
@ -3,9 +3,7 @@ package test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/coredns/coredns/plugin/proxy"
|
||||
"github.com/coredns/coredns/plugin/test"
|
||||
"github.com/coredns/coredns/request"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
@ -29,11 +27,10 @@ func TestLookupWildcard(t *testing.T) {
|
|||
}
|
||||
defer i.Stop()
|
||||
|
||||
p := proxy.NewLookup([]string{udp})
|
||||
state := request.Request{W: &test.ResponseWriter{}, Req: new(dns.Msg)}
|
||||
|
||||
for _, lookup := range []string{"a.w.example.org.", "a.a.w.example.org."} {
|
||||
resp, err := p.Lookup(state, lookup, dns.TypeTXT)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(lookup, dns.TypeTXT)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil || resp == nil {
|
||||
t.Fatalf("Expected to receive reply, but didn't for %s", lookup)
|
||||
}
|
||||
|
@ -64,7 +61,9 @@ func TestLookupWildcard(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, lookup := range []string{"w.example.org.", "a.w.example.org.", "a.a.w.example.org."} {
|
||||
resp, err := p.Lookup(state, lookup, dns.TypeSRV)
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(lookup, dns.TypeSRV)
|
||||
resp, err := dns.Exchange(m, udp)
|
||||
if err != nil || resp == nil {
|
||||
t.Fatal("Expected to receive reply, but didn't", lookup)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue