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:
Miek Gieben
2019-01-13 16:54:49 +00:00
committed by GitHub
parent 6b56a9c921
commit 9c16ed1d14
55 changed files with 184 additions and 1349 deletions

View File

@@ -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

View File

@@ -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.`

View File

@@ -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= )
`
*/

View File

@@ -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 {

View File

@@ -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())

View File

@@ -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. {

View File

@@ -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.