plugin/file: load secondary zones lazily on startup (#2944)
This fixes a long standing bug: fixes: #1609 Load secondary zones in a go-routine; this required another mutex to protect some fields; I think those were needded anyway because a transfer can also happen when we're running; we just didn't have a test for that situation. The test had to be changed to wait for the transfer to happen at this is async now. Signed-off-by: Miek Gieben <miek@miek.nl>
This commit is contained in:
parent
2c1f5009be
commit
3a0c7c6153
7 changed files with 21 additions and 17 deletions
|
@ -18,8 +18,8 @@ var log = clog.NewWithPlugin("file")
|
||||||
type (
|
type (
|
||||||
// File is the plugin that reads zone data from disk.
|
// File is the plugin that reads zone data from disk.
|
||||||
File struct {
|
File struct {
|
||||||
Next plugin.Handler
|
Next plugin.Handler
|
||||||
Zones Zones
|
Zones
|
||||||
}
|
}
|
||||||
|
|
||||||
// Zones maps zone names to a *Zone.
|
// Zones maps zone names to a *Zone.
|
||||||
|
|
|
@ -44,7 +44,9 @@ func (z *Zone) Lookup(ctx context.Context, state request.Request, qname string)
|
||||||
// If z is a secondary zone we might not have transferred it, meaning we have
|
// If z is a secondary zone we might not have transferred it, meaning we have
|
||||||
// all zone context setup, except the actual record. This means (for one thing) the apex
|
// all zone context setup, except the actual record. This means (for one thing) the apex
|
||||||
// is empty and we don't have a SOA record.
|
// is empty and we don't have a SOA record.
|
||||||
|
z.apexMu.RLock()
|
||||||
soa := z.Apex.SOA
|
soa := z.Apex.SOA
|
||||||
|
z.apexMu.RUnlock()
|
||||||
if soa == nil {
|
if soa == nil {
|
||||||
return nil, nil, nil, ServerFailure
|
return nil, nil, nil, ServerFailure
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,9 +51,11 @@ Transfer:
|
||||||
return Err
|
return Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
z.apexMu.Lock()
|
||||||
z.Tree = z1.Tree
|
z.Tree = z1.Tree
|
||||||
z.Apex = z1.Apex
|
z.Apex = z1.Apex
|
||||||
*z.Expired = false
|
*z.Expired = false
|
||||||
|
z.apexMu.Unlock()
|
||||||
log.Infof("Transferred: %s from %s", z.origin, tr)
|
log.Infof("Transferred: %s from %s", z.origin, tr)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ func TestShouldTransfer(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer s.Shutdown()
|
defer s.Shutdown()
|
||||||
|
|
||||||
z := new(Zone)
|
z := NewZone("testzone", "test")
|
||||||
z.origin = testZone
|
z.origin = testZone
|
||||||
z.TransferFrom = []string{addrstr}
|
z.TransferFrom = []string{addrstr}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,8 @@ type Zone struct {
|
||||||
origLen int
|
origLen int
|
||||||
file string
|
file string
|
||||||
*tree.Tree
|
*tree.Tree
|
||||||
Apex Apex
|
Apex
|
||||||
|
apexMu sync.RWMutex
|
||||||
|
|
||||||
TransferTo []string
|
TransferTo []string
|
||||||
StartupOnce sync.Once
|
StartupOnce sync.Once
|
||||||
|
@ -32,7 +33,7 @@ type Zone struct {
|
||||||
LastReloaded time.Time
|
LastReloaded time.Time
|
||||||
reloadMu sync.RWMutex
|
reloadMu sync.RWMutex
|
||||||
reloadShutdown chan bool
|
reloadShutdown chan bool
|
||||||
Upstream *upstream.Upstream // Upstream for looking up external 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.
|
// Apex contains the apex records of a zone: SOA, NS and their potential signatures.
|
||||||
|
@ -55,7 +56,6 @@ func NewZone(name, file string) *Zone {
|
||||||
LastReloaded: time.Now(),
|
LastReloaded: time.Now(),
|
||||||
}
|
}
|
||||||
*z.Expired = false
|
*z.Expired = false
|
||||||
|
|
||||||
return z
|
return z
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,11 +186,6 @@ func (z *Zone) All() []dns.RR {
|
||||||
return append([]dns.RR{z.Apex.SOA}, records...)
|
return append([]dns.RR{z.Apex.SOA}, records...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print prints the zone's tree to stdout.
|
|
||||||
func (z *Zone) Print() {
|
|
||||||
z.Tree.Print()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NameFromRight returns the labels from the right, staring with the
|
// NameFromRight returns the labels from the right, staring with the
|
||||||
// origin and then i labels extra. When we are overshooting the name
|
// origin and then i labels extra. When we are overshooting the name
|
||||||
// the returned boolean is set to true.
|
// the returned boolean is set to true.
|
||||||
|
|
|
@ -29,8 +29,8 @@ func setup(c *caddy.Controller) error {
|
||||||
if len(z.TransferFrom) > 0 {
|
if len(z.TransferFrom) > 0 {
|
||||||
c.OnStartup(func() error {
|
c.OnStartup(func() error {
|
||||||
z.StartupOnce.Do(func() {
|
z.StartupOnce.Do(func() {
|
||||||
z.TransferIn()
|
|
||||||
go func() {
|
go func() {
|
||||||
|
z.TransferIn()
|
||||||
z.Update()
|
z.Update()
|
||||||
}()
|
}()
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,6 +2,7 @@ package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/coredns/coredns/plugin/test"
|
"github.com/coredns/coredns/plugin/test"
|
||||||
|
|
||||||
|
@ -69,12 +70,16 @@ func TestSecondaryZoneTransfer(t *testing.T) {
|
||||||
m := new(dns.Msg)
|
m := new(dns.Msg)
|
||||||
m.SetQuestion("example.org.", dns.TypeSOA)
|
m.SetQuestion("example.org.", dns.TypeSOA)
|
||||||
|
|
||||||
r, err := dns.Exchange(m, udp)
|
var r *dns.Msg
|
||||||
if err != nil {
|
// This is now async; we we need to wait for it to be transfered.
|
||||||
t.Fatalf("Expected to receive reply, but didn't: %s", err)
|
for i := 0; i < 10; i++ {
|
||||||
|
r, _ = dns.Exchange(m, udp)
|
||||||
|
if len(r.Answer) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(100 * time.Microsecond)
|
||||||
}
|
}
|
||||||
|
if len(r.Answer) != 0 {
|
||||||
if len(r.Answer) == 0 {
|
|
||||||
t.Fatalf("Expected answer section")
|
t.Fatalf("Expected answer section")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue