aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Miek Gieben <miek@miek.nl> 2016-10-11 20:42:28 +0100
committerGravatar GitHub <noreply@github.com> 2016-10-11 20:42:28 +0100
commit710c9b111f31b9d5f0b280739420b31c95adf14b (patch)
tree911351fb9fab47d9a9f23bbf9723491efb3d3b23
parentbaea5eef2fd33f99a0612d413311dc0a15889a77 (diff)
downloadcoredns-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.go1
-rw-r--r--core/dnsserver/config.go4
-rw-r--r--core/dnsserver/directives.go1
-rw-r--r--middleware/file/README.md3
-rw-r--r--middleware/file/setup.go7
-rw-r--r--middleware/file/setup_test.go2
-rw-r--r--middleware/root/README.md21
-rw-r--r--middleware/root/root.go42
-rw-r--r--middleware/root/root_test.go104
-rw-r--r--middleware/secondary/setup_test.go2
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 {