Unverified Commit ba5afe95 authored by Hugome's avatar Hugome
Browse files

Add OVH API rate limiting option

parent ccab168a
master Raffo-patch-1 changelog-for-v0.7.3 correctly-update-aws-records-when-type-changes dansimone/support-prefer-ingress-annotations dependabot/go_modules/github.com/Azure/azure-sdk-for-go-61.4.0incompatible dependabot/go_modules/github.com/aliyun/alibaba-cloud-sdk-go-1.61.1473 dependabot/go_modules/github.com/exoscale/egoscale-1.19.0 dependabot/go_modules/github.com/projectcontour/contour-1.20.0 dependabot/go_modules/k8s.io/apimachinery-0.23.3 fix-1820 gh-pages infoblox-multiple-A-records-fix normalize raffo-fix-2348 raffo/add-dependabot raffo/add-trivy-scanning raffo/arm raffo/arm32v7 raffo/bump-ci-timeout raffo/bump-cloudbuild-timeout raffo/bump-deps-sec raffo/bump-kustomize raffo/bump-kustomize-1 raffo/bump-kustomize-version-0.7.5 raffo/bump-modules raffo/codeQL raffo/drop-the-changelog raffo/e2e-aws raffo/fix-1820 raffo/fix-1936 raffo/fix-build raffo/fix-dependabot raffo/fix-ns-deletion raffo/fix-scaleway-security raffo/fix-that-typo raffo/fix-trivy raffo/fix-trivy-again raffo/fix-vulnerabilities raffo/goarm raffo/knolog raffo/kustomize-endpoints raffo/multiarch raffo/multiarch-docs raffo/new-ingress-resource raffo/release-conventions raffo/release-note-patch raffo/release-script raffo/release-script-update raffo/remove-azure-test raffo/remove-broken-link raffo/remove-masters raffo/revert-tzdata raffo/update-kustomize-080 raffo/update-v0.10-role raffo/use-actions raffo/v0.7.6 test-things validate-txt-prefix v1.0.0-mf v0.10.2 v0.10.1 v0.10.0 v0.9.0 v0.8.0 v0.7.6 v0.7.5 v0.7.4 v0.7.3 external-dns-helm-chart-1.7.1 external-dns-helm-chart-1.7.0 external-dns-helm-chart-1.6.0 external-dns-helm-chart-1.5.0 external-dns-helm-chart-1.4.1 external-dns-helm-chart-1.4.0 external-dns-helm-chart-1.3.2 external-dns-helm-chart-1.3.1 external-dns-helm-chart-1.3.0 external-dns-helm-chart-1.2.0
No related merge requests found
Showing with 38 additions and 12 deletions
+38 -12
......@@ -51,6 +51,7 @@ require (
github.com/vinyldns/go-vinyldns v0.0.0-20190611170422-7119fe55ed92
github.com/vultr/govultr v0.3.2
go.etcd.io/etcd v0.5.0-alpha.5.0.20200401174654-e694b7bb0875
go.uber.org/ratelimit v0.1.0
golang.org/x/net v0.0.0-20200202094626-16171245cfb2
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
google.golang.org/api v0.15.0
......
......@@ -200,7 +200,7 @@ func main() {
case "digitalocean":
p, err = digitalocean.NewDigitalOceanProvider(ctx, domainFilter, cfg.DryRun, cfg.DigitalOceanAPIPageSize)
case "ovh":
p, err = ovh.NewOVHProvider(ctx, domainFilter, cfg.OVHEndpoint, cfg.DryRun)
p, err = ovh.NewOVHProvider(ctx, domainFilter, cfg.OVHEndpoint, cfg.OVHApiRateLimit, cfg.DryRun)
case "linode":
p, err = linode.NewLinodeProvider(domainFilter, cfg.DryRun, externaldns.Version)
case "dnsimple":
......
......@@ -99,6 +99,7 @@ type Config struct {
OCIConfigFile string
InMemoryZones []string
OVHEndpoint string
OVHApiRateLimit int
PDNSServer string
PDNSAPIKey string `secure:"yes"`
PDNSTLSEnabled bool
......@@ -197,6 +198,7 @@ var defaultConfig = &Config{
OCIConfigFile: "/etc/kubernetes/oci.yaml",
InMemoryZones: []string{},
OVHEndpoint: "ovh-eu",
OVHApiRateLimit: 20,
PDNSServer: "http://localhost:8081",
PDNSAPIKey: "",
PDNSTLSEnabled: false,
......@@ -360,6 +362,7 @@ func (cfg *Config) ParseFlags(args []string) error {
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)
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)
app.Flag("ovh-endpoint", "When using the OVH provider, specify the endpoint (default: ovh-eu)").Default(defaultConfig.OVHEndpoint).StringVar(&cfg.OVHEndpoint)
app.Flag("ovh-api-rate-limit", "When using the OVH provider, specify the API request rate limit, X operations by seconds (default: 20)").Default(strconv.Itoa(defaultConfig.OVHApiRateLimit)).IntVar(&cfg.OVHApiRateLimit)
app.Flag("pdns-server", "When using the PowerDNS/PDNS provider, specify the URL to the pdns server (required when --provider=pdns)").Default(defaultConfig.PDNSServer).StringVar(&cfg.PDNSServer)
app.Flag("pdns-api-key", "When using the PowerDNS/PDNS provider, specify the API key to use to authorize requests (required when --provider=pdns)").Default(defaultConfig.PDNSAPIKey).StringVar(&cfg.PDNSAPIKey)
app.Flag("pdns-tls-enabled", "When using the PowerDNS/PDNS provider, specify whether to use TLS (default: false, requires --tls-ca, optionally specify --tls-client-cert and --tls-client-cert-key)").Default(strconv.FormatBool(defaultConfig.PDNSTLSEnabled)).BoolVar(&cfg.PDNSTLSEnabled)
......
......@@ -75,6 +75,7 @@ var (
OCIConfigFile: "/etc/kubernetes/oci.yaml",
InMemoryZones: []string{""},
OVHEndpoint: "ovh-eu",
OVHApiRateLimit: 20,
PDNSServer: "http://localhost:8081",
PDNSAPIKey: "",
Policy: "sync",
......@@ -149,6 +150,7 @@ var (
OCIConfigFile: "oci.yaml",
InMemoryZones: []string{"example.org", "company.com"},
OVHEndpoint: "ovh-ca",
OVHApiRateLimit: 42,
PDNSServer: "http://ns.example.com:8081",
PDNSAPIKey: "some-secret-key",
PDNSTLSEnabled: true,
......@@ -237,6 +239,7 @@ func TestParseFlags(t *testing.T) {
"--inmemory-zone=example.org",
"--inmemory-zone=company.com",
"--ovh-endpoint=ovh-ca",
"--ovh-api-rate-limit=42",
"--pdns-server=http://ns.example.com:8081",
"--pdns-api-key=some-secret-key",
"--pdns-tls-enabled",
......@@ -326,6 +329,7 @@ func TestParseFlags(t *testing.T) {
"EXTERNAL_DNS_OCI_CONFIG_FILE": "oci.yaml",
"EXTERNAL_DNS_INMEMORY_ZONE": "example.org\ncompany.com",
"EXTERNAL_DNS_OVH_ENDPOINT": "ovh-ca",
"EXTERNAL_DNS_OVH_API_RATE_LIMIT": "42",
"EXTERNAL_DNS_DOMAIN_FILTER": "example.org\ncompany.com",
"EXTERNAL_DNS_EXCLUDE_DOMAINS": "xapi.example.org\nxapi.company.com",
"EXTERNAL_DNS_PDNS_SERVER": "http://ns.example.com:8081",
......
......@@ -29,6 +29,8 @@ import (
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
"sigs.k8s.io/external-dns/provider"
"go.uber.org/ratelimit"
)
const (
......@@ -50,6 +52,8 @@ type OVHProvider struct {
client ovhClient
apiRateLimiter ratelimit.Limiter
domainFilter endpoint.DomainFilter
DryRun bool
}
......@@ -79,7 +83,7 @@ type ovhChange struct {
}
// NewOVHProvider initializes a new OVH DNS based Provider.
func NewOVHProvider(ctx context.Context, domainFilter endpoint.DomainFilter, endpoint string, dryRun bool) (*OVHProvider, error) {
func NewOVHProvider(ctx context.Context, domainFilter endpoint.DomainFilter, endpoint string, apiRateLimit int, dryRun bool) (*OVHProvider, error) {
client, err := ovh.NewEndpointClient(endpoint)
if err != nil {
return nil, err
......@@ -89,9 +93,10 @@ func NewOVHProvider(ctx context.Context, domainFilter endpoint.DomainFilter, end
return nil, ErrNoDryRun
}
return &OVHProvider{
client: client,
domainFilter: domainFilter,
DryRun: dryRun,
client: client,
domainFilter: domainFilter,
apiRateLimiter: ratelimit.New(apiRateLimit),
DryRun: dryRun,
}, nil
}
......@@ -149,10 +154,14 @@ func (p *OVHProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) e
func (p *OVHProvider) refresh(zone string) error {
log.Debugf("OVH: Refresh %s zone", zone)
p.apiRateLimiter.Take()
return p.client.Post(fmt.Sprintf("/domain/zone/%s/refresh", zone), nil, nil)
}
func (p *OVHProvider) change(change ovhChange) error {
p.apiRateLimiter.Take()
switch change.Action {
case ovhCreate:
log.Debugf("OVH: Add an entry to %s", change.String())
......@@ -194,6 +203,7 @@ func (p *OVHProvider) zones() ([]string, error) {
zones := []string{}
filteredZones := []string{}
p.apiRateLimiter.Take()
if err := p.client.Get("/domain/zone", &zones); err != nil {
return nil, err
}
......@@ -213,6 +223,8 @@ func (p *OVHProvider) records(ctx *context.Context, zone *string, records chan<-
eg, _ := errgroup.WithContext(*ctx)
log.Debugf("OVH: Getting records for %s", *zone)
p.apiRateLimiter.Take()
if err := p.client.Get(fmt.Sprintf("/domain/zone/%s/record", *zone), &recordsIds); err != nil {
return err
}
......@@ -236,6 +248,8 @@ func (p *OVHProvider) record(zone *string, id uint64, records chan<- ovhRecord)
record := ovhRecord{}
log.Debugf("OVH: Getting record %d for %s", id, *zone)
p.apiRateLimiter.Take()
if err := p.client.Get(fmt.Sprintf("/domain/zone/%s/record/%d", *zone, id), &record); err != nil {
return err
}
......
......@@ -25,6 +25,7 @@ import (
"github.com/ovh/go-ovh/ovh"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"go.uber.org/ratelimit"
"sigs.k8s.io/external-dns/endpoint"
"sigs.k8s.io/external-dns/plan"
)
......@@ -58,8 +59,9 @@ func TestOvhZones(t *testing.T) {
assert := assert.New(t)
client := new(mockOvhClient)
provider := &OVHProvider{
client: client,
domainFilter: endpoint.NewDomainFilter([]string{"com"}),
client: client,
apiRateLimiter: ratelimit.New(10),
domainFilter: endpoint.NewDomainFilter([]string{"com"}),
}
// Basic zones
......@@ -81,7 +83,7 @@ func TestOvhZones(t *testing.T) {
func TestOvhZoneRecords(t *testing.T) {
assert := assert.New(t)
client := new(mockOvhClient)
provider := &OVHProvider{client: client}
provider := &OVHProvider{client: client, apiRateLimiter: ratelimit.New(10)}
// Basic zones records
client.On("Get", "/domain/zone").Return([]string{"example.org"}, nil).Once()
......@@ -125,7 +127,7 @@ func TestOvhZoneRecords(t *testing.T) {
func TestOvhRecords(t *testing.T) {
assert := assert.New(t)
client := new(mockOvhClient)
provider := &OVHProvider{client: client}
provider := &OVHProvider{client: client, apiRateLimiter: ratelimit.New(10)}
// Basic zones records
client.On("Get", "/domain/zone").Return([]string{"example.org", "example.net"}, nil).Once()
......@@ -158,7 +160,7 @@ func TestOvhRecords(t *testing.T) {
func TestOvhRefresh(t *testing.T) {
client := new(mockOvhClient)
provider := &OVHProvider{client: client}
provider := &OVHProvider{client: client, apiRateLimiter: ratelimit.New(10)}
// Basic zone refresh
client.On("Post", "/domain/zone/example.net/refresh", nil).Return(nil, nil).Once()
......@@ -199,7 +201,7 @@ func TestOvhNewChange(t *testing.T) {
func TestOvhApplyChanges(t *testing.T) {
assert := assert.New(t)
client := new(mockOvhClient)
provider := &OVHProvider{client: client}
provider := &OVHProvider{client: client, apiRateLimiter: ratelimit.New(10)}
changes := plan.Changes{
Create: []*endpoint.Endpoint{
{DNSName: ".example.net", RecordType: "A", RecordTTL: 10, Targets: []string{"203.0.113.42"}},
......@@ -252,7 +254,7 @@ func TestOvhApplyChanges(t *testing.T) {
func TestOvhChange(t *testing.T) {
assert := assert.New(t)
client := new(mockOvhClient)
provider := &OVHProvider{client: client}
provider := &OVHProvider{client: client, apiRateLimiter: ratelimit.New(10)}
// Record creation
client.On("Post", "/domain/zone/example.net/record", ovhRecordFields{SubDomain: "ovh"}).Return(nil, nil).Once()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment