mirror of
https://github.com/coredns/coredns.git
synced 2025-10-27 08:14:18 -04:00
middleware/root: add it (#330)
This PR adds the *root* middleware that specifies a path where all zone file (the *file* middleware is the only consumer now) can be found. It works the same as in Caddy. Documentation can be found in the README.md of the middleware. Fixes #307
This commit is contained in:
@@ -21,6 +21,7 @@ import (
|
||||
_ "github.com/miekg/coredns/middleware/pprof"
|
||||
_ "github.com/miekg/coredns/middleware/proxy"
|
||||
_ "github.com/miekg/coredns/middleware/rewrite"
|
||||
_ "github.com/miekg/coredns/middleware/root"
|
||||
_ "github.com/miekg/coredns/middleware/secondary"
|
||||
_ "github.com/miekg/coredns/middleware/whoami"
|
||||
)
|
||||
|
||||
@@ -17,6 +17,10 @@ type Config struct {
|
||||
// The port to listen on.
|
||||
Port string
|
||||
|
||||
// Root points to a base directory we we find user defined "things".
|
||||
// First consumer is the file middleware to looks for zone files in this place.
|
||||
Root string
|
||||
|
||||
// Middleware stack.
|
||||
Middleware []middleware.Middleware
|
||||
|
||||
|
||||
@@ -73,6 +73,7 @@ func RegisterDevDirective(name, before string) {
|
||||
// (after) them during a request, but they must not
|
||||
// care what middleware above them are doing.
|
||||
var directives = []string{
|
||||
"root",
|
||||
"bind",
|
||||
"health",
|
||||
"pprof",
|
||||
|
||||
@@ -13,7 +13,8 @@ zonefile.
|
||||
file DBFILE [ZONES...]
|
||||
~~~
|
||||
|
||||
* **DBFILE** the database file to read and parse.
|
||||
* **DBFILE** the database file to read and parse. If the path is relative the path from the *root*
|
||||
directive will be prepended to it.
|
||||
* **ZONES** zones it should be authoritative for. If empty, the zones from the configuration block
|
||||
are used.
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/miekg/coredns/core/dnsserver"
|
||||
"github.com/miekg/coredns/middleware"
|
||||
@@ -49,6 +50,8 @@ func fileParse(c *caddy.Controller) (Zones, error) {
|
||||
names := []string{}
|
||||
origins := []string{}
|
||||
|
||||
config := dnsserver.GetConfig(c)
|
||||
|
||||
for c.Next() {
|
||||
if c.Val() == "file" {
|
||||
// file db.file [zones...]
|
||||
@@ -64,6 +67,10 @@ func fileParse(c *caddy.Controller) (Zones, error) {
|
||||
origins = args
|
||||
}
|
||||
|
||||
if !path.IsAbs(fileName) && config.Root != "" {
|
||||
fileName = path.Join(config.Root, fileName)
|
||||
}
|
||||
|
||||
reader, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
// bail out
|
||||
|
||||
@@ -51,7 +51,7 @@ func TestFileParse(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
c := caddy.NewTestController("file", test.inputFileRules)
|
||||
c := caddy.NewTestController("dns", test.inputFileRules)
|
||||
actualZones, err := fileParse(c)
|
||||
|
||||
if err == nil && test.shouldErr {
|
||||
|
||||
21
middleware/root/README.md
Normal file
21
middleware/root/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# root
|
||||
|
||||
*root* simply specifies the root of where CoreDNS finds (e.g.) zone files.
|
||||
The default root is the current working directory of CoreDNS.
|
||||
A relative root path is relative to the current working directory.
|
||||
|
||||
## Syntax
|
||||
|
||||
~~~ txt
|
||||
root PATH
|
||||
~~~
|
||||
|
||||
**PATH** is the directory to set as CoreDNS' root.
|
||||
|
||||
## Examples
|
||||
|
||||
Serve zone data (when the *file* middleware is used) from `/etc/coredns/zones`:
|
||||
|
||||
~~~ txt
|
||||
root /etc/coredns/zones
|
||||
~~~
|
||||
42
middleware/root/root.go
Normal file
42
middleware/root/root.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package root
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/miekg/coredns/core/dnsserver"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
caddy.RegisterPlugin("root", caddy.Plugin{
|
||||
ServerType: "dns",
|
||||
Action: setup,
|
||||
})
|
||||
}
|
||||
|
||||
func setup(c *caddy.Controller) error {
|
||||
config := dnsserver.GetConfig(c)
|
||||
|
||||
for c.Next() {
|
||||
if !c.NextArg() {
|
||||
return c.ArgErr()
|
||||
}
|
||||
config.Root = c.Val()
|
||||
}
|
||||
|
||||
// Check if root path exists
|
||||
_, err := os.Stat(config.Root)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// Allow this, because the folder might appear later.
|
||||
// But make sure the user knows!
|
||||
log.Printf("[WARNING] Root path does not exist: %s", config.Root)
|
||||
} else {
|
||||
return c.Errf("Unable to access root path '%s': %v", config.Root, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
104
middleware/root/root_test.go
Normal file
104
middleware/root/root_test.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package root
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/miekg/coredns/core/dnsserver"
|
||||
|
||||
"github.com/mholt/caddy"
|
||||
)
|
||||
|
||||
func TestRoot(t *testing.T) {
|
||||
// Predefined error substrings
|
||||
parseErrContent := "Parse error:"
|
||||
unableToAccessErrContent := "Unable to access root path"
|
||||
|
||||
existingDirPath, err := getTempDirPath()
|
||||
if err != nil {
|
||||
t.Fatalf("BeforeTest: Failed to find an existing directory for testing! Error was: %v", err)
|
||||
}
|
||||
|
||||
nonExistingDir := filepath.Join(existingDirPath, "highly_unlikely_to_exist_dir")
|
||||
|
||||
existingFile, err := ioutil.TempFile("", "root_test")
|
||||
if err != nil {
|
||||
t.Fatalf("BeforeTest: Failed to create temp file for testing! Error was: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
existingFile.Close()
|
||||
os.Remove(existingFile.Name())
|
||||
}()
|
||||
|
||||
inaccessiblePath := getInaccessiblePath(existingFile.Name())
|
||||
|
||||
tests := []struct {
|
||||
input string
|
||||
shouldErr bool
|
||||
expectedRoot string // expected root, set to the controller. Empty for negative cases.
|
||||
expectedErrContent string // substring from the expected error. Empty for positive cases.
|
||||
}{
|
||||
// positive
|
||||
{
|
||||
fmt.Sprintf(`root %s`, nonExistingDir), false, nonExistingDir, "",
|
||||
},
|
||||
{
|
||||
fmt.Sprintf(`root %s`, existingDirPath), false, existingDirPath, "",
|
||||
},
|
||||
// negative
|
||||
{
|
||||
`root `, true, "", parseErrContent,
|
||||
},
|
||||
{
|
||||
fmt.Sprintf(`root %s`, inaccessiblePath), true, "", unableToAccessErrContent,
|
||||
},
|
||||
{
|
||||
fmt.Sprintf(`root {
|
||||
%s
|
||||
}`, existingDirPath), true, "", parseErrContent,
|
||||
},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
c := caddy.NewTestController("dns", test.input)
|
||||
err := setup(c)
|
||||
cfg := dnsserver.GetConfig(c)
|
||||
|
||||
if test.shouldErr && err == nil {
|
||||
t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if !test.shouldErr {
|
||||
t.Errorf("Test %d: Expected no error but found one for input %s. Error was: %v", i, test.input, err)
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), test.expectedErrContent) {
|
||||
t.Errorf("Test %d: Expected error to contain: %v, found error: %v, input: %s", i, test.expectedErrContent, err, test.input)
|
||||
}
|
||||
}
|
||||
|
||||
// check root only if we are in a positive test.
|
||||
if !test.shouldErr && test.expectedRoot != cfg.Root {
|
||||
t.Errorf("Root not correctly set for input %s. Expected: %s, actual: %s", test.input, test.expectedRoot, cfg.Root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getTempDirPath returnes the path to the system temp directory. If it does not exists - an error is returned.
|
||||
func getTempDirPath() (string, error) {
|
||||
tempDir := os.TempDir()
|
||||
_, err := os.Stat(tempDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tempDir, nil
|
||||
}
|
||||
|
||||
func getInaccessiblePath(file string) string {
|
||||
return filepath.Join("C:", "file\x00name") // null byte in filename is not allowed on Windows AND unix
|
||||
}
|
||||
@@ -29,7 +29,7 @@ func TestSecondaryParse(t *testing.T) {
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
c := caddy.NewTestController("secondary", test.inputFileRules)
|
||||
c := caddy.NewTestController("dns", test.inputFileRules)
|
||||
_, err := secondaryParse(c)
|
||||
|
||||
if err == nil && test.shouldErr {
|
||||
|
||||
Reference in New Issue
Block a user