Skip to content

Commit fb584aa

Browse files
authored
Merge pull request #4049 from anders-swanson/oracle-provider-cache
oracle provider: dns zone cache
2 parents fc87eaf + dbaca73 commit fb584aa

File tree

8 files changed

+149
-5
lines changed

8 files changed

+149
-5
lines changed

docs/tutorials/oracle.md

+3
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ spec:
180180
# Specifies the OCI DNS Zone scope, defaults to GLOBAL.
181181
# May be GLOBAL, PRIVATE, or an empty value to specify both GLOBAL and PRIVATE OCI DNS Zones
182182
# - --oci-zone-scope=GLOBAL
183+
# Specifies the zone cache duration, defaults to 0s. If set to 0s, the zone cache is disabled.
184+
# Use of zone caching is recommended to reduce the amount of requests sent to OCI DNS.
185+
# - --oci-zones-cache-duration=0s
183186
volumeMounts:
184187
- name: config
185188
mountPath: /etc/kubernetes/

main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ func main() {
360360
} else {
361361
config, err = oci.LoadOCIConfig(cfg.OCIConfigFile)
362362
}
363-
363+
config.ZoneCacheDuration = cfg.OCIZoneCacheDuration
364364
if err == nil {
365365
p, err = oci.NewOCIProvider(*config, domainFilter, zoneIDFilter, cfg.OCIZoneScope, cfg.DryRun)
366366
}

pkg/apis/externaldns/types.go

+3
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ type Config struct {
136136
OCICompartmentOCID string
137137
OCIAuthInstancePrincipal bool
138138
OCIZoneScope string
139+
OCIZoneCacheDuration time.Duration
139140
InMemoryZones []string
140141
OVHEndpoint string
141142
OVHApiRateLimit int
@@ -293,6 +294,7 @@ var defaultConfig = &Config{
293294
InfobloxCacheDuration: 0,
294295
OCIConfigFile: "/etc/kubernetes/oci.yaml",
295296
OCIZoneScope: "GLOBAL",
297+
OCIZoneCacheDuration: 0 * time.Second,
296298
InMemoryZones: []string{},
297299
OVHEndpoint: "ovh-eu",
298300
OVHApiRateLimit: 20,
@@ -527,6 +529,7 @@ func (cfg *Config) ParseFlags(args []string) error {
527529
app.Flag("oci-compartment-ocid", "When using the OCI provider, specify the OCID of the OCI compartment containing all managed zones and records. Required when using OCI IAM instance principal authentication.").StringVar(&cfg.OCICompartmentOCID)
528530
app.Flag("oci-zone-scope", "When using OCI provider, filter for zones with this scope (optional, options: GLOBAL, PRIVATE). Defaults to GLOBAL, setting to empty value will target both.").Default(defaultConfig.OCIZoneScope).EnumVar(&cfg.OCIZoneScope, "", "GLOBAL", "PRIVATE")
529531
app.Flag("oci-auth-instance-principal", "When using the OCI provider, specify whether OCI IAM instance principal authentication should be used (instead of key-based auth via the OCI config file).").Default(strconv.FormatBool(defaultConfig.OCIAuthInstancePrincipal)).BoolVar(&cfg.OCIAuthInstancePrincipal)
532+
app.Flag("oci-zones-cache-duration", "When using the OCI provider, set the zones list cache TTL (0s to disable).").Default(defaultConfig.OCIZoneCacheDuration.String()).DurationVar(&cfg.OCIZoneCacheDuration)
530533
app.Flag("rcodezero-txt-encrypt", "When using the Rcodezero provider with txt registry option, set if TXT rrs are encrypted (default: false)").Default(strconv.FormatBool(defaultConfig.RcodezeroTXTEncrypt)).BoolVar(&cfg.RcodezeroTXTEncrypt)
531534
app.Flag("inmemory-zone", "Provide a list of pre-configured zones for the inmemory provider; specify multiple times for multiple zones (optional)").Default("").StringsVar(&cfg.InMemoryZones)
532535
app.Flag("ovh-endpoint", "When using the OVH provider, specify the endpoint (default: ovh-eu)").Default(defaultConfig.OVHEndpoint).StringVar(&cfg.OVHEndpoint)

pkg/apis/externaldns/types_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ var (
9595
InfobloxMaxResults: 0,
9696
OCIConfigFile: "/etc/kubernetes/oci.yaml",
9797
OCIZoneScope: "GLOBAL",
98+
OCIZoneCacheDuration: 0 * time.Second,
9899
InMemoryZones: []string{""},
99100
OVHEndpoint: "ovh-eu",
100101
OVHApiRateLimit: 20,
@@ -205,6 +206,7 @@ var (
205206
InfobloxMaxResults: 2000,
206207
OCIConfigFile: "oci.yaml",
207208
OCIZoneScope: "PRIVATE",
209+
OCIZoneCacheDuration: 30 * time.Second,
208210
InMemoryZones: []string{"example.org", "company.com"},
209211
OVHEndpoint: "ovh-ca",
210212
OVHApiRateLimit: 42,
@@ -328,6 +330,7 @@ func TestParseFlags(t *testing.T) {
328330
"--pdns-skip-tls-verify",
329331
"--oci-config-file=oci.yaml",
330332
"--oci-zone-scope=PRIVATE",
333+
"--oci-zones-cache-duration=30s",
331334
"--tls-ca=/path/to/ca.crt",
332335
"--tls-client-cert=/path/to/cert.pem",
333336
"--tls-client-cert-key=/path/to/key.pem",
@@ -449,6 +452,7 @@ func TestParseFlags(t *testing.T) {
449452
"EXTERNAL_DNS_INFOBLOX_MAX_RESULTS": "2000",
450453
"EXTERNAL_DNS_OCI_CONFIG_FILE": "oci.yaml",
451454
"EXTERNAL_DNS_OCI_ZONE_SCOPE": "PRIVATE",
455+
"EXTERNAL_DNS_OCI_ZONES_CACHE_DURATION": "30s",
452456
"EXTERNAL_DNS_INMEMORY_ZONE": "example.org\ncompany.com",
453457
"EXTERNAL_DNS_OVH_ENDPOINT": "ovh-ca",
454458
"EXTERNAL_DNS_OVH_API_RATE_LIMIT": "42",

provider/oci/cache.go

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package oci
18+
19+
import (
20+
"time"
21+
22+
"github.com/oracle/oci-go-sdk/v65/dns"
23+
)
24+
25+
type zoneCache struct {
26+
age time.Time
27+
duration time.Duration
28+
zones map[string]dns.ZoneSummary
29+
}
30+
31+
func (z *zoneCache) Reset(zones map[string]dns.ZoneSummary) {
32+
if z.duration > time.Duration(0) {
33+
z.age = time.Now()
34+
z.zones = zones
35+
}
36+
}
37+
38+
func (z *zoneCache) Get() map[string]dns.ZoneSummary {
39+
return z.zones
40+
}
41+
42+
func (z *zoneCache) Expired() bool {
43+
return len(z.zones) < 1 || time.Since(z.age) > z.duration
44+
}

provider/oci/cache_test.go

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package oci
18+
19+
import (
20+
"github.com/oracle/oci-go-sdk/v65/dns"
21+
"github.com/stretchr/testify/assert"
22+
"testing"
23+
"time"
24+
)
25+
26+
func TestZoneCache(t *testing.T) {
27+
now := time.Now()
28+
var testCases = map[string]struct {
29+
z *zoneCache
30+
expired bool
31+
}{
32+
"inactive-zone-cache": {
33+
&zoneCache{
34+
duration: 0 * time.Second,
35+
},
36+
true,
37+
},
38+
"empty-active-zone-cache": {
39+
&zoneCache{
40+
duration: 30 * time.Second,
41+
},
42+
true,
43+
},
44+
"expired-zone-cache": {
45+
&zoneCache{
46+
age: now.Add(300 * time.Second),
47+
duration: 30 * time.Second,
48+
},
49+
true,
50+
},
51+
"active-zone-cache": {
52+
&zoneCache{
53+
zones: map[string]dns.ZoneSummary{
54+
zoneIdBaz: testPrivateZoneSummaryBaz,
55+
},
56+
duration: 30 * time.Second,
57+
},
58+
true,
59+
},
60+
}
61+
62+
for name, testCase := range testCases {
63+
t.Run(name, func(t *testing.T) {
64+
assert.Equal(t, testCase.expired, testCase.z.Expired())
65+
var resetZoneLength = 1
66+
if testCase.z.duration == 0 {
67+
resetZoneLength = 0
68+
}
69+
testCase.z.Reset(map[string]dns.ZoneSummary{
70+
zoneIdQux: testPrivateZoneSummaryQux,
71+
})
72+
assert.Len(t, testCase.z.Get(), resetZoneLength)
73+
})
74+
}
75+
}

provider/oci/oci.go

+14-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"os"
2222
"strings"
23+
"time"
2324

2425
"github.com/oracle/oci-go-sdk/v65/common"
2526
"github.com/oracle/oci-go-sdk/v65/common/auth"
@@ -49,8 +50,9 @@ type OCIAuthConfig struct {
4950

5051
// OCIConfig holds the configuration for the OCI Provider.
5152
type OCIConfig struct {
52-
Auth OCIAuthConfig `yaml:"auth"`
53-
CompartmentID string `yaml:"compartment"`
53+
Auth OCIAuthConfig `yaml:"auth"`
54+
CompartmentID string `yaml:"compartment"`
55+
ZoneCacheDuration time.Duration
5456
}
5557

5658
// OCIProvider is an implementation of Provider for Oracle Cloud Infrastructure
@@ -63,6 +65,7 @@ type OCIProvider struct {
6365
domainFilter endpoint.DomainFilter
6466
zoneIDFilter provider.ZoneIDFilter
6567
zoneScope string
68+
zoneCache *zoneCache
6669
dryRun bool
6770
}
6871

@@ -135,11 +138,18 @@ func NewOCIProvider(cfg OCIConfig, domainFilter endpoint.DomainFilter, zoneIDFil
135138
domainFilter: domainFilter,
136139
zoneIDFilter: zoneIDFilter,
137140
zoneScope: zoneScope,
138-
dryRun: dryRun,
141+
zoneCache: &zoneCache{
142+
duration: cfg.ZoneCacheDuration,
143+
},
144+
dryRun: dryRun,
139145
}, nil
140146
}
141147

142148
func (p *OCIProvider) zones(ctx context.Context) (map[string]dns.ZoneSummary, error) {
149+
if !p.zoneCache.Expired() {
150+
log.Debug("Using cached zones list")
151+
return p.zoneCache.zones, nil
152+
}
143153
zones := make(map[string]dns.ZoneSummary)
144154
scopes := []dns.GetZoneScopeEnum{dns.GetZoneScopeEnum(p.zoneScope)}
145155
// If zone scope is empty, list all zones types.
@@ -155,6 +165,7 @@ func (p *OCIProvider) zones(ctx context.Context) (map[string]dns.ZoneSummary, er
155165
if len(zones) == 0 {
156166
log.Warnf("No zones in compartment %q match domain filters %v", p.cfg.CompartmentID, p.domainFilter)
157167
}
168+
p.zoneCache.Reset(zones)
158169
return zones, nil
159170
}
160171

provider/oci/oci_test.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"sort"
2222
"strings"
2323
"testing"
24+
"time"
2425

2526
"github.com/oracle/oci-go-sdk/v65/common"
2627
"github.com/oracle/oci-go-sdk/v65/dns"
@@ -137,7 +138,10 @@ func newOCIProvider(client ociDNSClient, domainFilter endpoint.DomainFilter, zon
137138
domainFilter: domainFilter,
138139
zoneIDFilter: zoneIDFilter,
139140
zoneScope: zoneScope,
140-
dryRun: dryRun,
141+
zoneCache: &zoneCache{
142+
duration: 0 * time.Second,
143+
},
144+
dryRun: dryRun,
141145
}
142146
}
143147

0 commit comments

Comments
 (0)