Skip to content

Commit 0ff9926

Browse files
committed
Setup local cache with TTL for queries
1 parent ff90b5d commit 0ff9926

File tree

5 files changed

+87
-44
lines changed

5 files changed

+87
-44
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.14
55
require (
66
github.com/caddyserver/caddy v1.0.5
77
github.com/coredns/coredns v1.7.0
8+
github.com/imkira/go-ttlmap v2.0.0+incompatible
89
github.com/miekg/dns v1.1.29
910
github.com/prometheus/client_golang v1.7.1
1011
github.com/stretchr/testify v1.5.1

go.sum

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,9 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
235235
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=
236236
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
237237
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
238+
github.com/imkira/go-ttlmap v1.0.1 h1:6inPHYxX7xVBaxEicVlfrlWHZfBKz1Rf2Y3ah+h67BA=
239+
github.com/imkira/go-ttlmap v2.0.0+incompatible h1:U0HKc010vF8qEQY4pvmZ7QLRSOpV4i87DKRngNHsqb0=
240+
github.com/imkira/go-ttlmap v2.0.0+incompatible/go.mod h1:O+nQkEb7vlapbVn5HZ3m4FItjV+qNG3GY9W/sb3l7aw=
238241
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
239242
github.com/infobloxopen/go-trees v0.0.0-20190313150506-2af4e13f9062/go.mod h1:PcNJqIlcX/dj3DTG/+QQnRvSgTMG6CLpRMjWcv4+J6w=
240243
github.com/jimstudt/http-authentication v0.0.0-20140401203705-3eca13d6893a/go.mod h1:wK6yTYYcgjHE1Z1QtXACPDjcFJyBskHEdagmnq3vsP8=

netbox.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ func (n Netbox) ServeDNS(ctx context.Context, w dns.ResponseWriter, r *dns.Msg)
4545
answers := []dns.RR{}
4646
state := request.Request{W: w, Req: r}
4747

48-
ip_address := query(n.Url, n.Token, strings.TrimRight(state.QName(), "."))
48+
ip_address := query(n.Url, n.Token, strings.TrimRight(state.QName(), "."), n.CacheDuration)
4949
// no IP is found in netbox pass processing to the next plugin
5050
if len(ip_address) == 0 {
5151
return plugin.NextOrFailure(n.Name(), n.Next, ctx, w, r)

query.go

Lines changed: 43 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"time"
2323

2424
clog "github.com/coredns/coredns/plugin/pkg/log"
25+
"github.com/imkira/go-ttlmap"
2526
)
2627

2728
type Record struct {
@@ -33,52 +34,53 @@ type RecordsList struct {
3334
Records []Record `json:"results"`
3435
}
3536

36-
var stupidCache = make(map[string]string)
37-
38-
func query(url, token, dns_name string) string {
39-
40-
clog.Debug(stupidCache)
41-
42-
records := RecordsList{}
43-
client := &http.Client{}
44-
var resp *http.Response
45-
clog.Debug("Querying ", fmt.Sprintf("%s/?dns_name=%s", url, dns_name))
46-
req, err := http.NewRequest("GET", fmt.Sprintf("%s/?dns_name=%s", url, dns_name), nil)
47-
req.Header.Set("Authorization", fmt.Sprintf("Token %s", token))
48-
49-
for i := 1; i <= 10; i++ {
50-
resp, err = client.Do(req)
51-
37+
var localCache = ttlmap.New(nil)
38+
39+
func query(url, token, dns_name string, duration time.Duration) string {
40+
item, err := localCache.Get(dns_name)
41+
if err == nil {
42+
clog.Debug(fmt.Sprintf("Found in local cache %s", dns_name))
43+
return item.Value().(string)
44+
} else {
45+
records := RecordsList{}
46+
client := &http.Client{}
47+
var resp *http.Response
48+
clog.Debug("Querying ", fmt.Sprintf("%s/?dns_name=%s", url, dns_name))
49+
req, err := http.NewRequest("GET", fmt.Sprintf("%s/?dns_name=%s", url, dns_name), nil)
50+
req.Header.Set("Authorization", fmt.Sprintf("Token %s", token))
51+
52+
for i := 1; i <= 10; i++ {
53+
resp, err = client.Do(req)
54+
55+
if err != nil {
56+
clog.Fatalf("HTTP Error %v", err)
57+
}
58+
59+
if resp.StatusCode == http.StatusOK {
60+
break
61+
}
62+
63+
time.Sleep(1 * time.Second)
64+
}
65+
// TODO: check that we got status code 200
66+
body, err := ioutil.ReadAll(resp.Body)
5267
if err != nil {
53-
clog.Fatalf("HTTP Error %v", err)
68+
clog.Fatalf("Error reading body %v", err)
5469
}
5570

56-
if resp.StatusCode == http.StatusOK {
57-
break
71+
jsonAns := string(body)
72+
err = json.Unmarshal([]byte(jsonAns), &records)
73+
if err != nil {
74+
clog.Fatalf("could not unmarshal response %v", err)
5875
}
5976

60-
time.Sleep(1 * time.Second)
61-
}
62-
// TODO: check that we got status code 200
63-
body, err := ioutil.ReadAll(resp.Body)
64-
if err != nil {
65-
clog.Fatalf("Error reading body %v", err)
66-
}
67-
68-
jsonAns := string(body)
69-
err = json.Unmarshal([]byte(jsonAns), &records)
70-
if err != nil {
71-
clog.Fatalf("could not unmarshal response %v", err)
72-
}
77+
if len(records.Records) == 0 {
78+
clog.Info("Recored not found in", jsonAns)
79+
return ""
80+
}
7381

74-
if len(records.Records) == 0 {
75-
clog.Info("Recored not found in", jsonAns)
76-
return ""
82+
ip_address := strings.Split(records.Records[0].Address, "/")[0]
83+
localCache.Set(dns_name, ttlmap.NewItem(ip_address, ttlmap.WithTTL(duration)), nil)
84+
return ip_address
7785
}
78-
79-
ip_address := strings.Split(records.Records[0].Address, "/")[0]
80-
81-
stupidCache[dns_name] = ip_address
82-
83-
return ip_address
8486
}

query_test.go

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
package netbox
1616

1717
import (
18+
"github.com/stretchr/testify/assert"
1819
"gopkg.in/h2non/gock.v1"
1920
"testing"
21+
"time"
2022
)
2123

2224
func TestQuery(t *testing.T) {
@@ -27,7 +29,7 @@ func TestQuery(t *testing.T) {
2729
`{"count":1, "results":[{"address": "10.0.0.2/25", "dns_name": "my_host"}]}`)
2830

2931
want := "10.0.0.2"
30-
got := query("https://example.org/api/ipam/ip-addresses", "mytoken", "my_host")
32+
got := query("https://example.org/api/ipam/ip-addresses", "mytoken", "my_host", time.Millisecond*100)
3133
if got != want {
3234
t.Fatalf("Expected %s but got %s", want, got)
3335
}
@@ -42,9 +44,44 @@ func TestNoSuchHost(t *testing.T) {
4244
200).BodyString(`{"count":0,"next":null,"previous":null,"results":[]}`)
4345

4446
want := ""
45-
got := query("https://example.org/api/ipam/ip-addresses", "mytoken", "NoSuchHost")
47+
got := query("https://example.org/api/ipam/ip-addresses", "mytoken", "NoSuchHost", time.Millisecond*100)
4648
if got != want {
4749
t.Fatalf("Expected empty string but got %s", got)
4850
}
4951

5052
}
53+
54+
func TestLocalCache(t *testing.T) {
55+
defer gock.Off() // Flush pending mocks after test execution
56+
gock.New("https://example.org/api/ipam/ip-addresses/").MatchParams(
57+
map[string]string{"dns_name": "my_host"}).Reply(
58+
200).BodyString(
59+
`{"count":1, "results":[{"address": "10.0.0.2/25", "dns_name": "my_host"}]}`)
60+
61+
ip_address := ""
62+
63+
got := query("https://example.org/api/ipam/ip-addresses", "mytoken", "my_host", time.Millisecond*100)
64+
65+
item, err := localCache.Get("my_host")
66+
if err == nil {
67+
ip_address = item.Value().(string)
68+
}
69+
70+
assert.Equal(t, got, ip_address, "local cache item didn't match")
71+
72+
}
73+
74+
func TestLocalCacheExpiration(t *testing.T) {
75+
defer gock.Off() // Flush pending mocks after test execution
76+
gock.New("https://example.org/api/ipam/ip-addresses/").MatchParams(
77+
map[string]string{"dns_name": "my_host"}).Reply(
78+
200).BodyString(
79+
`{"count":1, "results":[{"address": "10.0.0.2/25", "dns_name": "my_host"}]}`)
80+
81+
query("https://example.org/api/ipam/ip-addresses", "mytoken", "my_host", time.Millisecond*100)
82+
<-time.After(101 * time.Millisecond)
83+
item, err := localCache.Get("my_host")
84+
if err != nil {
85+
t.Fatalf("Expected errors, but got: %v", item)
86+
}
87+
}

0 commit comments

Comments
 (0)