• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubernetes-sigs / external-dns / 14148469308

29 Mar 2025 06:48PM UTC coverage: 71.607% (+0.2%) from 71.367%
14148469308

Pull #5222

github

ivankatliarchuk
chore(code-cleanup): move logic away from main.go add tests

Signed-off-by: ivan katliarchuk <ivan.katliarchuk@gmail.com>
Pull Request #5222: chore(code-cleanup): move logic away from main.go add tests

56 of 378 new or added lines in 4 files covered. (14.81%)

13 existing lines in 1 file now uncovered.

14363 of 20058 relevant lines covered (71.61%)

703.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

16.57
/controller/execute.go
1
/*
2
Copyright 2025 The Kubernetes Authors.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package controller
18

19
import (
20
        "context"
21
        "fmt"
22
        "net/http"
23
        "os"
24
        "os/signal"
25
        "syscall"
26
        "time"
27

28
        "github.com/aws/aws-sdk-go-v2/service/dynamodb"
29
        "github.com/aws/aws-sdk-go-v2/service/route53"
30
        sd "github.com/aws/aws-sdk-go-v2/service/servicediscovery"
31
        "github.com/go-logr/logr"
32
        "github.com/prometheus/client_golang/prometheus/promhttp"
33
        log "github.com/sirupsen/logrus"
34
        "k8s.io/klog/v2"
35

36
        "sigs.k8s.io/external-dns/endpoint"
37
        "sigs.k8s.io/external-dns/pkg/apis/externaldns"
38
        "sigs.k8s.io/external-dns/pkg/apis/externaldns/validation"
39
        "sigs.k8s.io/external-dns/pkg/metrics"
40
        "sigs.k8s.io/external-dns/plan"
41
        "sigs.k8s.io/external-dns/provider"
42
        "sigs.k8s.io/external-dns/provider/akamai"
43
        "sigs.k8s.io/external-dns/provider/alibabacloud"
44
        "sigs.k8s.io/external-dns/provider/aws"
45
        "sigs.k8s.io/external-dns/provider/awssd"
46
        "sigs.k8s.io/external-dns/provider/azure"
47
        "sigs.k8s.io/external-dns/provider/civo"
48
        "sigs.k8s.io/external-dns/provider/cloudflare"
49
        "sigs.k8s.io/external-dns/provider/coredns"
50
        "sigs.k8s.io/external-dns/provider/digitalocean"
51
        "sigs.k8s.io/external-dns/provider/dnsimple"
52
        "sigs.k8s.io/external-dns/provider/exoscale"
53
        "sigs.k8s.io/external-dns/provider/gandi"
54
        "sigs.k8s.io/external-dns/provider/godaddy"
55
        "sigs.k8s.io/external-dns/provider/google"
56
        "sigs.k8s.io/external-dns/provider/ibmcloud"
57
        "sigs.k8s.io/external-dns/provider/inmemory"
58
        "sigs.k8s.io/external-dns/provider/linode"
59
        "sigs.k8s.io/external-dns/provider/ns1"
60
        "sigs.k8s.io/external-dns/provider/oci"
61
        "sigs.k8s.io/external-dns/provider/ovh"
62
        "sigs.k8s.io/external-dns/provider/pdns"
63
        "sigs.k8s.io/external-dns/provider/pihole"
64
        "sigs.k8s.io/external-dns/provider/plural"
65
        "sigs.k8s.io/external-dns/provider/rfc2136"
66
        "sigs.k8s.io/external-dns/provider/scaleway"
67
        "sigs.k8s.io/external-dns/provider/tencentcloud"
68
        "sigs.k8s.io/external-dns/provider/transip"
69
        "sigs.k8s.io/external-dns/provider/ultradns"
70
        "sigs.k8s.io/external-dns/provider/webhook"
71
        webhookapi "sigs.k8s.io/external-dns/provider/webhook/api"
72
        "sigs.k8s.io/external-dns/registry"
73
        "sigs.k8s.io/external-dns/source"
74
)
75

NEW
76
func Execute() {
×
NEW
77
        cfg := externaldns.NewConfig()
×
NEW
78
        if err := cfg.ParseFlags(os.Args[1:]); err != nil {
×
NEW
79
                log.Fatalf("flag parsing error: %v", err)
×
NEW
80
        }
×
NEW
81
        log.Infof("config: %s", cfg)
×
NEW
82
        if err := validation.ValidateConfig(cfg); err != nil {
×
NEW
83
                log.Fatalf("config validation failed: %v", err)
×
NEW
84
        }
×
85

NEW
86
        configureLogger(cfg)
×
NEW
87

×
NEW
88
        if cfg.DryRun {
×
NEW
89
                log.Info("running in dry-run mode. No changes to DNS records will be made.")
×
NEW
90
        }
×
91

NEW
92
        if log.GetLevel() < log.DebugLevel {
×
NEW
93
                // Klog V2 is used by k8s.io/apimachinery/pkg/labels and can throw (a lot) of irrelevant logs
×
NEW
94
                // See https://github.com/kubernetes-sigs/external-dns/issues/2348
×
NEW
95
                defer klog.ClearLogger()
×
NEW
96
                klog.SetLogger(logr.Discard())
×
NEW
97
        }
×
98

NEW
99
        log.Info(externaldns.Banner())
×
NEW
100

×
NEW
101
        ctx, cancel := context.WithCancel(context.Background())
×
NEW
102

×
NEW
103
        go serveMetrics(cfg.MetricsAddress)
×
NEW
104
        go handleSigterm(cancel)
×
NEW
105

×
NEW
106
        // Create a source.Config from the flags passed by the user.
×
NEW
107
        sourceCfg := source.NewSourceConfig(cfg)
×
NEW
108

×
NEW
109
        // Lookup all the selected sources by names and pass them the desired configuration.
×
NEW
110
        sources, err := source.ByNames(ctx, &source.SingletonClientGenerator{
×
NEW
111
                KubeConfig:   cfg.KubeConfig,
×
NEW
112
                APIServerURL: cfg.APIServerURL,
×
NEW
113
                // If update events are enabled, disable timeout.
×
NEW
114
                RequestTimeout: func() time.Duration {
×
NEW
115
                        if cfg.UpdateEvents {
×
NEW
116
                                return 0
×
NEW
117
                        }
×
NEW
118
                        return cfg.RequestTimeout
×
119
                }(),
120
        }, cfg.Sources, sourceCfg)
NEW
121
        if err != nil {
×
NEW
122
                log.Fatal(err)
×
NEW
123
        }
×
124

125
        // Filter targets
NEW
126
        targetFilter := endpoint.NewTargetNetFilterWithExclusions(cfg.TargetNetFilter, cfg.ExcludeTargetNets)
×
NEW
127

×
NEW
128
        // Combine multiple sources into a single, deduplicated source.
×
NEW
129
        endpointsSource := source.NewDedupSource(source.NewMultiSource(sources, sourceCfg.DefaultTargets))
×
NEW
130
        endpointsSource = source.NewNAT64Source(endpointsSource, cfg.NAT64Networks)
×
NEW
131
        endpointsSource = source.NewTargetFilterSource(endpointsSource, targetFilter)
×
NEW
132

×
NEW
133
        domainFilter := createDomainFilter(cfg)
×
NEW
134
        zoneNameFilter := endpoint.NewDomainFilter(cfg.ZoneNameFilter)
×
NEW
135
        zoneIDFilter := provider.NewZoneIDFilter(cfg.ZoneIDFilter)
×
NEW
136
        zoneTypeFilter := provider.NewZoneTypeFilter(cfg.AWSZoneType)
×
NEW
137
        zoneTagFilter := provider.NewZoneTagFilter(cfg.AWSZoneTagFilter)
×
NEW
138

×
NEW
139
        var p provider.Provider
×
NEW
140
        switch cfg.Provider {
×
NEW
141
        case "akamai":
×
NEW
142
                p, err = akamai.NewAkamaiProvider(
×
NEW
143
                        akamai.AkamaiConfig{
×
NEW
144
                                DomainFilter:          domainFilter,
×
NEW
145
                                ZoneIDFilter:          zoneIDFilter,
×
NEW
146
                                ServiceConsumerDomain: cfg.AkamaiServiceConsumerDomain,
×
NEW
147
                                ClientToken:           cfg.AkamaiClientToken,
×
NEW
148
                                ClientSecret:          cfg.AkamaiClientSecret,
×
NEW
149
                                AccessToken:           cfg.AkamaiAccessToken,
×
NEW
150
                                EdgercPath:            cfg.AkamaiEdgercPath,
×
NEW
151
                                EdgercSection:         cfg.AkamaiEdgercSection,
×
NEW
152
                                DryRun:                cfg.DryRun,
×
NEW
153
                        }, nil)
×
NEW
154
        case "alibabacloud":
×
NEW
155
                p, err = alibabacloud.NewAlibabaCloudProvider(cfg.AlibabaCloudConfigFile, domainFilter, zoneIDFilter, cfg.AlibabaCloudZoneType, cfg.DryRun)
×
NEW
156
        case "aws":
×
NEW
157
                configs := aws.CreateV2Configs(cfg)
×
NEW
158
                clients := make(map[string]aws.Route53API, len(configs))
×
NEW
159
                for profile, config := range configs {
×
NEW
160
                        clients[profile] = route53.NewFromConfig(config)
×
NEW
161
                }
×
162

NEW
163
                p, err = aws.NewAWSProvider(
×
NEW
164
                        aws.AWSConfig{
×
NEW
165
                                DomainFilter:          domainFilter,
×
NEW
166
                                ZoneIDFilter:          zoneIDFilter,
×
NEW
167
                                ZoneTypeFilter:        zoneTypeFilter,
×
NEW
168
                                ZoneTagFilter:         zoneTagFilter,
×
NEW
169
                                ZoneMatchParent:       cfg.AWSZoneMatchParent,
×
NEW
170
                                BatchChangeSize:       cfg.AWSBatchChangeSize,
×
NEW
171
                                BatchChangeSizeBytes:  cfg.AWSBatchChangeSizeBytes,
×
NEW
172
                                BatchChangeSizeValues: cfg.AWSBatchChangeSizeValues,
×
NEW
173
                                BatchChangeInterval:   cfg.AWSBatchChangeInterval,
×
NEW
174
                                EvaluateTargetHealth:  cfg.AWSEvaluateTargetHealth,
×
NEW
175
                                PreferCNAME:           cfg.AWSPreferCNAME,
×
NEW
176
                                DryRun:                cfg.DryRun,
×
NEW
177
                                ZoneCacheDuration:     cfg.AWSZoneCacheDuration,
×
NEW
178
                        },
×
NEW
179
                        clients,
×
NEW
180
                )
×
NEW
181
        case "aws-sd":
×
NEW
182
                // Check that only compatible Registry is used with AWS-SD
×
NEW
183
                if cfg.Registry != "noop" && cfg.Registry != "aws-sd" {
×
NEW
184
                        log.Infof("Registry \"%s\" cannot be used with AWS Cloud Map. Switching to \"aws-sd\".", cfg.Registry)
×
NEW
185
                        cfg.Registry = "aws-sd"
×
NEW
186
                }
×
NEW
187
                p, err = awssd.NewAWSSDProvider(domainFilter, cfg.AWSZoneType, cfg.DryRun, cfg.AWSSDServiceCleanup, cfg.TXTOwnerID, cfg.AWSSDCreateTag, sd.NewFromConfig(aws.CreateDefaultV2Config(cfg)))
×
NEW
188
        case "azure-dns", "azure":
×
NEW
189
                p, err = azure.NewAzureProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.DryRun)
×
NEW
190
        case "azure-private-dns":
×
NEW
191
                p, err = azure.NewAzurePrivateDNSProvider(cfg.AzureConfigFile, domainFilter, zoneNameFilter, zoneIDFilter, cfg.AzureSubscriptionID, cfg.AzureResourceGroup, cfg.AzureUserAssignedIdentityClientID, cfg.AzureActiveDirectoryAuthorityHost, cfg.AzureZonesCacheDuration, cfg.DryRun)
×
NEW
192
        case "ultradns":
×
NEW
193
                p, err = ultradns.NewUltraDNSProvider(domainFilter, cfg.DryRun)
×
NEW
194
        case "civo":
×
NEW
195
                p, err = civo.NewCivoProvider(domainFilter, cfg.DryRun)
×
NEW
196
        case "cloudflare":
×
NEW
197
                p, err = cloudflare.NewCloudFlareProvider(
×
NEW
198
                        domainFilter,
×
NEW
199
                        zoneIDFilter,
×
NEW
200
                        cfg.CloudflareProxied,
×
NEW
201
                        cfg.DryRun,
×
NEW
202
                        cfg.CloudflareDNSRecordsPerPage,
×
NEW
203
                        cfg.CloudflareRegionKey,
×
NEW
204
                        cloudflare.CustomHostnamesConfig{
×
NEW
205
                                Enabled:              cfg.CloudflareCustomHostnames,
×
NEW
206
                                MinTLSVersion:        cfg.CloudflareCustomHostnamesMinTLSVersion,
×
NEW
207
                                CertificateAuthority: cfg.CloudflareCustomHostnamesCertificateAuthority,
×
NEW
208
                        })
×
NEW
209
        case "google":
×
NEW
210
                p, err = google.NewGoogleProvider(ctx, cfg.GoogleProject, domainFilter, zoneIDFilter, cfg.GoogleBatchChangeSize, cfg.GoogleBatchChangeInterval, cfg.GoogleZoneVisibility, cfg.DryRun)
×
NEW
211
        case "digitalocean":
×
NEW
212
                p, err = digitalocean.NewDigitalOceanProvider(ctx, domainFilter, cfg.DryRun, cfg.DigitalOceanAPIPageSize)
×
NEW
213
        case "ovh":
×
NEW
214
                p, err = ovh.NewOVHProvider(ctx, domainFilter, cfg.OVHEndpoint, cfg.OVHApiRateLimit, cfg.OVHEnableCNAMERelative, cfg.DryRun)
×
NEW
215
        case "linode":
×
NEW
216
                p, err = linode.NewLinodeProvider(domainFilter, cfg.DryRun)
×
NEW
217
        case "dnsimple":
×
NEW
218
                p, err = dnsimple.NewDnsimpleProvider(domainFilter, zoneIDFilter, cfg.DryRun)
×
NEW
219
        case "coredns", "skydns":
×
NEW
220
                p, err = coredns.NewCoreDNSProvider(domainFilter, cfg.CoreDNSPrefix, cfg.DryRun)
×
NEW
221
        case "exoscale":
×
NEW
222
                p, err = exoscale.NewExoscaleProvider(
×
NEW
223
                        cfg.ExoscaleAPIEnvironment,
×
NEW
224
                        cfg.ExoscaleAPIZone,
×
NEW
225
                        cfg.ExoscaleAPIKey,
×
NEW
226
                        cfg.ExoscaleAPISecret,
×
NEW
227
                        cfg.DryRun,
×
NEW
228
                        exoscale.ExoscaleWithDomain(domainFilter),
×
NEW
229
                        exoscale.ExoscaleWithLogging(),
×
NEW
230
                )
×
NEW
231
        case "inmemory":
×
NEW
232
                p, err = inmemory.NewInMemoryProvider(inmemory.InMemoryInitZones(cfg.InMemoryZones), inmemory.InMemoryWithDomain(domainFilter), inmemory.InMemoryWithLogging()), nil
×
NEW
233
        case "pdns":
×
NEW
234
                p, err = pdns.NewPDNSProvider(
×
NEW
235
                        ctx,
×
NEW
236
                        pdns.PDNSConfig{
×
NEW
237
                                DomainFilter: domainFilter,
×
NEW
238
                                DryRun:       cfg.DryRun,
×
NEW
239
                                Server:       cfg.PDNSServer,
×
NEW
240
                                ServerID:     cfg.PDNSServerID,
×
NEW
241
                                APIKey:       cfg.PDNSAPIKey,
×
NEW
242
                                TLSConfig: pdns.TLSConfig{
×
NEW
243
                                        SkipTLSVerify:         cfg.PDNSSkipTLSVerify,
×
NEW
244
                                        CAFilePath:            cfg.TLSCA,
×
NEW
245
                                        ClientCertFilePath:    cfg.TLSClientCert,
×
NEW
246
                                        ClientCertKeyFilePath: cfg.TLSClientCertKey,
×
NEW
247
                                },
×
NEW
248
                        },
×
NEW
249
                )
×
NEW
250
        case "oci":
×
NEW
251
                var config *oci.OCIConfig
×
NEW
252
                // if the instance-principals flag was set, and a compartment OCID was provided, then ignore the
×
NEW
253
                // OCI config file, and provide a config that uses instance principal authentication.
×
NEW
254
                if cfg.OCIAuthInstancePrincipal {
×
NEW
255
                        if len(cfg.OCICompartmentOCID) == 0 {
×
NEW
256
                                err = fmt.Errorf("instance principal authentication requested, but no compartment OCID provided")
×
NEW
257
                        } else {
×
NEW
258
                                authConfig := oci.OCIAuthConfig{UseInstancePrincipal: true}
×
NEW
259
                                config = &oci.OCIConfig{Auth: authConfig, CompartmentID: cfg.OCICompartmentOCID}
×
NEW
260
                        }
×
NEW
261
                } else {
×
NEW
262
                        config, err = oci.LoadOCIConfig(cfg.OCIConfigFile)
×
NEW
263
                }
×
NEW
264
                config.ZoneCacheDuration = cfg.OCIZoneCacheDuration
×
NEW
265
                if err == nil {
×
NEW
266
                        p, err = oci.NewOCIProvider(*config, domainFilter, zoneIDFilter, cfg.OCIZoneScope, cfg.DryRun)
×
NEW
267
                }
×
NEW
268
        case "rfc2136":
×
NEW
269
                tlsConfig := rfc2136.TLSConfig{
×
NEW
270
                        UseTLS:                cfg.RFC2136UseTLS,
×
NEW
271
                        SkipTLSVerify:         cfg.RFC2136SkipTLSVerify,
×
NEW
272
                        CAFilePath:            cfg.TLSCA,
×
NEW
273
                        ClientCertFilePath:    cfg.TLSClientCert,
×
NEW
274
                        ClientCertKeyFilePath: cfg.TLSClientCertKey,
×
NEW
275
                }
×
NEW
276
                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)
×
NEW
277
        case "ns1":
×
NEW
278
                p, err = ns1.NewNS1Provider(
×
NEW
279
                        ns1.NS1Config{
×
NEW
280
                                DomainFilter:  domainFilter,
×
NEW
281
                                ZoneIDFilter:  zoneIDFilter,
×
NEW
282
                                NS1Endpoint:   cfg.NS1Endpoint,
×
NEW
283
                                NS1IgnoreSSL:  cfg.NS1IgnoreSSL,
×
NEW
284
                                DryRun:        cfg.DryRun,
×
NEW
285
                                MinTTLSeconds: cfg.NS1MinTTLSeconds,
×
NEW
286
                        },
×
NEW
287
                )
×
NEW
288
        case "transip":
×
NEW
289
                p, err = transip.NewTransIPProvider(cfg.TransIPAccountName, cfg.TransIPPrivateKeyFile, domainFilter, cfg.DryRun)
×
NEW
290
        case "scaleway":
×
NEW
291
                p, err = scaleway.NewScalewayProvider(ctx, domainFilter, cfg.DryRun)
×
NEW
292
        case "godaddy":
×
NEW
293
                p, err = godaddy.NewGoDaddyProvider(ctx, domainFilter, cfg.GoDaddyTTL, cfg.GoDaddyAPIKey, cfg.GoDaddySecretKey, cfg.GoDaddyOTE, cfg.DryRun)
×
NEW
294
        case "gandi":
×
NEW
295
                p, err = gandi.NewGandiProvider(ctx, domainFilter, cfg.DryRun)
×
NEW
296
        case "pihole":
×
NEW
297
                p, err = pihole.NewPiholeProvider(
×
NEW
298
                        pihole.PiholeConfig{
×
NEW
299
                                Server:                cfg.PiholeServer,
×
NEW
300
                                Password:              cfg.PiholePassword,
×
NEW
301
                                TLSInsecureSkipVerify: cfg.PiholeTLSInsecureSkipVerify,
×
NEW
302
                                DomainFilter:          domainFilter,
×
NEW
303
                                DryRun:                cfg.DryRun,
×
NEW
304
                        },
×
NEW
305
                )
×
NEW
306
        case "ibmcloud":
×
NEW
307
                p, err = ibmcloud.NewIBMCloudProvider(cfg.IBMCloudConfigFile, domainFilter, zoneIDFilter, endpointsSource, cfg.IBMCloudProxied, cfg.DryRun)
×
NEW
308
        case "plural":
×
NEW
309
                p, err = plural.NewPluralProvider(cfg.PluralCluster, cfg.PluralProvider)
×
NEW
310
        case "tencentcloud":
×
NEW
311
                p, err = tencentcloud.NewTencentCloudProvider(domainFilter, zoneIDFilter, cfg.TencentCloudConfigFile, cfg.TencentCloudZoneType, cfg.DryRun)
×
NEW
312
        case "webhook":
×
NEW
313
                p, err = webhook.NewWebhookProvider(cfg.WebhookProviderURL)
×
NEW
314
        default:
×
NEW
315
                log.Fatalf("unknown dns provider: %s", cfg.Provider)
×
316
        }
NEW
317
        if err != nil {
×
NEW
318
                log.Fatal(err)
×
NEW
319
        }
×
320

NEW
321
        if cfg.WebhookServer {
×
NEW
322
                webhookapi.StartHTTPApi(p, nil, cfg.WebhookProviderReadTimeout, cfg.WebhookProviderWriteTimeout, "127.0.0.1:8888")
×
NEW
323
                os.Exit(0)
×
NEW
324
        }
×
325

NEW
326
        if cfg.ProviderCacheTime > 0 {
×
NEW
327
                p = provider.NewCachedProvider(
×
NEW
328
                        p,
×
NEW
329
                        cfg.ProviderCacheTime,
×
NEW
330
                )
×
NEW
331
        }
×
332

NEW
333
        reg, err := selectRegistry(cfg, p)
×
NEW
334
        if err != nil {
×
NEW
335
                log.Fatal(err)
×
NEW
336
        }
×
337

NEW
338
        policy, exists := plan.Policies[cfg.Policy]
×
NEW
339
        if !exists {
×
NEW
340
                log.Fatalf("unknown policy: %s", cfg.Policy)
×
NEW
341
        }
×
342

NEW
343
        ctrl := Controller{
×
NEW
344
                Source:               endpointsSource,
×
NEW
345
                Registry:             reg,
×
NEW
346
                Policy:               policy,
×
NEW
347
                Interval:             cfg.Interval,
×
NEW
348
                DomainFilter:         domainFilter,
×
NEW
349
                ManagedRecordTypes:   cfg.ManagedDNSRecordTypes,
×
NEW
350
                ExcludeRecordTypes:   cfg.ExcludeDNSRecordTypes,
×
NEW
351
                MinEventSyncInterval: cfg.MinEventSyncInterval,
×
NEW
352
        }
×
NEW
353

×
NEW
354
        if cfg.Once {
×
NEW
355
                err := ctrl.RunOnce(ctx)
×
NEW
356
                if err != nil {
×
NEW
357
                        log.Fatal(err)
×
NEW
358
                }
×
359

NEW
360
                os.Exit(0)
×
361
        }
362

NEW
363
        if cfg.UpdateEvents {
×
NEW
364
                // Add RunOnce as the handler function that will be called when ingress/service sources have changed.
×
NEW
365
                // Note that k8s Informers will perform an initial list operation, which results in the handler
×
NEW
366
                // function initially being called for every Service/Ingress that exists
×
NEW
367
                ctrl.Source.AddEventHandler(ctx, func() { ctrl.ScheduleRunOnce(time.Now()) })
×
368
        }
369

NEW
370
        ctrl.ScheduleRunOnce(time.Now())
×
NEW
371
        ctrl.Run(ctx)
×
372
}
373

374
// This function configures the logger format and level based on the provided configuration.
375
func configureLogger(cfg *externaldns.Config) {
3✔
376
        if cfg.LogFormat == "json" {
4✔
377
                log.SetFormatter(&log.JSONFormatter{})
1✔
378
        }
1✔
379
        ll, err := log.ParseLevel(cfg.LogLevel)
3✔
380
        if err != nil {
4✔
381
                log.Fatalf("failed to parse log level: %v", err)
1✔
382
        }
1✔
383
        log.SetLevel(ll)
3✔
384
}
385

386
// selectRegistry selects the appropriate registry implementation based on the configuration in cfg.
387
// It initializes and returns a registry along with any error encountered during setup.
388
// Supported registry types include: dynamodb, noop, txt, and aws-sd.
389
func selectRegistry(cfg *externaldns.Config, p provider.Provider) (registry.Registry, error) {
5✔
390
        var r registry.Registry
5✔
391
        var err error
5✔
392
        switch cfg.Registry {
5✔
393
        case "dynamodb":
1✔
394
                var dynamodbOpts []func(*dynamodb.Options)
1✔
395
                if cfg.AWSDynamoDBRegion != "" {
2✔
396
                        dynamodbOpts = []func(*dynamodb.Options){
1✔
397
                                func(opts *dynamodb.Options) {
2✔
398
                                        opts.Region = cfg.AWSDynamoDBRegion
1✔
399
                                },
1✔
400
                        }
401
                }
402
                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)
1✔
403
        case "noop":
1✔
404
                r, err = registry.NewNoopRegistry(p)
1✔
405
        case "txt":
1✔
406
                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)
1✔
407
        case "aws-sd":
1✔
408
                r, err = registry.NewAWSSDRegistry(p, cfg.TXTOwnerID)
1✔
409
        default:
1✔
410
                log.Fatalf("unknown registry: %s", cfg.Registry)
1✔
411
        }
412
        return r, err
5✔
413
}
414

415
// RegexDomainFilter overrides DomainFilter
416
func createDomainFilter(cfg *externaldns.Config) endpoint.DomainFilter {
5✔
417
        if cfg.RegexDomainFilter != nil && cfg.RegexDomainFilter.String() != "" {
7✔
418
                return endpoint.NewRegexDomainFilter(cfg.RegexDomainFilter, cfg.RegexDomainExclusion)
2✔
419
        } else {
5✔
420
                return endpoint.NewDomainFilterWithExclusions(cfg.DomainFilter, cfg.ExcludeDomains)
3✔
421
        }
3✔
422
}
423

424
// handleSigterm listens for a SIGTERM signal and triggers the provided cancel function
425
// to gracefully terminate the application. It logs a message when the signal is received.
426
func handleSigterm(cancel func()) {
1✔
427
        signals := make(chan os.Signal, 1)
1✔
428
        signal.Notify(signals, syscall.SIGTERM)
1✔
429
        <-signals
1✔
430
        log.Info("Received SIGTERM. Terminating...")
1✔
431
        cancel()
1✔
432
}
1✔
433

434
// serveMetrics starts an HTTP server that serves health and metrics endpoints.
435
// The /healthz endpoint returns a 200 OK status to indicate the service is healthy.
436
// The /metrics endpoint serves Prometheus metrics.
437
// The server listens on the specified address and logs debug information about the endpoints.
438
func serveMetrics(address string) {
1✔
439
        http.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) {
2✔
440
                w.WriteHeader(http.StatusOK)
1✔
441
                _, _ = w.Write([]byte("OK"))
1✔
442
        })
1✔
443

444
        log.Debugf("serving 'healthz' on 'localhost:%s/healthz'", address)
1✔
445
        log.Debugf("serving 'metrics' on 'localhost:%s/metrics'", address)
1✔
446
        log.Debugf("registered '%d' metrics", len(metrics.RegisterMetric.Metrics))
1✔
447

1✔
448
        http.Handle("/metrics", promhttp.Handler())
1✔
449

1✔
450
        log.Fatal(http.ListenAndServe(address, nil))
1✔
451
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc