diff --git a/.github/workflows/end-to-end-tests.yml b/.github/workflows/end-to-end-tests.yml index 77e5703084..500a335705 100644 --- a/.github/workflows/end-to-end-tests.yml +++ b/.github/workflows/end-to-end-tests.yml @@ -14,6 +14,10 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v6 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod - name: e2e run: | ./scripts/e2e-test.sh diff --git a/.golangci.yml b/.golangci.yml index 76de2af755..fee59f9e08 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -45,7 +45,7 @@ linters: - name: confusing-naming disabled: true cyclop: # Lower cyclomatic complexity threshold after the max complexity is lowered - max-complexity: 37 # Controller/execute.go:147:1: calculated cyclomatic complexity for function buildProvider is 37 + max-complexity: 32 # provider/rfc2136/rfc2136.go:350:1: calculated cyclomatic complexity for function ApplyChanges is 32. See https://github.com/kubernetes-sigs/external-dns/issues/5419 goconst: min-occurrences: 3 # Ignore well-known DNS record types, boolean strings, and common values diff --git a/controller/execute.go b/controller/execute.go index eeb9673c29..b4387c84ba 100644 --- a/controller/execute.go +++ b/controller/execute.go @@ -25,8 +25,6 @@ import ( "syscall" "time" - "github.com/aws/aws-sdk-go-v2/service/route53" - sd "github.com/aws/aws-sdk-go-v2/service/servicediscovery" "github.com/go-logr/logr" "github.com/prometheus/client_golang/prometheus/promhttp" log "github.com/sirupsen/logrus" @@ -39,32 +37,6 @@ import ( "sigs.k8s.io/external-dns/pkg/metrics" "sigs.k8s.io/external-dns/plan" "sigs.k8s.io/external-dns/provider" - "sigs.k8s.io/external-dns/provider/akamai" - "sigs.k8s.io/external-dns/provider/alibabacloud" - "sigs.k8s.io/external-dns/provider/aws" - "sigs.k8s.io/external-dns/provider/awssd" - "sigs.k8s.io/external-dns/provider/azure" - "sigs.k8s.io/external-dns/provider/civo" - "sigs.k8s.io/external-dns/provider/cloudflare" - "sigs.k8s.io/external-dns/provider/coredns" - "sigs.k8s.io/external-dns/provider/digitalocean" - "sigs.k8s.io/external-dns/provider/dnsimple" - "sigs.k8s.io/external-dns/provider/exoscale" - "sigs.k8s.io/external-dns/provider/gandi" - "sigs.k8s.io/external-dns/provider/godaddy" - "sigs.k8s.io/external-dns/provider/google" - "sigs.k8s.io/external-dns/provider/inmemory" - "sigs.k8s.io/external-dns/provider/linode" - "sigs.k8s.io/external-dns/provider/ns1" - "sigs.k8s.io/external-dns/provider/oci" - "sigs.k8s.io/external-dns/provider/ovh" - "sigs.k8s.io/external-dns/provider/pdns" - "sigs.k8s.io/external-dns/provider/pihole" - "sigs.k8s.io/external-dns/provider/plural" - "sigs.k8s.io/external-dns/provider/rfc2136" - "sigs.k8s.io/external-dns/provider/scaleway" - "sigs.k8s.io/external-dns/provider/transip" - "sigs.k8s.io/external-dns/provider/webhook" webhookapi "sigs.k8s.io/external-dns/provider/webhook/api" "sigs.k8s.io/external-dns/registry" "sigs.k8s.io/external-dns/source" @@ -109,6 +81,7 @@ func Execute() { go handleSigterm(cancel) sCfg := source.NewSourceConfig(cfg) + // TODO: Move source construction to the source package (blocked by cyclic dependency with wrappers) endpointsSource, err := buildSource(ctx, sCfg) if err != nil { log.Fatal(err) // nolint: gocritic // exitAfterDefer @@ -121,6 +94,7 @@ func Execute() { endpoint.WithRegexDomainExclude(cfg.RegexDomainExclude), ) + // TODO: Move provider construction to the provider package prvdr, err := buildProvider(ctx, cfg, domainFilter) if err != nil { log.Fatal(err) @@ -161,202 +135,22 @@ func buildProvider( cfg *externaldns.Config, domainFilter *endpoint.DomainFilter, ) (provider.Provider, error) { - var p provider.Provider - var err error - - zoneNameFilter := endpoint.NewDomainFilter(cfg.ZoneNameFilter) - zoneIDFilter := provider.NewZoneIDFilter(cfg.ZoneIDFilter) - zoneTypeFilter := provider.NewZoneTypeFilter(cfg.AWSZoneType) - zoneTagFilter := provider.NewZoneTagFilter(cfg.AWSZoneTagFilter) - - // TODO: Controller focuses on orchestration, not provider construction - // TODO: refactor to move this to provider package, cover with tests - // TODO: example provider.SelectProvider(cfg, ...) - switch cfg.Provider { - case "akamai": - p, err = akamai.NewAkamaiProvider( - akamai.AkamaiConfig{ - DomainFilter: domainFilter, - ZoneIDFilter: zoneIDFilter, - ServiceConsumerDomain: cfg.AkamaiServiceConsumerDomain, - ClientToken: cfg.AkamaiClientToken, - ClientSecret: cfg.AkamaiClientSecret, - AccessToken: cfg.AkamaiAccessToken, - EdgercPath: cfg.AkamaiEdgercPath, - EdgercSection: cfg.AkamaiEdgercSection, - DryRun: cfg.DryRun, - }, nil) - case "alibabacloud": - p, err = alibabacloud.NewAlibabaCloudProvider(cfg.AlibabaCloudConfigFile, domainFilter, zoneIDFilter, cfg.AlibabaCloudZoneType, cfg.DryRun) - case "aws": - configs := aws.CreateV2Configs(cfg) - clients := make(map[string]aws.Route53API, len(configs)) - for profile, config := range configs { - clients[profile] = route53.NewFromConfig(config) - } - - p, err = aws.NewAWSProvider( - aws.AWSConfig{ - DomainFilter: domainFilter, - ZoneIDFilter: zoneIDFilter, - ZoneTypeFilter: zoneTypeFilter, - ZoneTagFilter: zoneTagFilter, - ZoneMatchParent: cfg.AWSZoneMatchParent, - BatchChangeSize: cfg.AWSBatchChangeSize, - BatchChangeSizeBytes: cfg.AWSBatchChangeSizeBytes, - BatchChangeSizeValues: cfg.AWSBatchChangeSizeValues, - BatchChangeInterval: cfg.AWSBatchChangeInterval, - EvaluateTargetHealth: cfg.AWSEvaluateTargetHealth, - PreferCNAME: cfg.AWSPreferCNAME, - DryRun: cfg.DryRun, - ZoneCacheDuration: cfg.AWSZoneCacheDuration, - }, - clients, - ) - case "aws-sd": - // Check that only compatible Registry is used with AWS-SD - if cfg.Registry != "noop" && cfg.Registry != "aws-sd" { - log.Infof("Registry \"%s\" cannot be used with AWS Cloud Map. Switching to \"aws-sd\".", cfg.Registry) - cfg.Registry = "aws-sd" - } - p, err = awssd.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.DryRun, cfg.AWSSDServiceCleanup, cfg.TXTOwnerID, cfg.AWSSDCreateTag, sd.NewFromConfig(aws.CreateDefaultV2Config(cfg))) - case "azure-dns", "azure": - p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.AzureMaxRetriesCount, cfg.DryRun) - case "azure-private-dns": - p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.AzureMaxRetriesCount, cfg.DryRun) - case "civo": - p, err = civo.NewCivoProvider(domainFilter, cfg.DryRun) - case "cloudflare": - p, err = cloudflare.NewCloudFlareProvider( - domainFilter, - zoneIDFilter, - cfg.CloudflareProxied, - cfg.DryRun, - cloudflare.RegionalServicesConfig{ - Enabled: cfg.CloudflareRegionalServices, - RegionKey: cfg.CloudflareRegionKey, - }, - cloudflare.CustomHostnamesConfig{ - Enabled: cfg.CloudflareCustomHostnames, - MinTLSVersion: cfg.CloudflareCustomHostnamesMinTLSVersion, - CertificateAuthority: cfg.CloudflareCustomHostnamesCertificateAuthority, - }, - cloudflare.DNSRecordsConfig{ - PerPage: cfg.CloudflareDNSRecordsPerPage, - Comment: cfg.CloudflareDNSRecordsComment, - }) - case "google": - p, err = google.NewGoogleProvider(ctx, cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.GoogleBatchChangeSize, cfg.GoogleBatchChangeInterval, cfg.GoogleZoneVisibility, cfg.DryRun) - case "digitalocean": - p, err = digitalocean.NewDigitalOceanProvider(ctx, domainFilter, cfg.DryRun, cfg.DigitalOceanAPIPageSize) - case "ovh": - p, err = ovh.NewOVHProvider(ctx, domainFilter, cfg.OVHEndpoint, cfg.OVHApiRateLimit, cfg.OVHEnableCNAMERelative, cfg.DryRun) - case "linode": - p, err = linode.NewLinodeProvider(domainFilter, cfg.DryRun) - case "dnsimple": - p, err = dnsimple.NewDnsimpleProvider(domainFilter, zoneIDFilter, cfg.DryRun) - case "coredns", "skydns": - p, err = coredns.NewCoreDNSProvider(domainFilter, cfg.CoreDNSPrefix, cfg.TXTOwnerID, cfg.CoreDNSStrictlyOwned, cfg.DryRun) - case "exoscale": - p, err = exoscale.NewExoscaleProvider( - cfg.ExoscaleAPIEnvironment, - cfg.ExoscaleAPIZone, - cfg.ExoscaleAPIKey, - cfg.ExoscaleAPISecret, - cfg.DryRun, - exoscale.ExoscaleWithDomain(domainFilter), - exoscale.ExoscaleWithLogging(), - ) - case "inmemory": - p, err = inmemory.NewInMemoryProvider(inmemory.InMemoryInitZones(cfg.InMemoryZones), inmemory.InMemoryWithDomain(domainFilter), inmemory.InMemoryWithLogging()), nil - case "pdns": - p, err = pdns.NewPDNSProvider( - ctx, - pdns.PDNSConfig{ - DomainFilter: domainFilter, - DryRun: cfg.DryRun, - Server: cfg.PDNSServer, - ServerID: cfg.PDNSServerID, - APIKey: cfg.PDNSAPIKey, - TLSConfig: pdns.TLSConfig{ - SkipTLSVerify: cfg.PDNSSkipTLSVerify, - CAFilePath: cfg.TLSCA, - ClientCertFilePath: cfg.TLSClientCert, - ClientCertKeyFilePath: cfg.TLSClientCertKey, - }, - }, - ) - case "oci": - var config *oci.OCIConfig - // if the instance-principals flag was set, and a compartment OCID was provided, then ignore the - // OCI config file, and provide a config that uses instance principal authentication. - if cfg.OCIAuthInstancePrincipal { - if len(cfg.OCICompartmentOCID) == 0 { - err = fmt.Errorf("instance principal authentication requested, but no compartment OCID provided") - break - } - authConfig := oci.OCIAuthConfig{UseInstancePrincipal: true} - config = &oci.OCIConfig{Auth: authConfig, CompartmentID: cfg.OCICompartmentOCID} - } else { - if config, err = oci.LoadOCIConfig(cfg.OCIConfigFile); err != nil { - break - } - } - config.ZoneCacheDuration = cfg.OCIZoneCacheDuration - p, err = oci.NewOCIProvider(*config, domainFilter, zoneIDFilter, cfg.OCIZoneScope, cfg.DryRun) - case "rfc2136": - tlsConfig := rfc2136.TLSConfig{ - UseTLS: cfg.RFC2136UseTLS, - SkipTLSVerify: cfg.RFC2136SkipTLSVerify, - CAFilePath: cfg.TLSCA, - ClientCertFilePath: cfg.TLSClientCert, - ClientCertKeyFilePath: cfg.TLSClientCertKey, - } - p, err = rfc2136.NewRfc2136Provider(cfg.RFC2136Host, cfg.RFC2136Port, cfg.RFC2136Zone, cfg.RFC2136Insecure, cfg.RFC2136TSIGKeyName, cfg.RFC2136TSIGSecret, cfg.RFC2136TSIGSecretAlg, cfg.RFC2136TAXFR, domainFilter, cfg.DryRun, cfg.RFC2136MinTTL, cfg.RFC2136CreatePTR, cfg.RFC2136GSSTSIG, cfg.RFC2136KerberosUsername, cfg.RFC2136KerberosPassword, cfg.RFC2136KerberosRealm, cfg.RFC2136BatchChangeSize, tlsConfig, cfg.RFC2136LoadBalancingStrategy, nil) - case "ns1": - p, err = ns1.NewNS1Provider( - ns1.NS1Config{ - DomainFilter: domainFilter, - ZoneIDFilter: zoneIDFilter, - NS1Endpoint: cfg.NS1Endpoint, - NS1IgnoreSSL: cfg.NS1IgnoreSSL, - DryRun: cfg.DryRun, - MinTTLSeconds: cfg.NS1MinTTLSeconds, - }, - ) - case "transip": - p, err = transip.NewTransIPProvider(cfg.TransIPAccountName, cfg.TransIPPrivateKeyFile, domainFilter, cfg.DryRun) - case "scaleway": - p, err = scaleway.NewScalewayProvider(ctx, domainFilter, cfg.DryRun) - case "godaddy": - p, err = godaddy.NewGoDaddyProvider(ctx, domainFilter, cfg.GoDaddyTTL, cfg.GoDaddyAPIKey, cfg.GoDaddySecretKey, cfg.GoDaddyOTE, cfg.DryRun) - case "gandi": - p, err = gandi.NewGandiProvider(ctx, domainFilter, cfg.DryRun) - case "pihole": - p, err = pihole.NewPiholeProvider( - pihole.PiholeConfig{ - Server: cfg.PiholeServer, - Password: cfg.PiholePassword, - TLSInsecureSkipVerify: cfg.PiholeTLSInsecureSkipVerify, - DomainFilter: domainFilter, - DryRun: cfg.DryRun, - APIVersion: cfg.PiholeApiVersion, - }, - ) - case "plural": - p, err = plural.NewPluralProvider(cfg.PluralCluster, cfg.PluralProvider) - case "webhook": - p, err = webhook.NewWebhookProvider(cfg.WebhookProviderURL) - default: - err = fmt.Errorf("unknown dns provider: %s", cfg.Provider) + factory, ok := providerFactories[cfg.Provider] + if !ok { + return nil, fmt.Errorf("unknown dns provider: %s", cfg.Provider) } + p, err := factory(ctx, cfg, domainFilter) + if err != nil { + return nil, err + } + if p != nil && cfg.ProviderCacheTime > 0 { p = provider.NewCachedProvider( p, cfg.ProviderCacheTime, ) } - return p, err + return p, nil } func buildController( diff --git a/controller/execute_test.go b/controller/execute_test.go index d89bb61228..815580a0de 100644 --- a/controller/execute_test.go +++ b/controller/execute_test.go @@ -230,6 +230,30 @@ func TestBuildProvider(t *testing.T) { } } +func TestBuildProvider_Coverage(t *testing.T) { + // this test ensures that all providers are registered in the factory map + // and that the buildProvider function does not return an "unknown provider" error + knownProviders := []string{ + "akamai", "alibabacloud", "aws", "aws-sd", "azure", "azure-dns", "azure-private-dns", + "civo", "cloudflare", "coredns", "digitalocean", "dnsimple", "exoscale", "gandi", + "godaddy", "google", "inmemory", "linode", "ns1", "oci", "ovh", "pdns", "pihole", + "plural", "rfc2136", "scaleway", "skydns", "transip", "webhook", + } + + for _, providerName := range knownProviders { + t.Run(providerName, func(t *testing.T) { + cfg := &externaldns.Config{ + Provider: providerName, + } + // We don't care about the specific error (missing config), just that it's NOT "unknown provider" + _, err := buildProvider(t.Context(), cfg, nil) + if err != nil { + assert.NotContains(t, err.Error(), "unknown dns provider") + } + }) + } +} + func TestBuildSourceWithWrappers(t *testing.T) { svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotImplemented) @@ -299,7 +323,7 @@ func TestHelperProcess(t *testing.T) { func runExecuteSubprocess(t *testing.T, args []string) (int, string, error) { t.Helper() // make sure the subprocess does not run forever - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(t.Context(), 10*time.Second) defer cancel() cmdArgs := append([]string{"-test.run=TestHelperProcess", "--"}, args...) @@ -446,7 +470,7 @@ func TestControllerRunCancelContextStopsLoop(t *testing.T) { Registry: "txt", TXTOwnerID: "test-owner", } - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithCancel(t.Context()) defer cancel() src, err := buildSource(ctx, source.NewSourceConfig(cfg)) require.NoError(t, err) diff --git a/controller/provider_factories.go b/controller/provider_factories.go new file mode 100644 index 0000000000..97241f2ac8 --- /dev/null +++ b/controller/provider_factories.go @@ -0,0 +1,268 @@ +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "fmt" + + "github.com/aws/aws-sdk-go-v2/service/route53" + sd "github.com/aws/aws-sdk-go-v2/service/servicediscovery" + log "github.com/sirupsen/logrus" + + "sigs.k8s.io/external-dns/endpoint" + "sigs.k8s.io/external-dns/pkg/apis/externaldns" + "sigs.k8s.io/external-dns/provider" + "sigs.k8s.io/external-dns/provider/akamai" + "sigs.k8s.io/external-dns/provider/alibabacloud" + "sigs.k8s.io/external-dns/provider/aws" + "sigs.k8s.io/external-dns/provider/awssd" + "sigs.k8s.io/external-dns/provider/azure" + "sigs.k8s.io/external-dns/provider/civo" + "sigs.k8s.io/external-dns/provider/cloudflare" + "sigs.k8s.io/external-dns/provider/coredns" + "sigs.k8s.io/external-dns/provider/digitalocean" + "sigs.k8s.io/external-dns/provider/dnsimple" + "sigs.k8s.io/external-dns/provider/exoscale" + "sigs.k8s.io/external-dns/provider/gandi" + "sigs.k8s.io/external-dns/provider/godaddy" + "sigs.k8s.io/external-dns/provider/google" + "sigs.k8s.io/external-dns/provider/inmemory" + "sigs.k8s.io/external-dns/provider/linode" + "sigs.k8s.io/external-dns/provider/ns1" + "sigs.k8s.io/external-dns/provider/oci" + "sigs.k8s.io/external-dns/provider/ovh" + "sigs.k8s.io/external-dns/provider/pdns" + "sigs.k8s.io/external-dns/provider/pihole" + "sigs.k8s.io/external-dns/provider/plural" + "sigs.k8s.io/external-dns/provider/rfc2136" + "sigs.k8s.io/external-dns/provider/scaleway" + "sigs.k8s.io/external-dns/provider/transip" + "sigs.k8s.io/external-dns/provider/webhook" +) + +type providerFactory func(context.Context, *externaldns.Config, *endpoint.DomainFilter) (provider.Provider, error) + +var providerFactories = map[string]providerFactory{ + "akamai": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return akamai.NewAkamaiProvider( + akamai.AkamaiConfig{ + DomainFilter: domainFilter, + ZoneIDFilter: provider.NewZoneIDFilter(cfg.ZoneIDFilter), + ServiceConsumerDomain: cfg.AkamaiServiceConsumerDomain, + ClientToken: cfg.AkamaiClientToken, + ClientSecret: cfg.AkamaiClientSecret, + AccessToken: cfg.AkamaiAccessToken, + EdgercPath: cfg.AkamaiEdgercPath, + EdgercSection: cfg.AkamaiEdgercSection, + DryRun: cfg.DryRun, + }, nil) + }, + "alibabacloud": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return alibabacloud.NewAlibabaCloudProvider(cfg.AlibabaCloudConfigFile, domainFilter, provider.NewZoneIDFilter(cfg.ZoneIDFilter), cfg.AlibabaCloudZoneType, cfg.DryRun) + }, + "aws": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + configs := aws.CreateV2Configs(cfg) + clients := make(map[string]aws.Route53API, len(configs)) + for profile, config := range configs { + clients[profile] = route53.NewFromConfig(config) + } + + return aws.NewAWSProvider( + aws.AWSConfig{ + DomainFilter: domainFilter, + ZoneIDFilter: provider.NewZoneIDFilter(cfg.ZoneIDFilter), + ZoneTypeFilter: provider.NewZoneTypeFilter(cfg.AWSZoneType), + ZoneTagFilter: provider.NewZoneTagFilter(cfg.AWSZoneTagFilter), + ZoneMatchParent: cfg.AWSZoneMatchParent, + BatchChangeSize: cfg.AWSBatchChangeSize, + BatchChangeSizeBytes: cfg.AWSBatchChangeSizeBytes, + BatchChangeSizeValues: cfg.AWSBatchChangeSizeValues, + BatchChangeInterval: cfg.AWSBatchChangeInterval, + EvaluateTargetHealth: cfg.AWSEvaluateTargetHealth, + PreferCNAME: cfg.AWSPreferCNAME, + DryRun: cfg.DryRun, + ZoneCacheDuration: cfg.AWSZoneCacheDuration, + }, + clients, + ) + }, + "aws-sd": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + // Check that only compatible Registry is used with AWS-SD + if cfg.Registry != "noop" && cfg.Registry != "aws-sd" { + log.Infof("Registry \"%s\" cannot be used with AWS Cloud Map. Switching to \"aws-sd\".", cfg.Registry) + cfg.Registry = "aws-sd" + } + return awssd.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.DryRun, cfg.AWSSDServiceCleanup, cfg.TXTOwnerID, cfg.AWSSDCreateTag, sd.NewFromConfig(aws.CreateDefaultV2Config(cfg))) + }, + "azure-dns": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, endpoint.NewDomainFilter(cfg.ZoneNameFilter), provider.NewZoneIDFilter(cfg.ZoneIDFilter), cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.AzureMaxRetriesCount, cfg.DryRun) + }, + "azure": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, endpoint.NewDomainFilter(cfg.ZoneNameFilter), provider.NewZoneIDFilter(cfg.ZoneIDFilter), cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.AzureMaxRetriesCount, cfg.DryRun) + }, + "azure-private-dns": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, endpoint.NewDomainFilter(cfg.ZoneNameFilter), provider.NewZoneIDFilter(cfg.ZoneIDFilter), cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.AzureMaxRetriesCount, cfg.DryRun) + }, + "civo": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return civo.NewCivoProvider(domainFilter, cfg.DryRun) + }, + "cloudflare": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return cloudflare.NewCloudFlareProvider( + domainFilter, + provider.NewZoneIDFilter(cfg.ZoneIDFilter), + cfg.CloudflareProxied, + cfg.DryRun, + cloudflare.RegionalServicesConfig{ + Enabled: cfg.CloudflareRegionalServices, + RegionKey: cfg.CloudflareRegionKey, + }, + cloudflare.CustomHostnamesConfig{ + Enabled: cfg.CloudflareCustomHostnames, + MinTLSVersion: cfg.CloudflareCustomHostnamesMinTLSVersion, + CertificateAuthority: cfg.CloudflareCustomHostnamesCertificateAuthority, + }, + cloudflare.DNSRecordsConfig{ + PerPage: cfg.CloudflareDNSRecordsPerPage, + Comment: cfg.CloudflareDNSRecordsComment, + }) + }, + "google": func(ctx context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return google.NewGoogleProvider(ctx, cfg.GoogleProject, domainFilter, provider.NewZoneIDFilter(cfg.ZoneIDFilter), cfg.GoogleBatchChangeSize, cfg.GoogleBatchChangeInterval, cfg.GoogleZoneVisibility, cfg.DryRun) + }, + "digitalocean": func(ctx context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return digitalocean.NewDigitalOceanProvider(ctx, domainFilter, cfg.DryRun, cfg.DigitalOceanAPIPageSize) + }, + "ovh": func(ctx context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return ovh.NewOVHProvider(ctx, domainFilter, cfg.OVHEndpoint, cfg.OVHApiRateLimit, cfg.OVHEnableCNAMERelative, cfg.DryRun) + }, + "linode": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return linode.NewLinodeProvider(domainFilter, cfg.DryRun) + }, + "dnsimple": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return dnsimple.NewDnsimpleProvider(domainFilter, provider.NewZoneIDFilter(cfg.ZoneIDFilter), cfg.DryRun) + }, + "coredns": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return coredns.NewCoreDNSProvider(domainFilter, cfg.CoreDNSPrefix, cfg.TXTOwnerID, cfg.CoreDNSStrictlyOwned, cfg.DryRun) + }, + "skydns": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return coredns.NewCoreDNSProvider(domainFilter, cfg.CoreDNSPrefix, cfg.TXTOwnerID, cfg.CoreDNSStrictlyOwned, cfg.DryRun) + }, + "exoscale": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return exoscale.NewExoscaleProvider( + cfg.ExoscaleAPIEnvironment, + cfg.ExoscaleAPIZone, + cfg.ExoscaleAPIKey, + cfg.ExoscaleAPISecret, + cfg.DryRun, + exoscale.ExoscaleWithDomain(domainFilter), + exoscale.ExoscaleWithLogging(), + ) + }, + "inmemory": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return inmemory.NewInMemoryProvider(inmemory.InMemoryInitZones(cfg.InMemoryZones), inmemory.InMemoryWithDomain(domainFilter), inmemory.InMemoryWithLogging()), nil + }, + "pdns": func(ctx context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return pdns.NewPDNSProvider( + ctx, + pdns.PDNSConfig{ + DomainFilter: domainFilter, + DryRun: cfg.DryRun, + Server: cfg.PDNSServer, + ServerID: cfg.PDNSServerID, + APIKey: cfg.PDNSAPIKey, + TLSConfig: pdns.TLSConfig{ + SkipTLSVerify: cfg.PDNSSkipTLSVerify, + CAFilePath: cfg.TLSCA, + ClientCertFilePath: cfg.TLSClientCert, + ClientCertKeyFilePath: cfg.TLSClientCertKey, + }, + }, + ) + }, + "oci": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + var config *oci.OCIConfig + var err error + // if the instance-principals flag was set, and a compartment OCID was provided, then ignore the + // OCI config file, and provide a config that uses instance principal authentication. + if cfg.OCIAuthInstancePrincipal { + if len(cfg.OCICompartmentOCID) == 0 { + return nil, fmt.Errorf("instance principal authentication requested, but no compartment OCID provided") + } + authConfig := oci.OCIAuthConfig{UseInstancePrincipal: true} + config = &oci.OCIConfig{Auth: authConfig, CompartmentID: cfg.OCICompartmentOCID} + } else { + if config, err = oci.LoadOCIConfig(cfg.OCIConfigFile); err != nil { + return nil, err + } + } + config.ZoneCacheDuration = cfg.OCIZoneCacheDuration + return oci.NewOCIProvider(*config, domainFilter, provider.NewZoneIDFilter(cfg.ZoneIDFilter), cfg.OCIZoneScope, cfg.DryRun) + }, + "rfc2136": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + tlsConfig := rfc2136.TLSConfig{ + UseTLS: cfg.RFC2136UseTLS, + SkipTLSVerify: cfg.RFC2136SkipTLSVerify, + CAFilePath: cfg.TLSCA, + ClientCertFilePath: cfg.TLSClientCert, + ClientCertKeyFilePath: cfg.TLSClientCertKey, + } + return rfc2136.NewRfc2136Provider(cfg.RFC2136Host, cfg.RFC2136Port, cfg.RFC2136Zone, cfg.RFC2136Insecure, cfg.RFC2136TSIGKeyName, cfg.RFC2136TSIGSecret, cfg.RFC2136TSIGSecretAlg, cfg.RFC2136TAXFR, domainFilter, cfg.DryRun, cfg.RFC2136MinTTL, cfg.RFC2136CreatePTR, cfg.RFC2136GSSTSIG, cfg.RFC2136KerberosUsername, cfg.RFC2136KerberosPassword, cfg.RFC2136KerberosRealm, cfg.RFC2136BatchChangeSize, tlsConfig, cfg.RFC2136LoadBalancingStrategy, nil) + }, + "ns1": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return ns1.NewNS1Provider( + ns1.NS1Config{ + DomainFilter: domainFilter, + ZoneIDFilter: provider.NewZoneIDFilter(cfg.ZoneIDFilter), + NS1Endpoint: cfg.NS1Endpoint, + NS1IgnoreSSL: cfg.NS1IgnoreSSL, + DryRun: cfg.DryRun, + MinTTLSeconds: cfg.NS1MinTTLSeconds, + }, + ) + }, + "transip": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return transip.NewTransIPProvider(cfg.TransIPAccountName, cfg.TransIPPrivateKeyFile, domainFilter, cfg.DryRun) + }, + "scaleway": func(ctx context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return scaleway.NewScalewayProvider(ctx, domainFilter, cfg.DryRun) + }, + "godaddy": func(ctx context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return godaddy.NewGoDaddyProvider(ctx, domainFilter, cfg.GoDaddyTTL, cfg.GoDaddyAPIKey, cfg.GoDaddySecretKey, cfg.GoDaddyOTE, cfg.DryRun) + }, + "gandi": func(ctx context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return gandi.NewGandiProvider(ctx, domainFilter, cfg.DryRun) + }, + "pihole": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return pihole.NewPiholeProvider( + pihole.PiholeConfig{ + Server: cfg.PiholeServer, + Password: cfg.PiholePassword, + TLSInsecureSkipVerify: cfg.PiholeTLSInsecureSkipVerify, + DomainFilter: domainFilter, + DryRun: cfg.DryRun, + APIVersion: cfg.PiholeApiVersion, + }, + ) + }, + "plural": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return plural.NewPluralProvider(cfg.PluralCluster, cfg.PluralProvider) + }, + "webhook": func(_ context.Context, cfg *externaldns.Config, domainFilter *endpoint.DomainFilter) (provider.Provider, error) { + return webhook.NewWebhookProvider(cfg.WebhookProviderURL) + }, +} diff --git a/go.mod b/go.mod index e949170aa8..9bf9db573e 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,6 @@ require ( github.com/bodgit/tsig v1.2.2 github.com/cenkalti/backoff/v5 v5.0.3 github.com/civo/civogo v0.6.5 - github.com/cloudflare/cloudflare-go v0.116.0 github.com/cloudflare/cloudflare-go/v5 v5.1.0 github.com/datawire/ambassador v1.12.4 github.com/denverdino/aliyungo v0.0.0-20230411124812-ab98a9173ace @@ -111,7 +110,6 @@ require ( github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.1 // indirect github.com/go-resty/resty/v2 v2.17.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect github.com/gofrs/flock v0.10.0 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect diff --git a/go.sum b/go.sum index 35dbbce376..545a131d60 100644 --- a/go.sum +++ b/go.sum @@ -185,8 +185,6 @@ github.com/civo/civogo v0.6.5 h1:nS5TWJB2BnW1X26wN/nWGxYMgj6VEyZxSt/1OlKrQZw= github.com/civo/civogo v0.6.5/go.mod h1:akFVdRAQfJi4t8pGduUOiBwaW/NSC9i45m/dzhF09AY= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/cloudflare-go v0.116.0 h1:iRPMnTtnswRpELO65NTwMX4+RTdxZl+Xf/zi+HPE95s= -github.com/cloudflare/cloudflare-go v0.116.0/go.mod h1:Ds6urDwn/TF2uIU24mu7H91xkKP8gSAHxQ44DSZgVmU= github.com/cloudflare/cloudflare-go/v5 v5.1.0 h1:vvWUtrt5ZPEBFidL2ik64QipXLZmhMBgtRTw4bYvPwE= github.com/cloudflare/cloudflare-go/v5 v5.1.0/go.mod h1:C6OjOlDHOk/g7lXehothXJRFZrSIJMLzOZB2SXQhcjk= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -409,8 +407,6 @@ github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.19.0 h1:EmkZ9RIsX+Uq4DYFowegAuJo8+xdX3T/2dwNPXbxEYE= github.com/goccy/go-yaml v1.19.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=