aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Arthur Outhenin-Chalandre <arthur@cri.epita.fr> 2023-01-27 17:35:24 +0100
committerGravatar GitHub <noreply@github.com> 2023-01-27 11:35:24 -0500
commitbf7c2cf37bf35ba3a7596cb32c5a3607d896f95d (patch)
tree83a2dc9ae6ec2bd04074f42dd0d5f1962f916f68
parentd3e4fc78c3b80c1768b0f95ad849b07ba9968ba5 (diff)
downloadcoredns-bf7c2cf37bf35ba3a7596cb32c5a3607d896f95d.tar.gz
coredns-bf7c2cf37bf35ba3a7596cb32c5a3607d896f95d.tar.zst
coredns-bf7c2cf37bf35ba3a7596cb32c5a3607d896f95d.zip
plugin/cache: add a new keepttl option (#5879)
adds a new option `keepttl` to the cache plugin Signed-off-by: Arthur Outhenin-Chalandre <arthur.outhenin-chalandre@proton.ch>
-rw-r--r--plugin/cache/README.md8
-rw-r--r--plugin/cache/cache.go3
-rw-r--r--plugin/cache/cache_test.go38
-rw-r--r--plugin/cache/handler.go5
-rw-r--r--plugin/cache/setup.go5
-rw-r--r--plugin/cache/setup_test.go29
6 files changed, 87 insertions, 1 deletions
diff --git a/plugin/cache/README.md b/plugin/cache/README.md
index 6fc20ae2c..d516a91db 100644
--- a/plugin/cache/README.md
+++ b/plugin/cache/README.md
@@ -39,6 +39,7 @@ cache [TTL] [ZONES...] {
serve_stale [DURATION] [REFRESH_MODE]
servfail DURATION
disable success|denial [ZONES...]
+ keepttl
}
~~~
@@ -69,6 +70,11 @@ cache [TTL] [ZONES...] {
greater than 5 minutes.
* `disable` disable the success or denial cache for the listed **ZONES**. If no **ZONES** are given, the specified
cache will be disabled for all zones.
+* `keepttl` do not age TTL when serving responses from cache. The entry will still be removed from cache
+ when the TTL expires as normal, but until it expires responses will include the original TTL instead
+ of the remaining TTL. This can be useful if CoreDNS is used as an authoritative server and you want
+ to serve a consistent TTL to downstream clients. This is **NOT** recommended when CoreDNS is caching
+ records it is not authoritative for because it could result in downstream clients using stale answers.
## Capacity and Eviction
@@ -135,4 +141,4 @@ example.org {
disable denial sub.example.org
}
}
-~~~ \ No newline at end of file
+~~~
diff --git a/plugin/cache/cache.go b/plugin/cache/cache.go
index 54f2587fa..563b2abbe 100644
--- a/plugin/cache/cache.go
+++ b/plugin/cache/cache.go
@@ -48,6 +48,9 @@ type Cache struct {
pexcept []string
nexcept []string
+ // Keep ttl option
+ keepttl bool
+
// Testing.
now func() time.Time
}
diff --git a/plugin/cache/cache_test.go b/plugin/cache/cache_test.go
index 861e9b8fd..5a023f400 100644
--- a/plugin/cache/cache_test.go
+++ b/plugin/cache/cache_test.go
@@ -690,6 +690,44 @@ func TestCacheWildcardMetadata(t *testing.T) {
}
}
+func TestCacheKeepTTL(t *testing.T) {
+ defaultTtl := 60
+
+ c := New()
+ c.Next = ttlBackend(defaultTtl)
+
+ req := new(dns.Msg)
+ req.SetQuestion("cached.org.", dns.TypeA)
+ ctx := context.TODO()
+
+ // Cache cached.org. with 60s TTL
+ rec := dnstest.NewRecorder(&test.ResponseWriter{})
+ c.keepttl = true
+ c.ServeDNS(ctx, rec, req)
+
+ tests := []struct {
+ name string
+ futureSeconds int
+ }{
+ {"cached.org.", 0},
+ {"cached.org.", 30},
+ {"uncached.org.", 60},
+ }
+
+ for i, tt := range tests {
+ rec := dnstest.NewRecorder(&test.ResponseWriter{})
+ c.now = func() time.Time { return time.Now().Add(time.Duration(tt.futureSeconds) * time.Second) }
+ r := req.Copy()
+ r.SetQuestion(tt.name, dns.TypeA)
+ c.ServeDNS(ctx, rec, r)
+
+ recTtl := rec.Msg.Answer[0].Header().Ttl
+ if defaultTtl != int(recTtl) {
+ t.Errorf("Test %d: expecting TTL=%d, got TTL=%d", i, defaultTtl, recTtl)
+ }
+ }
+}
+
// wildcardMetadataBackend mocks a backend that reponds with a response for qname synthesized by wildcard
// and sets the zone/wildcard metadata value
func wildcardMetadataBackend(qname, wildcard string) plugin.Handler {
diff --git a/plugin/cache/handler.go b/plugin/cache/handler.go
index e60637af8..de7cae456 100644
--- a/plugin/cache/handler.go
+++ b/plugin/cache/handler.go
@@ -71,6 +71,11 @@ func (c *Cache) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg)
})
}
+ if c.keepttl {
+ // If keepttl is enabled we fake the current time to the stored
+ // one so that we always get the original TTL
+ now = i.stored
+ }
resp := i.toMsg(r, now, do, ad)
w.WriteMsg(resp)
return dns.RcodeSuccess, nil
diff --git a/plugin/cache/setup.go b/plugin/cache/setup.go
index 6a537d986..7e5dfa174 100644
--- a/plugin/cache/setup.go
+++ b/plugin/cache/setup.go
@@ -240,6 +240,11 @@ func cacheParse(c *caddy.Controller) (*Cache, error) {
default:
return nil, fmt.Errorf("cache type for disable must be %q or %q", Success, Denial)
}
+ case "keepttl":
+ if len(args) != 0 {
+ return nil, c.ArgErr()
+ }
+ ca.keepttl = true
default:
return nil, c.ArgErr()
}
diff --git a/plugin/cache/setup_test.go b/plugin/cache/setup_test.go
index 5d8b9653c..46ac5bd9e 100644
--- a/plugin/cache/setup_test.go
+++ b/plugin/cache/setup_test.go
@@ -231,3 +231,32 @@ func TestDisable(t *testing.T) {
}
}
}
+
+func TestKeepttl(t *testing.T) {
+ tests := []struct {
+ input string
+ shouldErr bool
+ }{
+ // positive
+ {"keepttl", false},
+ // negative
+ {"keepttl arg1", true},
+ }
+ for i, test := range tests {
+ c := caddy.NewTestController("dns", fmt.Sprintf("cache {\n%s\n}", test.input))
+ ca, err := cacheParse(c)
+ if test.shouldErr && err == nil {
+ t.Errorf("Test %v: Expected error but found nil", i)
+ continue
+ } else if !test.shouldErr && err != nil {
+ t.Errorf("Test %v: Expected no error but found error: %v", i, err)
+ continue
+ }
+ if test.shouldErr {
+ continue
+ }
+ if !ca.keepttl {
+ t.Errorf("Test %v: Expected keepttl enabled but disabled", i)
+ }
+ }
+}