diff options
author | 2016-10-11 20:42:28 +0100 | |
---|---|---|
committer | 2016-10-11 20:42:28 +0100 | |
commit | 710c9b111f31b9d5f0b280739420b31c95adf14b (patch) | |
tree | 911351fb9fab47d9a9f23bbf9723491efb3d3b23 | |
parent | baea5eef2fd33f99a0612d413311dc0a15889a77 (diff) | |
download | coredns-710c9b111f31b9d5f0b280739420b31c95adf14b.tar.gz coredns-710c9b111f31b9d5f0b280739420b31c95adf14b.tar.zst coredns-710c9b111f31b9d5f0b280739420b31c95adf14b.zip |
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
-rw-r--r-- | core/coredns.go | 1 | ||||
-rw-r--r-- | core/dnsserver/config.go | 4 | ||||
-rw-r--r-- | core/dnsserver/directives.go | 1 | ||||
-rw-r--r-- | middleware/file/README.md | 3 | ||||
-rw-r--r-- | middleware/file/setup.go | 7 | ||||
-rw-r--r-- | middleware/file/setup_test.go | 2 | ||||
-rw-r--r-- | middleware/root/README.md | 21 | ||||
-rw-r--r-- | middleware/root/root.go | 42 | ||||
-rw-r--r-- | middleware/root/root_test.go | 104 | ||||
-rw-r--r-- | middleware/secondary/setup_test.go | 2 |
10 files changed, 184 insertions, 3 deletions
diff --git a/core/coredns.go b/core/coredns.go index 8b53c0dda..558b03efc 100644 --- a/core/coredns.go +++ b/core/coredns.go @@ -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" ) diff --git a/core/dnsserver/config.go b/core/dnsserver/config.go index cbe2fd5da..20ff8389a 100644 --- a/core/dnsserver/config.go +++ b/core/dnsserver/config.go @@ -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 diff --git a/core/dnsserver/directives.go b/core/dnsserver/directives.go index e2883a1c2..5898f8365 100644 --- a/core/dnsserver/directives.go +++ b/core/dnsserver/directives.go @@ -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", diff --git a/middleware/file/README.md b/middleware/file/README.md index eee2517b4..4cc27e2de 100644 --- a/middleware/file/README.md +++ b/middleware/file/README.md @@ -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. diff --git a/middleware/file/setup.go b/middleware/file/setup.go index dd1f17d9a..95e3209f4 100644 --- a/middleware/file/setup.go +++ b/middleware/file/setup.go @@ -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 diff --git a/middleware/file/setup_test.go b/middleware/file/setup_test.go index 9f37fd688..9fd9e3587 100644 --- a/middleware/file/setup_test.go +++ b/middleware/file/setup_test.go @@ -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 { diff --git a/middleware/root/README.md b/middleware/root/README.md new file mode 100644 index 000000000..5d09a31f3 --- /dev/null +++ b/middleware/root/README.md @@ -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 +~~~ diff --git a/middleware/root/root.go b/middleware/root/root.go new file mode 100644 index 000000000..6a7f9fe9f --- /dev/null +++ b/middleware/root/root.go @@ -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 +} diff --git a/middleware/root/root_test.go b/middleware/root/root_test.go new file mode 100644 index 000000000..06c81a05e --- /dev/null +++ b/middleware/root/root_test.go @@ -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 +} diff --git a/middleware/secondary/setup_test.go b/middleware/secondary/setup_test.go index 8eea1460f..c58483f6b 100644 --- a/middleware/secondary/setup_test.go +++ b/middleware/secondary/setup_test.go @@ -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 { |