mirror of
https://github.com/kubernetes-sigs/external-dns.git
synced 2025-08-05 17:16:59 +02:00
chore(code-cleanup): move logic away from main.go add tests (#5222)
* feat(code cleanup): remove from main.go Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * chore(code-cleanup): move logic from main.go add tests Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * chore(code-cleanup): move logic from main.go add tests Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * chore(code-cleanup): move logic from main.go add tests Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * chore(code-cleanup): move logic from main.go add tests Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * chore(code-cleanup): move logic away from main.go add tests Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * chore(code-cleanup): move logic away from main.go add tests Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * chore(code-cleanup): move logic away from main.go add tests Co-authored-by: Michel Loiseleur <97035654+mloiseleur@users.noreply.github.com> * chore(code-cleanup): move logic away from main.go add tests Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> * chore(code-cleanup): move logic away from main.go add tests Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> --------- Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com> Co-authored-by: Michel Loiseleur <97035654+mloiseleur@users.noreply.github.com>
This commit is contained in:
parent
e59e96ea64
commit
65d534e4a1
451
controller/execute.go
Normal file
451
controller/execute.go
Normal file
@ -0,0 +1,451 @@
|
||||
/*
|
||||
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"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
|
||||
"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"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/pkg/apis/externaldns"
|
||||
"sigs.k8s.io/external-dns/pkg/apis/externaldns/validation"
|
||||
"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/ibmcloud"
|
||||
"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/tencentcloud"
|
||||
"sigs.k8s.io/external-dns/provider/transip"
|
||||
"sigs.k8s.io/external-dns/provider/ultradns"
|
||||
"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"
|
||||
)
|
||||
|
||||
func Execute() {
|
||||
cfg := externaldns.NewConfig()
|
||||
if err := cfg.ParseFlags(os.Args[1:]); err != nil {
|
||||
log.Fatalf("flag parsing error: %v", err)
|
||||
}
|
||||
log.Infof("config: %s", cfg)
|
||||
if err := validation.ValidateConfig(cfg); err != nil {
|
||||
log.Fatalf("config validation failed: %v", err)
|
||||
}
|
||||
|
||||
configureLogger(cfg)
|
||||
|
||||
if cfg.DryRun {
|
||||
log.Info("running in dry-run mode. No changes to DNS records will be made.")
|
||||
}
|
||||
|
||||
if log.GetLevel() < log.DebugLevel {
|
||||
// Klog V2 is used by k8s.io/apimachinery/pkg/labels and can throw (a lot) of irrelevant logs
|
||||
// See https://github.com/kubernetes-sigs/external-dns/issues/2348
|
||||
defer klog.ClearLogger()
|
||||
klog.SetLogger(logr.Discard())
|
||||
}
|
||||
|
||||
log.Info(externaldns.Banner())
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
go serveMetrics(cfg.MetricsAddress)
|
||||
go handleSigterm(cancel)
|
||||
|
||||
// Create a source.Config from the flags passed by the user.
|
||||
sourceCfg := source.NewSourceConfig(cfg)
|
||||
|
||||
// Lookup all the selected sources by names and pass them the desired configuration.
|
||||
sources, err := source.ByNames(ctx, &source.SingletonClientGenerator{
|
||||
KubeConfig: cfg.KubeConfig,
|
||||
APIServerURL: cfg.APIServerURL,
|
||||
// If update events are enabled, disable timeout.
|
||||
RequestTimeout: func() time.Duration {
|
||||
if cfg.UpdateEvents {
|
||||
return 0
|
||||
}
|
||||
return cfg.RequestTimeout
|
||||
}(),
|
||||
}, cfg.Sources, sourceCfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Filter targets
|
||||
targetFilter := endpoint.NewTargetNetFilterWithExclusions(cfg.TargetNetFilter, cfg.ExcludeTargetNets)
|
||||
|
||||
// Combine multiple sources into a single, deduplicated source.
|
||||
endpointsSource := source.NewDedupSource(source.NewMultiSource(sources, sourceCfg.DefaultTargets))
|
||||
endpointsSource = source.NewNAT64Source(endpointsSource, cfg.NAT64Networks)
|
||||
endpointsSource = source.NewTargetFilterSource(endpointsSource, targetFilter)
|
||||
|
||||
domainFilter := createDomainFilter(cfg)
|
||||
zoneNameFilter := endpoint.NewDomainFilter(cfg.ZoneNameFilter)
|
||||
zoneIDFilter := provider.NewZoneIDFilter(cfg.ZoneIDFilter)
|
||||
zoneTypeFilter := provider.NewZoneTypeFilter(cfg.AWSZoneType)
|
||||
zoneTagFilter := provider.NewZoneTagFilter(cfg.AWSZoneTagFilter)
|
||||
|
||||
var p provider.Provider
|
||||
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.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.DryRun)
|
||||
case "ultradns":
|
||||
p, err = ultradns.NewUltraDNSProvider(domainFilter, cfg.DryRun)
|
||||
case "civo":
|
||||
p, err = civo.NewCivoProvider(domainFilter, cfg.DryRun)
|
||||
case "cloudflare":
|
||||
p, err = cloudflare.NewCloudFlareProvider(
|
||||
domainFilter,
|
||||
zoneIDFilter,
|
||||
cfg.CloudflareProxied,
|
||||
cfg.DryRun,
|
||||
cfg.CloudflareDNSRecordsPerPage,
|
||||
cfg.CloudflareRegionKey,
|
||||
cloudflare.CustomHostnamesConfig{
|
||||
Enabled: cfg.CloudflareCustomHostnames,
|
||||
MinTLSVersion: cfg.CloudflareCustomHostnamesMinTLSVersion,
|
||||
CertificateAuthority: cfg.CloudflareCustomHostnamesCertificateAuthority,
|
||||
})
|
||||
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.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")
|
||||
} else {
|
||||
authConfig := oci.OCIAuthConfig{UseInstancePrincipal: true}
|
||||
config = &oci.OCIConfig{Auth: authConfig, CompartmentID: cfg.OCICompartmentOCID}
|
||||
}
|
||||
} else {
|
||||
config, err = oci.LoadOCIConfig(cfg.OCIConfigFile)
|
||||
}
|
||||
config.ZoneCacheDuration = cfg.OCIZoneCacheDuration
|
||||
if err == nil {
|
||||
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,
|
||||
},
|
||||
)
|
||||
case "ibmcloud":
|
||||
p, err = ibmcloud.NewIBMCloudProvider(cfg.IBMCloudConfigFile, domainFilter, zoneIDFilter, endpointsSource, cfg.IBMCloudProxied, cfg.DryRun)
|
||||
case "plural":
|
||||
p, err = plural.NewPluralProvider(cfg.PluralCluster, cfg.PluralProvider)
|
||||
case "tencentcloud":
|
||||
p, err = tencentcloud.NewTencentCloudProvider(domainFilter, zoneIDFilter, cfg.TencentCloudConfigFile, cfg.TencentCloudZoneType, cfg.DryRun)
|
||||
case "webhook":
|
||||
p, err = webhook.NewWebhookProvider(cfg.WebhookProviderURL)
|
||||
default:
|
||||
log.Fatalf("unknown dns provider: %s", cfg.Provider)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if cfg.WebhookServer {
|
||||
webhookapi.StartHTTPApi(p, nil, cfg.WebhookProviderReadTimeout, cfg.WebhookProviderWriteTimeout, "127.0.0.1:8888")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if cfg.ProviderCacheTime > 0 {
|
||||
p = provider.NewCachedProvider(
|
||||
p,
|
||||
cfg.ProviderCacheTime,
|
||||
)
|
||||
}
|
||||
|
||||
reg, err := selectRegistry(cfg, p)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
policy, exists := plan.Policies[cfg.Policy]
|
||||
if !exists {
|
||||
log.Fatalf("unknown policy: %s", cfg.Policy)
|
||||
}
|
||||
|
||||
ctrl := Controller{
|
||||
Source: endpointsSource,
|
||||
Registry: reg,
|
||||
Policy: policy,
|
||||
Interval: cfg.Interval,
|
||||
DomainFilter: domainFilter,
|
||||
ManagedRecordTypes: cfg.ManagedDNSRecordTypes,
|
||||
ExcludeRecordTypes: cfg.ExcludeDNSRecordTypes,
|
||||
MinEventSyncInterval: cfg.MinEventSyncInterval,
|
||||
}
|
||||
|
||||
if cfg.Once {
|
||||
err := ctrl.RunOnce(ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if cfg.UpdateEvents {
|
||||
// Add RunOnce as the handler function that will be called when ingress/service sources have changed.
|
||||
// Note that k8s Informers will perform an initial list operation, which results in the handler
|
||||
// function initially being called for every Service/Ingress that exists
|
||||
ctrl.Source.AddEventHandler(ctx, func() { ctrl.ScheduleRunOnce(time.Now()) })
|
||||
}
|
||||
|
||||
ctrl.ScheduleRunOnce(time.Now())
|
||||
ctrl.Run(ctx)
|
||||
}
|
||||
|
||||
// This function configures the logger format and level based on the provided configuration.
|
||||
func configureLogger(cfg *externaldns.Config) {
|
||||
if cfg.LogFormat == "json" {
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
}
|
||||
ll, err := log.ParseLevel(cfg.LogLevel)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to parse log level: %v", err)
|
||||
}
|
||||
log.SetLevel(ll)
|
||||
}
|
||||
|
||||
// selectRegistry selects the appropriate registry implementation based on the configuration in cfg.
|
||||
// It initializes and returns a registry along with any error encountered during setup.
|
||||
// Supported registry types include: dynamodb, noop, txt, and aws-sd.
|
||||
func selectRegistry(cfg *externaldns.Config, p provider.Provider) (registry.Registry, error) {
|
||||
var r registry.Registry
|
||||
var err error
|
||||
switch cfg.Registry {
|
||||
case "dynamodb":
|
||||
var dynamodbOpts []func(*dynamodb.Options)
|
||||
if cfg.AWSDynamoDBRegion != "" {
|
||||
dynamodbOpts = []func(*dynamodb.Options){
|
||||
func(opts *dynamodb.Options) {
|
||||
opts.Region = cfg.AWSDynamoDBRegion
|
||||
},
|
||||
}
|
||||
}
|
||||
r, err = registry.NewDynamoDBRegistry(p, cfg.TXTOwnerID, dynamodb.NewFromConfig(aws.CreateDefaultV2Config(cfg), dynamodbOpts...), cfg.AWSDynamoDBTable, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.ExcludeDNSRecordTypes, []byte(cfg.TXTEncryptAESKey), cfg.TXTCacheInterval)
|
||||
case "noop":
|
||||
r, err = registry.NewNoopRegistry(p)
|
||||
case "txt":
|
||||
r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTOwnerID, cfg.TXTCacheInterval, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.ExcludeDNSRecordTypes, cfg.TXTEncryptEnabled, []byte(cfg.TXTEncryptAESKey), cfg.TXTNewFormatOnly)
|
||||
case "aws-sd":
|
||||
r, err = registry.NewAWSSDRegistry(p, cfg.TXTOwnerID)
|
||||
default:
|
||||
log.Fatalf("unknown registry: %s", cfg.Registry)
|
||||
}
|
||||
return r, err
|
||||
}
|
||||
|
||||
// RegexDomainFilter overrides DomainFilter
|
||||
func createDomainFilter(cfg *externaldns.Config) endpoint.DomainFilter {
|
||||
if cfg.RegexDomainFilter != nil && cfg.RegexDomainFilter.String() != "" {
|
||||
return endpoint.NewRegexDomainFilter(cfg.RegexDomainFilter, cfg.RegexDomainExclusion)
|
||||
} else {
|
||||
return endpoint.NewDomainFilterWithExclusions(cfg.DomainFilter, cfg.ExcludeDomains)
|
||||
}
|
||||
}
|
||||
|
||||
// handleSigterm listens for a SIGTERM signal and triggers the provided cancel function
|
||||
// to gracefully terminate the application. It logs a message when the signal is received.
|
||||
func handleSigterm(cancel func()) {
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, syscall.SIGTERM)
|
||||
<-signals
|
||||
log.Info("Received SIGTERM. Terminating...")
|
||||
cancel()
|
||||
}
|
||||
|
||||
// serveMetrics starts an HTTP server that serves health and metrics endpoints.
|
||||
// The /healthz endpoint returns a 200 OK status to indicate the service is healthy.
|
||||
// The /metrics endpoint serves Prometheus metrics.
|
||||
// The server listens on the specified address and logs debug information about the endpoints.
|
||||
func serveMetrics(address string) {
|
||||
http.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("OK"))
|
||||
})
|
||||
|
||||
log.Debugf("serving 'healthz' on 'localhost:%s/healthz'", address)
|
||||
log.Debugf("serving 'metrics' on 'localhost:%s/metrics'", address)
|
||||
log.Debugf("registered '%d' metrics", len(metrics.RegisterMetric.Metrics))
|
||||
|
||||
http.Handle("/metrics", promhttp.Handler())
|
||||
|
||||
log.Fatal(http.ListenAndServe(address, nil))
|
||||
}
|
321
controller/execute_test.go
Normal file
321
controller/execute_test.go
Normal file
@ -0,0 +1,321 @@
|
||||
/*
|
||||
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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"context"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/pkg/apis/externaldns"
|
||||
"sigs.k8s.io/external-dns/plan"
|
||||
"sigs.k8s.io/external-dns/provider"
|
||||
)
|
||||
|
||||
func TestSelectRegistry(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg *externaldns.Config
|
||||
provider provider.Provider
|
||||
wantErr bool
|
||||
wantType string
|
||||
}{
|
||||
{
|
||||
name: "DynamoDB registry",
|
||||
cfg: &externaldns.Config{
|
||||
Registry: "dynamodb",
|
||||
AWSDynamoDBRegion: "us-west-2",
|
||||
AWSDynamoDBTable: "test-table",
|
||||
TXTOwnerID: "owner-id",
|
||||
TXTWildcardReplacement: "wildcard",
|
||||
ManagedDNSRecordTypes: []string{"A", "CNAME"},
|
||||
ExcludeDNSRecordTypes: []string{"TXT"},
|
||||
TXTCacheInterval: 60,
|
||||
},
|
||||
provider: &MockProvider{},
|
||||
wantErr: false,
|
||||
wantType: "DynamoDBRegistry",
|
||||
},
|
||||
{
|
||||
name: "Noop registry",
|
||||
cfg: &externaldns.Config{
|
||||
Registry: "noop",
|
||||
},
|
||||
provider: &MockProvider{},
|
||||
wantErr: false,
|
||||
wantType: "NoopRegistry",
|
||||
},
|
||||
{
|
||||
name: "TXT registry",
|
||||
cfg: &externaldns.Config{
|
||||
Registry: "txt",
|
||||
TXTPrefix: "prefix",
|
||||
TXTOwnerID: "owner-id",
|
||||
TXTCacheInterval: 60,
|
||||
TXTWildcardReplacement: "wildcard",
|
||||
ManagedDNSRecordTypes: []string{"A", "CNAME"},
|
||||
ExcludeDNSRecordTypes: []string{"TXT"},
|
||||
TXTNewFormatOnly: true,
|
||||
},
|
||||
provider: &MockProvider{},
|
||||
wantErr: false,
|
||||
wantType: "TXTRegistry",
|
||||
},
|
||||
{
|
||||
name: "AWS-SD registry",
|
||||
cfg: &externaldns.Config{
|
||||
Registry: "aws-sd",
|
||||
TXTOwnerID: "owner-id",
|
||||
},
|
||||
provider: &MockProvider{},
|
||||
wantErr: false,
|
||||
wantType: "AWSSDRegistry",
|
||||
},
|
||||
{
|
||||
name: "Unknown registry",
|
||||
cfg: &externaldns.Config{
|
||||
Registry: "unknown",
|
||||
},
|
||||
provider: &MockProvider{},
|
||||
wantErr: true,
|
||||
wantType: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.wantErr {
|
||||
defer func() { log.StandardLogger().ExitFunc = nil }()
|
||||
b := new(bytes.Buffer)
|
||||
log.StandardLogger().ExitFunc = func(int) {}
|
||||
log.StandardLogger().SetOutput(b)
|
||||
|
||||
_, err := selectRegistry(tt.cfg, tt.provider)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, b.String(), "unknown registry: unknown")
|
||||
} else {
|
||||
reg, err := selectRegistry(tt.cfg, tt.provider)
|
||||
assert.NoError(t, err)
|
||||
assert.Contains(t, reflect.TypeOf(reg).String(), tt.wantType)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateDomainFilter(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg *externaldns.Config
|
||||
expectedDomainFilter endpoint.DomainFilter
|
||||
isConfigured bool
|
||||
}{
|
||||
{
|
||||
name: "RegexDomainFilter",
|
||||
cfg: &externaldns.Config{
|
||||
RegexDomainFilter: regexp.MustCompile(`example\.com`),
|
||||
RegexDomainExclusion: regexp.MustCompile(`excluded\.example\.com`),
|
||||
},
|
||||
expectedDomainFilter: endpoint.NewRegexDomainFilter(regexp.MustCompile(`example\.com`), regexp.MustCompile(`excluded\.example\.com`)),
|
||||
isConfigured: true,
|
||||
},
|
||||
{
|
||||
name: "RegexDomainWithoutExclusionFilter",
|
||||
cfg: &externaldns.Config{
|
||||
RegexDomainFilter: regexp.MustCompile(`example\.com`),
|
||||
},
|
||||
expectedDomainFilter: endpoint.NewRegexDomainFilter(regexp.MustCompile(`example\.com`), nil),
|
||||
isConfigured: true,
|
||||
},
|
||||
{
|
||||
name: "DomainFilterWithExclusions",
|
||||
cfg: &externaldns.Config{
|
||||
DomainFilter: []string{"example.com"},
|
||||
ExcludeDomains: []string{"excluded.example.com"},
|
||||
},
|
||||
expectedDomainFilter: endpoint.NewDomainFilterWithExclusions([]string{"example.com"}, []string{"excluded.example.com"}),
|
||||
isConfigured: true,
|
||||
},
|
||||
{
|
||||
name: "DomainFilterWithExclusionsOnly",
|
||||
cfg: &externaldns.Config{
|
||||
ExcludeDomains: []string{"excluded.example.com"},
|
||||
},
|
||||
expectedDomainFilter: endpoint.NewDomainFilterWithExclusions([]string{}, []string{"excluded.example.com"}),
|
||||
isConfigured: true,
|
||||
},
|
||||
{
|
||||
name: "EmptyDomainFilter",
|
||||
cfg: &externaldns.Config{
|
||||
DomainFilter: []string{},
|
||||
ExcludeDomains: []string{},
|
||||
},
|
||||
expectedDomainFilter: endpoint.NewDomainFilterWithExclusions([]string{}, []string{}),
|
||||
isConfigured: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
filter := createDomainFilter(tt.cfg)
|
||||
assert.Equal(t, tt.isConfigured, filter.IsConfigured())
|
||||
assert.Equal(t, tt.expectedDomainFilter, filter)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleSigterm(t *testing.T) {
|
||||
cancelCalled := make(chan bool, 1)
|
||||
cancel := func() {
|
||||
cancelCalled <- true
|
||||
}
|
||||
|
||||
var logOutput bytes.Buffer
|
||||
log.SetOutput(&logOutput)
|
||||
defer log.SetOutput(os.Stderr)
|
||||
|
||||
go handleSigterm(cancel)
|
||||
|
||||
// Simulate sending a SIGTERM signal
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGTERM)
|
||||
err := syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Wait for the cancel function to be called
|
||||
select {
|
||||
case <-cancelCalled:
|
||||
assert.Contains(t, logOutput.String(), "Received SIGTERM. Terminating...")
|
||||
case sig := <-sigChan:
|
||||
assert.Equal(t, syscall.SIGTERM, sig)
|
||||
case <-time.After(1 * time.Second):
|
||||
t.Fatal("cancel function was not called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeMetrics(t *testing.T) {
|
||||
l, _ := net.Listen("tcp", ":0")
|
||||
_ = l.Close()
|
||||
_, port, _ := net.SplitHostPort(l.Addr().String())
|
||||
|
||||
go serveMetrics(fmt.Sprintf(":%s", port))
|
||||
resp, err := http.Get(fmt.Sprintf("http://localhost:%s", port) + "/healthz")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
|
||||
resp, err = http.Get(fmt.Sprintf("http://localhost:%s", port) + "/metrics")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
}
|
||||
|
||||
func TestConfigureLogger(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg *externaldns.Config
|
||||
wantLevel log.Level
|
||||
wantJSON bool
|
||||
wantErr bool
|
||||
wantErrMsg string
|
||||
}{
|
||||
{
|
||||
name: "Default log format and level",
|
||||
cfg: &externaldns.Config{
|
||||
LogLevel: "info",
|
||||
LogFormat: "text",
|
||||
},
|
||||
wantLevel: log.InfoLevel,
|
||||
},
|
||||
{
|
||||
name: "JSON log format",
|
||||
cfg: &externaldns.Config{
|
||||
LogLevel: "debug",
|
||||
LogFormat: "json",
|
||||
},
|
||||
wantLevel: log.DebugLevel,
|
||||
wantJSON: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid log level",
|
||||
cfg: &externaldns.Config{
|
||||
LogLevel: "invalid",
|
||||
LogFormat: "text",
|
||||
},
|
||||
wantLevel: log.InfoLevel,
|
||||
wantErr: true,
|
||||
wantErrMsg: "failed to parse log level",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.wantErr {
|
||||
defer func() { log.StandardLogger().ExitFunc = nil }()
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
var captureLogFatal bool
|
||||
log.StandardLogger().ExitFunc = func(int) { captureLogFatal = true }
|
||||
log.StandardLogger().SetOutput(b)
|
||||
|
||||
configureLogger(tt.cfg)
|
||||
|
||||
assert.True(t, captureLogFatal)
|
||||
assert.Contains(t, b.String(), tt.wantErrMsg)
|
||||
} else {
|
||||
configureLogger(tt.cfg)
|
||||
assert.Equal(t, tt.wantLevel, log.GetLevel())
|
||||
|
||||
if tt.wantJSON {
|
||||
assert.IsType(t, &log.JSONFormatter{}, log.StandardLogger().Formatter)
|
||||
} else {
|
||||
assert.IsType(t, &log.TextFormatter{}, log.StandardLogger().Formatter)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// mocks
|
||||
type MockProvider struct{}
|
||||
|
||||
func (m *MockProvider) Records(ctx context.Context) ([]*endpoint.Endpoint, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p *MockProvider) ApplyChanges(ctx context.Context, changes *plan.Changes) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MockProvider) AdjustEndpoints(endpoints []*endpoint.Endpoint) ([]*endpoint.Endpoint, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (m *MockProvider) GetDomainFilter() endpoint.DomainFilterInterface {
|
||||
return nil
|
||||
}
|
450
main.go
450
main.go
@ -17,459 +17,11 @@ limitations under the License.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
|
||||
"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"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"sigs.k8s.io/external-dns/controller"
|
||||
"sigs.k8s.io/external-dns/endpoint"
|
||||
"sigs.k8s.io/external-dns/pkg/apis/externaldns"
|
||||
"sigs.k8s.io/external-dns/pkg/apis/externaldns/validation"
|
||||
"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/ibmcloud"
|
||||
"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/tencentcloud"
|
||||
"sigs.k8s.io/external-dns/provider/transip"
|
||||
"sigs.k8s.io/external-dns/provider/ultradns"
|
||||
"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"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cfg := externaldns.NewConfig()
|
||||
if err := cfg.ParseFlags(os.Args[1:]); err != nil {
|
||||
log.Fatalf("flag parsing error: %v", err)
|
||||
}
|
||||
if cfg.LogFormat == "json" {
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
}
|
||||
log.Infof("config: %s", cfg)
|
||||
|
||||
if err := validation.ValidateConfig(cfg); err != nil {
|
||||
log.Fatalf("config validation failed: %v", err)
|
||||
}
|
||||
|
||||
if cfg.DryRun {
|
||||
log.Info("running in dry-run mode. No changes to DNS records will be made.")
|
||||
}
|
||||
|
||||
ll, err := log.ParseLevel(cfg.LogLevel)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to parse log level: %v", err)
|
||||
}
|
||||
log.SetLevel(ll)
|
||||
|
||||
if ll < log.DebugLevel {
|
||||
// Klog V2 is used by k8s.io/apimachinery/pkg/labels and can throw (a lot) of irrelevant logs
|
||||
// See https://github.com/kubernetes-sigs/external-dns/issues/2348
|
||||
defer klog.ClearLogger()
|
||||
klog.SetLogger(logr.Discard())
|
||||
}
|
||||
|
||||
log.Info(externaldns.Banner())
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
go serveMetrics(cfg.MetricsAddress)
|
||||
go handleSigterm(cancel)
|
||||
|
||||
// error is explicitly ignored because the filter is already validated in validation.ValidateConfig
|
||||
labelSelector, _ := labels.Parse(cfg.LabelFilter)
|
||||
|
||||
// Create a source.Config from the flags passed by the user.
|
||||
sourceCfg := &source.Config{
|
||||
Namespace: cfg.Namespace,
|
||||
AnnotationFilter: cfg.AnnotationFilter,
|
||||
LabelFilter: labelSelector,
|
||||
IngressClassNames: cfg.IngressClassNames,
|
||||
FQDNTemplate: cfg.FQDNTemplate,
|
||||
CombineFQDNAndAnnotation: cfg.CombineFQDNAndAnnotation,
|
||||
IgnoreHostnameAnnotation: cfg.IgnoreHostnameAnnotation,
|
||||
IgnoreNonHostNetworkPods: cfg.IgnoreNonHostNetworkPods,
|
||||
IgnoreIngressTLSSpec: cfg.IgnoreIngressTLSSpec,
|
||||
IgnoreIngressRulesSpec: cfg.IgnoreIngressRulesSpec,
|
||||
ListenEndpointEvents: cfg.ListenEndpointEvents,
|
||||
GatewayName: cfg.GatewayName,
|
||||
GatewayNamespace: cfg.GatewayNamespace,
|
||||
GatewayLabelFilter: cfg.GatewayLabelFilter,
|
||||
Compatibility: cfg.Compatibility,
|
||||
PodSourceDomain: cfg.PodSourceDomain,
|
||||
PublishInternal: cfg.PublishInternal,
|
||||
PublishHostIP: cfg.PublishHostIP,
|
||||
AlwaysPublishNotReadyAddresses: cfg.AlwaysPublishNotReadyAddresses,
|
||||
ConnectorServer: cfg.ConnectorSourceServer,
|
||||
CRDSourceAPIVersion: cfg.CRDSourceAPIVersion,
|
||||
CRDSourceKind: cfg.CRDSourceKind,
|
||||
KubeConfig: cfg.KubeConfig,
|
||||
APIServerURL: cfg.APIServerURL,
|
||||
ServiceTypeFilter: cfg.ServiceTypeFilter,
|
||||
CFAPIEndpoint: cfg.CFAPIEndpoint,
|
||||
CFUsername: cfg.CFUsername,
|
||||
CFPassword: cfg.CFPassword,
|
||||
GlooNamespaces: cfg.GlooNamespaces,
|
||||
SkipperRouteGroupVersion: cfg.SkipperRouteGroupVersion,
|
||||
RequestTimeout: cfg.RequestTimeout,
|
||||
DefaultTargets: cfg.DefaultTargets,
|
||||
OCPRouterName: cfg.OCPRouterName,
|
||||
UpdateEvents: cfg.UpdateEvents,
|
||||
ResolveLoadBalancerHostname: cfg.ResolveServiceLoadBalancerHostname,
|
||||
TraefikDisableLegacy: cfg.TraefikDisableLegacy,
|
||||
TraefikDisableNew: cfg.TraefikDisableNew,
|
||||
ExposeInternalIPv6: cfg.ExposeInternalIPV6,
|
||||
}
|
||||
|
||||
// Lookup all the selected sources by names and pass them the desired configuration.
|
||||
sources, err := source.ByNames(ctx, &source.SingletonClientGenerator{
|
||||
KubeConfig: cfg.KubeConfig,
|
||||
APIServerURL: cfg.APIServerURL,
|
||||
// If update events are enabled, disable timeout.
|
||||
RequestTimeout: func() time.Duration {
|
||||
if cfg.UpdateEvents {
|
||||
return 0
|
||||
}
|
||||
return cfg.RequestTimeout
|
||||
}(),
|
||||
}, cfg.Sources, sourceCfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Filter targets
|
||||
targetFilter := endpoint.NewTargetNetFilterWithExclusions(cfg.TargetNetFilter, cfg.ExcludeTargetNets)
|
||||
|
||||
// Combine multiple sources into a single, deduplicated source.
|
||||
endpointsSource := source.NewDedupSource(source.NewMultiSource(sources, sourceCfg.DefaultTargets))
|
||||
endpointsSource = source.NewNAT64Source(endpointsSource, cfg.NAT64Networks)
|
||||
endpointsSource = source.NewTargetFilterSource(endpointsSource, targetFilter)
|
||||
|
||||
// RegexDomainFilter overrides DomainFilter
|
||||
var domainFilter endpoint.DomainFilter
|
||||
if cfg.RegexDomainFilter.String() != "" {
|
||||
domainFilter = endpoint.NewRegexDomainFilter(cfg.RegexDomainFilter, cfg.RegexDomainExclusion)
|
||||
} else {
|
||||
domainFilter = endpoint.NewDomainFilterWithExclusions(cfg.DomainFilter, cfg.ExcludeDomains)
|
||||
}
|
||||
zoneNameFilter := endpoint.NewDomainFilter(cfg.ZoneNameFilter)
|
||||
zoneIDFilter := provider.NewZoneIDFilter(cfg.ZoneIDFilter)
|
||||
zoneTypeFilter := provider.NewZoneTypeFilter(cfg.AWSZoneType)
|
||||
zoneTagFilter := provider.NewZoneTagFilter(cfg.AWSZoneTagFilter)
|
||||
|
||||
var p provider.Provider
|
||||
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.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.DryRun)
|
||||
case "ultradns":
|
||||
p, err = ultradns.NewUltraDNSProvider(domainFilter, cfg.DryRun)
|
||||
case "civo":
|
||||
p, err = civo.NewCivoProvider(domainFilter, cfg.DryRun)
|
||||
case "cloudflare":
|
||||
p, err = cloudflare.NewCloudFlareProvider(
|
||||
domainFilter,
|
||||
zoneIDFilter,
|
||||
cfg.CloudflareProxied,
|
||||
cfg.DryRun,
|
||||
cfg.CloudflareDNSRecordsPerPage,
|
||||
cfg.CloudflareRegionKey,
|
||||
cloudflare.CustomHostnamesConfig{
|
||||
Enabled: cfg.CloudflareCustomHostnames,
|
||||
MinTLSVersion: cfg.CloudflareCustomHostnamesMinTLSVersion,
|
||||
CertificateAuthority: cfg.CloudflareCustomHostnamesCertificateAuthority,
|
||||
})
|
||||
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.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")
|
||||
} else {
|
||||
authConfig := oci.OCIAuthConfig{UseInstancePrincipal: true}
|
||||
config = &oci.OCIConfig{Auth: authConfig, CompartmentID: cfg.OCICompartmentOCID}
|
||||
}
|
||||
} else {
|
||||
config, err = oci.LoadOCIConfig(cfg.OCIConfigFile)
|
||||
}
|
||||
config.ZoneCacheDuration = cfg.OCIZoneCacheDuration
|
||||
if err == nil {
|
||||
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,
|
||||
},
|
||||
)
|
||||
case "ibmcloud":
|
||||
p, err = ibmcloud.NewIBMCloudProvider(cfg.IBMCloudConfigFile, domainFilter, zoneIDFilter, endpointsSource, cfg.IBMCloudProxied, cfg.DryRun)
|
||||
case "plural":
|
||||
p, err = plural.NewPluralProvider(cfg.PluralCluster, cfg.PluralProvider)
|
||||
case "tencentcloud":
|
||||
p, err = tencentcloud.NewTencentCloudProvider(domainFilter, zoneIDFilter, cfg.TencentCloudConfigFile, cfg.TencentCloudZoneType, cfg.DryRun)
|
||||
case "webhook":
|
||||
p, err = webhook.NewWebhookProvider(cfg.WebhookProviderURL)
|
||||
default:
|
||||
log.Fatalf("unknown dns provider: %s", cfg.Provider)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if cfg.WebhookServer {
|
||||
webhookapi.StartHTTPApi(p, nil, cfg.WebhookProviderReadTimeout, cfg.WebhookProviderWriteTimeout, "127.0.0.1:8888")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if cfg.ProviderCacheTime > 0 {
|
||||
p = provider.NewCachedProvider(
|
||||
p,
|
||||
cfg.ProviderCacheTime,
|
||||
)
|
||||
}
|
||||
|
||||
var r registry.Registry
|
||||
switch cfg.Registry {
|
||||
case "dynamodb":
|
||||
var dynamodbOpts []func(*dynamodb.Options)
|
||||
if cfg.AWSDynamoDBRegion != "" {
|
||||
dynamodbOpts = []func(*dynamodb.Options){
|
||||
func(opts *dynamodb.Options) {
|
||||
opts.Region = cfg.AWSDynamoDBRegion
|
||||
},
|
||||
}
|
||||
}
|
||||
r, err = registry.NewDynamoDBRegistry(p, cfg.TXTOwnerID, dynamodb.NewFromConfig(aws.CreateDefaultV2Config(cfg), dynamodbOpts...), cfg.AWSDynamoDBTable, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.ExcludeDNSRecordTypes, []byte(cfg.TXTEncryptAESKey), cfg.TXTCacheInterval)
|
||||
case "noop":
|
||||
r, err = registry.NewNoopRegistry(p)
|
||||
case "txt":
|
||||
r, err = registry.NewTXTRegistry(p, cfg.TXTPrefix, cfg.TXTSuffix, cfg.TXTOwnerID, cfg.TXTCacheInterval, cfg.TXTWildcardReplacement, cfg.ManagedDNSRecordTypes, cfg.ExcludeDNSRecordTypes, cfg.TXTEncryptEnabled, []byte(cfg.TXTEncryptAESKey), cfg.TXTNewFormatOnly)
|
||||
case "aws-sd":
|
||||
r, err = registry.NewAWSSDRegistry(p, cfg.TXTOwnerID)
|
||||
default:
|
||||
log.Fatalf("unknown registry: %s", cfg.Registry)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
policy, exists := plan.Policies[cfg.Policy]
|
||||
if !exists {
|
||||
log.Fatalf("unknown policy: %s", cfg.Policy)
|
||||
}
|
||||
|
||||
ctrl := controller.Controller{
|
||||
Source: endpointsSource,
|
||||
Registry: r,
|
||||
Policy: policy,
|
||||
Interval: cfg.Interval,
|
||||
DomainFilter: domainFilter,
|
||||
ManagedRecordTypes: cfg.ManagedDNSRecordTypes,
|
||||
ExcludeRecordTypes: cfg.ExcludeDNSRecordTypes,
|
||||
MinEventSyncInterval: cfg.MinEventSyncInterval,
|
||||
}
|
||||
|
||||
if cfg.Once {
|
||||
err := ctrl.RunOnce(ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if cfg.UpdateEvents {
|
||||
// Add RunOnce as the handler function that will be called when ingress/service sources have changed.
|
||||
// Note that k8s Informers will perform an initial list operation, which results in the handler
|
||||
// function initially being called for every Service/Ingress that exists
|
||||
ctrl.Source.AddEventHandler(ctx, func() { ctrl.ScheduleRunOnce(time.Now()) })
|
||||
}
|
||||
|
||||
ctrl.ScheduleRunOnce(time.Now())
|
||||
ctrl.Run(ctx)
|
||||
}
|
||||
|
||||
func handleSigterm(cancel func()) {
|
||||
signals := make(chan os.Signal, 1)
|
||||
signal.Notify(signals, syscall.SIGTERM)
|
||||
<-signals
|
||||
log.Info("Received SIGTERM. Terminating...")
|
||||
cancel()
|
||||
}
|
||||
|
||||
func serveMetrics(address string) {
|
||||
http.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("OK"))
|
||||
})
|
||||
|
||||
log.Debugf("serving 'healthz' on 'localhost:%s/healthz'", address)
|
||||
log.Debugf("serving 'metrics' on 'localhost:%s/metrics'", address)
|
||||
log.Debugf("registered '%d' metrics", len(metrics.RegisterMetric.Metrics))
|
||||
|
||||
http.Handle("/metrics", promhttp.Handler())
|
||||
|
||||
log.Fatal(http.ListenAndServe(address, nil))
|
||||
controller.Execute()
|
||||
}
|
||||
|
@ -30,8 +30,6 @@ import (
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"sigs.k8s.io/external-dns/source"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -448,7 +446,7 @@ func App(cfg *Config) *kingpin.Application {
|
||||
app.Flag("gloo-namespace", "The Gloo Proxy namespace; specify multiple times for multiple namespaces. (default: gloo-system)").Default("gloo-system").StringsVar(&cfg.GlooNamespaces)
|
||||
|
||||
// Flags related to Skipper RouteGroup
|
||||
app.Flag("skipper-routegroup-groupversion", "The resource version for skipper routegroup").Default(source.DefaultRoutegroupVersion).StringVar(&cfg.SkipperRouteGroupVersion)
|
||||
app.Flag("skipper-routegroup-groupversion", "The resource version for skipper routegroup").Default(defaultConfig.SkipperRouteGroupVersion).StringVar(&cfg.SkipperRouteGroupVersion)
|
||||
|
||||
// Flags related to processing source
|
||||
app.Flag("source", "The resource types that are queried for endpoints; specify multiple times for multiple sources (required, options: service, ingress, node, pod, fake, connector, gateway-httproute, gateway-grpcroute, gateway-tlsroute, gateway-tcproute, gateway-udproute, istio-gateway, istio-virtualservice, cloudfoundry, contour-httpproxy, gloo-proxy, crd, empty, skipper-routegroup, openshift-route, ambassador-host, kong-tcpingress, f5-virtualserver, f5-transportserver, traefik-proxy)").Required().PlaceHolder("source").EnumsVar(&cfg.Sources, "service", "ingress", "node", "pod", "gateway-httproute", "gateway-grpcroute", "gateway-tlsroute", "gateway-tcproute", "gateway-udproute", "istio-gateway", "istio-virtualservice", "cloudfoundry", "contour-httpproxy", "gloo-proxy", "fake", "connector", "crd", "empty", "skipper-routegroup", "openshift-route", "ambassador-host", "kong-tcpingress", "f5-virtualserver", "f5-transportserver", "traefik-proxy")
|
||||
|
@ -36,6 +36,8 @@ import (
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
gateway "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned"
|
||||
|
||||
"sigs.k8s.io/external-dns/pkg/apis/externaldns"
|
||||
)
|
||||
|
||||
// ErrSourceNotFound is returned when a requested source doesn't exist.
|
||||
@ -83,6 +85,51 @@ type Config struct {
|
||||
ExposeInternalIPv6 bool
|
||||
}
|
||||
|
||||
func NewSourceConfig(cfg *externaldns.Config) *Config {
|
||||
// error is explicitly ignored because the filter is already validated in validation.ValidateConfig
|
||||
labelSelector, _ := labels.Parse(cfg.LabelFilter)
|
||||
return &Config{
|
||||
Namespace: cfg.Namespace,
|
||||
AnnotationFilter: cfg.AnnotationFilter,
|
||||
LabelFilter: labelSelector,
|
||||
IngressClassNames: cfg.IngressClassNames,
|
||||
FQDNTemplate: cfg.FQDNTemplate,
|
||||
CombineFQDNAndAnnotation: cfg.CombineFQDNAndAnnotation,
|
||||
IgnoreHostnameAnnotation: cfg.IgnoreHostnameAnnotation,
|
||||
IgnoreNonHostNetworkPods: cfg.IgnoreNonHostNetworkPods,
|
||||
IgnoreIngressTLSSpec: cfg.IgnoreIngressTLSSpec,
|
||||
IgnoreIngressRulesSpec: cfg.IgnoreIngressRulesSpec,
|
||||
ListenEndpointEvents: cfg.ListenEndpointEvents,
|
||||
GatewayName: cfg.GatewayName,
|
||||
GatewayNamespace: cfg.GatewayNamespace,
|
||||
GatewayLabelFilter: cfg.GatewayLabelFilter,
|
||||
Compatibility: cfg.Compatibility,
|
||||
PodSourceDomain: cfg.PodSourceDomain,
|
||||
PublishInternal: cfg.PublishInternal,
|
||||
PublishHostIP: cfg.PublishHostIP,
|
||||
AlwaysPublishNotReadyAddresses: cfg.AlwaysPublishNotReadyAddresses,
|
||||
ConnectorServer: cfg.ConnectorSourceServer,
|
||||
CRDSourceAPIVersion: cfg.CRDSourceAPIVersion,
|
||||
CRDSourceKind: cfg.CRDSourceKind,
|
||||
KubeConfig: cfg.KubeConfig,
|
||||
APIServerURL: cfg.APIServerURL,
|
||||
ServiceTypeFilter: cfg.ServiceTypeFilter,
|
||||
CFAPIEndpoint: cfg.CFAPIEndpoint,
|
||||
CFUsername: cfg.CFUsername,
|
||||
CFPassword: cfg.CFPassword,
|
||||
GlooNamespaces: cfg.GlooNamespaces,
|
||||
SkipperRouteGroupVersion: cfg.SkipperRouteGroupVersion,
|
||||
RequestTimeout: cfg.RequestTimeout,
|
||||
DefaultTargets: cfg.DefaultTargets,
|
||||
OCPRouterName: cfg.OCPRouterName,
|
||||
UpdateEvents: cfg.UpdateEvents,
|
||||
ResolveLoadBalancerHostname: cfg.ResolveServiceLoadBalancerHostname,
|
||||
TraefikDisableLegacy: cfg.TraefikDisableLegacy,
|
||||
TraefikDisableNew: cfg.TraefikDisableNew,
|
||||
ExposeInternalIPv6: cfg.ExposeInternalIPV6,
|
||||
}
|
||||
}
|
||||
|
||||
// ClientGenerator provides clients
|
||||
type ClientGenerator interface {
|
||||
KubeClient() (kubernetes.Interface, error)
|
||||
|
Loading…
Reference in New Issue
Block a user